2014-10-23 10:52:21 -04:00
|
|
|
var util = require('util');
|
2015-01-28 15:33:44 -05:00
|
|
|
|
|
|
|
var Command = require('./command.js');
|
|
|
|
var Packets = require('../packets/index.js');
|
|
|
|
|
|
|
|
var compileParser = require('../compile_binary_parser.js');
|
2014-10-23 10:52:21 -04:00
|
|
|
|
|
|
|
function Execute(options, callback)
|
|
|
|
{
|
|
|
|
Command.call(this);
|
2015-01-28 15:33:44 -05:00
|
|
|
this.statement = options.statement;
|
2014-10-23 10:52:21 -04:00
|
|
|
|
2015-01-28 15:33:44 -05:00
|
|
|
this.onResult = callback;
|
|
|
|
this.parameters = options.values;
|
2014-10-23 10:52:21 -04:00
|
|
|
|
|
|
|
this.resultFields = [];
|
|
|
|
this.resultFieldCount = 0;
|
|
|
|
this.insertId = 0;
|
|
|
|
|
|
|
|
this.rows = [];
|
|
|
|
this.options = options;
|
|
|
|
}
|
|
|
|
util.inherits(Execute, Command);
|
|
|
|
|
2015-01-28 15:33:44 -05:00
|
|
|
Execute.prototype.buildParserFromFields = function(fields, connection) {
|
|
|
|
var parserKey;
|
|
|
|
if (!this.statement.parser) {
|
|
|
|
// compile row parser
|
|
|
|
parserKey = connection.keyFromFields(fields, this.options);
|
|
|
|
this.statement.parser = connection.binaryProtocolParsers[parserKey];
|
|
|
|
if (!this.statement.parser) {
|
|
|
|
this.statement.parser = compileParser(fields, this.options, connection.config);
|
|
|
|
connection.binaryProtocolParsers[parserKey] = this.statement.parser;
|
|
|
|
}
|
2014-10-23 10:52:21 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function statementKey(query, options) {
|
|
|
|
return (typeof options.nestTables) +
|
|
|
|
'/' + options.nestTables + '/' + options.rowsAsHash
|
|
|
|
+ query;
|
|
|
|
}
|
|
|
|
|
2015-01-28 15:33:44 -05:00
|
|
|
Execute.prototype.start = function(packet, connection) {
|
|
|
|
var cachedStatement = connection._statements[statementKey(this.query, this.options)];
|
|
|
|
var executePacket = new Packets.Execute(this.statement.id, this.parameters);
|
2014-10-23 10:52:21 -04:00
|
|
|
connection.writePacket(executePacket.toPacket(1));
|
|
|
|
return Execute.prototype.resultsetHeader;
|
|
|
|
};
|
|
|
|
|
|
|
|
Execute.prototype.resultsetHeader = function(packet) {
|
|
|
|
var self = this;
|
|
|
|
var header = new Packets.ResultSetHeader(packet);
|
|
|
|
this.resultFieldCount = header.fieldCount;
|
2015-01-28 15:33:44 -05:00
|
|
|
// we'll re-create row parser if schema reported by
|
|
|
|
// prepare is different from one in result set
|
|
|
|
if (this.statement.columns.length != this.resultFieldCount)
|
|
|
|
this.statement.parser = null;
|
2014-10-23 10:52:21 -04:00
|
|
|
this.insertId = header.insertId;
|
|
|
|
if (this.resultFieldCount === 0) {
|
|
|
|
if (this.onResult)
|
|
|
|
process.nextTick(function() {
|
|
|
|
self.onResult(null, header, []);
|
|
|
|
});
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return Execute.prototype.readResultField;
|
|
|
|
};
|
|
|
|
|
2015-01-28 15:33:44 -05:00
|
|
|
Execute.prototype.readResultField = function(packet, connection) {
|
2014-10-23 10:52:21 -04:00
|
|
|
var def;
|
2015-01-28 15:33:44 -05:00
|
|
|
|
|
|
|
// TODO: this used to be performance optimisation (don't parse column def if we
|
|
|
|
// already have one in statement info, but since we can't 100% trust statement info
|
|
|
|
// column defs it's probably bettery to always use fields from result header.
|
|
|
|
// config option to ignore this?
|
|
|
|
|
|
|
|
// ignore result fields definition, we are reusing fields from prepare response
|
|
|
|
if (this.statement.parser)
|
2014-10-23 10:52:21 -04:00
|
|
|
this.resultFields.push(null);
|
|
|
|
else {
|
|
|
|
def = new Packets.ColumnDefinition(packet);
|
|
|
|
this.resultFields.push(def);
|
|
|
|
}
|
|
|
|
if (this.resultFields.length == this.resultFieldCount) {
|
|
|
|
return Execute.prototype.resultFieldsEOF;
|
|
|
|
}
|
|
|
|
return Execute.prototype.readResultField;
|
|
|
|
};
|
|
|
|
|
2015-01-28 15:33:44 -05:00
|
|
|
Execute.prototype.resultFieldsEOF = function(packet, connection) {
|
2014-10-23 10:52:21 -04:00
|
|
|
// check EOF
|
|
|
|
if (!packet.isEOF())
|
2015-01-28 15:33:44 -05:00
|
|
|
return connection.protocolError("Expected EOF packet");
|
|
|
|
|
|
|
|
if (!this.statement.parser)
|
|
|
|
this.buildParserFromFields(this.resultFields, connection)
|
2014-10-23 10:52:21 -04:00
|
|
|
return Execute.prototype.row;
|
|
|
|
};
|
|
|
|
|
2015-01-28 15:33:44 -05:00
|
|
|
Execute.prototype.row = function(packet, connection)
|
2014-10-23 10:52:21 -04:00
|
|
|
{
|
|
|
|
var self = this;
|
|
|
|
// TODO: refactor to share code with Query::row
|
|
|
|
if (packet.isEOF()) {
|
2015-01-28 15:33:44 -05:00
|
|
|
|
|
|
|
// TODO: multiple statements
|
|
|
|
//packet.eofStatusFlags() & ServerStatus.SERVER_MORE_RESULTS_EXISTS;
|
|
|
|
|
2014-10-23 10:52:21 -04:00
|
|
|
if (this.onResult)
|
|
|
|
process.nextTick(function() {
|
2015-01-28 15:33:44 -05:00
|
|
|
self.onResult(null, self.rows, self.resultFields);
|
2014-10-23 10:52:21 -04:00
|
|
|
})
|
|
|
|
return null;
|
|
|
|
}
|
2015-01-28 15:33:44 -05:00
|
|
|
|
|
|
|
var r = new this.statement.parser(packet);
|
2014-10-23 10:52:21 -04:00
|
|
|
if (this.onResult)
|
|
|
|
this.rows.push(r);
|
|
|
|
else
|
|
|
|
this.emit('result', r);
|
|
|
|
return Execute.prototype.row;
|
|
|
|
};
|
|
|
|
|
|
|
|
module.exports = Execute;
|