aboutsummaryrefslogtreecommitdiff
path: root/node_modules/mysql2/lib/packets/execute.js
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/mysql2/lib/packets/execute.js')
-rw-r--r--node_modules/mysql2/lib/packets/execute.js150
1 files changed, 150 insertions, 0 deletions
diff --git a/node_modules/mysql2/lib/packets/execute.js b/node_modules/mysql2/lib/packets/execute.js
new file mode 100644
index 0000000..c612f66
--- /dev/null
+++ b/node_modules/mysql2/lib/packets/execute.js
@@ -0,0 +1,150 @@
+'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;