123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150 |
- 'use strict';
- const CursorType = require('../constants/cursor');
- const CommandCodes = require('../constants/commands');
- const Types = require('../constants/types');
- const Packet = require('../packets/packet');
- const CharsetToEncoding = require('../constants/charset_encodings.js');
- function isJSON(value) {
- return (
- Array.isArray(value) ||
- value.constructor === Object ||
- (typeof value.toJSON === 'function' && !Buffer.isBuffer(value))
- );
- }
- /**
- * Converts a value to an object describing type, String/Buffer representation and length
- * @param {*} value
- */
- function toParameter(value, encoding, timezone) {
- let type = Types.VAR_STRING;
- let length;
- let writer = function(value) {
- // eslint-disable-next-line no-invalid-this
- return Packet.prototype.writeLengthCodedString.call(this, value, encoding);
- };
- if (value !== null) {
- switch (typeof value) {
- case 'undefined':
- throw new TypeError('Bind parameters must not contain undefined');
- case 'number':
- type = Types.DOUBLE;
- length = 8;
- writer = Packet.prototype.writeDouble;
- break;
- case 'boolean':
- value = value | 0;
- type = Types.TINY;
- length = 1;
- writer = Packet.prototype.writeInt8;
- break;
- case 'object':
- if (Object.prototype.toString.call(value) === '[object Date]') {
- type = Types.DATETIME;
- length = 12;
- writer = function(value) {
- // eslint-disable-next-line no-invalid-this
- return Packet.prototype.writeDate.call(this, value, timezone);
- };
- } else if (isJSON(value)) {
- value = JSON.stringify(value);
- type = Types.JSON;
- } else if (Buffer.isBuffer(value)) {
- length = Packet.lengthCodedNumberLength(value.length) + value.length;
- writer = Packet.prototype.writeLengthCodedBuffer;
- }
- break;
- default:
- value = value.toString();
- }
- } else {
- value = '';
- type = Types.NULL;
- }
- if (!length) {
- length = Packet.lengthCodedStringLength(value, encoding);
- }
- return { value, type, length, writer };
- }
- class Execute {
- constructor(id, parameters, charsetNumber, timezone) {
- this.id = id;
- this.parameters = parameters;
- this.encoding = CharsetToEncoding[charsetNumber];
- this.timezone = timezone;
- }
- toPacket() {
- // TODO: don't try to calculate packet length in advance, allocate some big buffer in advance (header + 256 bytes?)
- // and copy + reallocate if not enough
- // 0 + 4 - length, seqId
- // 4 + 1 - COM_EXECUTE
- // 5 + 4 - stmtId
- // 9 + 1 - flags
- // 10 + 4 - iteration-count (always 1)
- let length = 14;
- let parameters;
- if (this.parameters && this.parameters.length > 0) {
- length += Math.floor((this.parameters.length + 7) / 8);
- length += 1; // new-params-bound-flag
- length += 2 * this.parameters.length; // type byte for each parameter if new-params-bound-flag is set
- parameters = this.parameters.map(value =>
- toParameter(value, this.encoding, this.timezone)
- );
- length += parameters.reduce(
- (accumulator, parameter) => accumulator + parameter.length,
- 0
- );
- }
- const buffer = Buffer.allocUnsafe(length);
- const packet = new Packet(0, buffer, 0, length);
- packet.offset = 4;
- packet.writeInt8(CommandCodes.STMT_EXECUTE);
- packet.writeInt32(this.id);
- packet.writeInt8(CursorType.NO_CURSOR); // flags
- packet.writeInt32(1); // iteration-count, always 1
- if (parameters) {
- let bitmap = 0;
- let bitValue = 1;
- parameters.forEach(parameter => {
- if (parameter.type === Types.NULL) {
- bitmap += bitValue;
- }
- bitValue *= 2;
- if (bitValue === 256) {
- packet.writeInt8(bitmap);
- bitmap = 0;
- bitValue = 1;
- }
- });
- if (bitValue !== 1) {
- packet.writeInt8(bitmap);
- }
- // TODO: explain meaning of the flag
- // afaik, if set n*2 bytes with type of parameter are sent before parameters
- // if not, previous execution types are used (TODO prooflink)
- packet.writeInt8(1); // new-params-bound-flag
- // Write parameter types
- parameters.forEach(parameter => {
- packet.writeInt8(parameter.type); // field type
- packet.writeInt8(0); // parameter flag
- });
- // Write parameter values
- parameters.forEach(parameter => {
- if (parameter.type !== Types.NULL) {
- parameter.writer.call(packet, parameter.value);
- }
- });
- }
- return packet;
- }
- }
- module.exports = Execute;
|