mysql2
Version:
fast mysql driver. Implements core protocol, prepared statements, ssl and compression in native JS
159 lines (143 loc) • 5.4 kB
JavaScript
var vm = require('vm');
var FieldFlags = require('./constants/field_flags.js');
var Charsets = require('./constants/charsets.js');
var CharsetToEncoding = require('./constants/charset_encodings.js');
var Types = require('./constants/types.js');
var srcEscape = require('./helpers').srcEscape;
var typeNames = [];
for (var t in Types) {
typeNames[Types[t]] = t;
}
function compile (fields, options, config) {
var result = [];
var i = 0;
var nullBitmapLength = Math.floor((fields.length + 7 + 2) / 8);
result.push('(function(){ return function BinaryRow(packet) {');
if (options.rowsAsArray) {
result.push(' var result = new Array(' + fields.length + ');');
}
var resultTables = {};
var resultTablesArray = [];
if (options.nestTables === true) {
for (i = 0; i < fields.length; i++) {
resultTables[fields[i].table] = 1;
}
resultTablesArray = Object.keys(resultTables);
for (i = 0; i < resultTablesArray.length; i++) {
result.push(' this[' + srcEscape(resultTablesArray[i]) + '] = {};');
}
}
result.push(' var statusByte = packet.readInt8();');
for (i = 0; i < nullBitmapLength; ++i) {
result.push(' var nullBitmaskByte' + i + ' = packet.readInt8();');
}
var lvalue = '';
var currentFieldNullBit = 4;
var nullByteIndex = 0;
var fieldName = '';
var tableName = '';
for (i = 0; i < fields.length; i++) {
fieldName = srcEscape(fields[i].name);
result.push(' // ' + fieldName + ': ' + typeNames[fields[i].columnType]);
if (typeof options.nestTables == 'string') {
tableName = srcEscape(fields[i].table);
lvalue = [' this[', srcEscape(fields[i].table + options.nestTables + fields[i].name), ']'].join('');
} else if (options.nestTables === true) {
tableName = srcEscape(fields[i].table);
lvalue = [' this[', tableName, '][', fieldName, ']'].join('');
} else if (options.rowsAsArray) {
lvalue = ' result[' + i.toString(10) + ']';
} else {
lvalue = ' this[' + srcEscape(fields[i].name) + ']';
}
// TODO: this used to be an optimisation ( if column marked as NOT_NULL don't include code to check null
// bitmap at all, but it seems that we can't rely on this flag, see #178
// TODO: benchmark performance difference
//
// if (fields[i].flags & FieldFlags.NOT_NULL) { // don't need to check null bitmap if field can't be null.
// result.push(lvalue + ' = ' + readCodeFor(fields[i], config));
// } else if (fields[i].columnType == Types.NULL) {
// result.push(lvalue + ' = null;');
// } else {
result.push(' if (nullBitmaskByte' + nullByteIndex + ' & ' + currentFieldNullBit + ')');
result.push(' ' + lvalue + ' = null;');
result.push(' else');
result.push(' ' + lvalue + ' = ' + readCodeFor(fields[i], config, options));
// }
currentFieldNullBit *= 2;
if (currentFieldNullBit == 0x100) {
currentFieldNullBit = 1;
nullByteIndex++;
}
}
if (options.rowsAsArray) {
result.push(' return result;');
}
result.push('}; })()');
var src = result.join('\n');
if (config.debug) {
console.log('Compiled binary protocol row parser:');
var cardinal = require('cardinal');
console.log(cardinal.highlight(src));
}
return vm.runInThisContext(src);
}
function readCodeFor (field, config, options) {
var supportBigNumbers = options.supportBigNumbers || config.supportBigNumbers;
var bigNumberStrings = options.bigNumberStrings || config.bigNumberStrings;
var unsigned = field.flags & FieldFlags.UNSIGNED;
switch (field.columnType) {
case Types.TINY:
return unsigned ? 'packet.readInt8();' : 'packet.readSInt8();';
case Types.SHORT:
return unsigned ? 'packet.readInt16();' : 'packet.readSInt16();';
case Types.LONG:
case Types.INT24: // in binary protocol int24 is encoded in 4 bytes int32
return unsigned ? 'packet.readInt32();' : 'packet.readSInt32();';
case Types.YEAR:
return 'packet.readInt16()';
case Types.FLOAT:
return 'packet.readFloat();';
case Types.DOUBLE:
return 'packet.readDouble();';
case Types.NULL:
return 'null;';
case Types.DATE:
case Types.DATETIME:
case Types.TIMESTAMP:
case Types.NEWDATE:
if (config.dateStrings) {
return 'packet.readDateTimeString();';
}
return 'packet.readDateTime();';
case Types.TIME:
return 'packet.readTimeString()';
case Types.DECIMAL:
case Types.NEWDECIMAL:
if (config.decimalNumbers) {
return 'packet.parseLengthCodedFloat();';
}
return 'packet.readLengthCodedString("ascii");';
case Types.GEOMETRY:
return 'packet.parseGeometryValue();';
case Types.JSON:
return 'JSON.parse(packet.readLengthCodedString("' + CharsetToEncoding[field.characterSet] + '"));';
case Types.LONGLONG:
if (!supportBigNumbers) {
return unsigned ? 'packet.readInt64JSNumber();' : 'packet.readSInt64JSNumber();';
} else {
if (bigNumberStrings) {
return unsigned ? 'packet.readInt64String();' : 'packet.readSInt64String();';
} else {
return unsigned ? 'packet.readInt64();' : 'packet.readSInt64();';
}
}
default:
if (field.characterSet == Charsets.BINARY) {
return 'packet.readLengthCodedBuffer();';
} else {
return 'packet.readLengthCodedString("' + CharsetToEncoding[field.characterSet] + '")';
}
}
}
module.exports = compile;