2014-10-23 10:52:21 -04:00
|
|
|
var vm = require('vm');
|
2015-01-28 15:33:44 -05:00
|
|
|
|
|
|
|
var FieldFlags = require('./constants/field_flags.js');
|
|
|
|
var Charsets = require('./constants/charsets.js');
|
|
|
|
var Types = require('./constants/types.js');
|
|
|
|
var srcEscape = require('./helpers').srcEscape;
|
2014-10-23 10:52:21 -04:00
|
|
|
|
|
|
|
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)
|
2015-01-28 15:33:44 -05:00
|
|
|
result.push(' var result = new Array(' + fields.length + ');');
|
2014-10-23 10:52:21 -04:00
|
|
|
|
|
|
|
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) + ']';
|
|
|
|
|
|
|
|
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));
|
|
|
|
}
|
|
|
|
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) {
|
2015-01-28 15:33:44 -05:00
|
|
|
console.log('Compiled binary protocol row parser:');
|
2014-10-23 10:52:21 -04:00
|
|
|
var cardinal = require('cardinal');
|
|
|
|
console.log(cardinal.highlight(src));
|
|
|
|
}
|
|
|
|
return vm.runInThisContext(src);
|
|
|
|
}
|
|
|
|
|
|
|
|
function readCodeFor(field, config) {
|
|
|
|
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:
|
|
|
|
return "packet.readLengthCodedString();";
|
|
|
|
case Types.GEOMETRY:
|
|
|
|
return "packet.parseGeometryValue();";
|
|
|
|
case Types.LONGLONG: // TODO: 8 bytes. Implement as two 4 bytes read for now (it's out of JavaScript int precision!)
|
|
|
|
return unsigned ? "packet.readInt64();" : "packet.readSInt64();";
|
|
|
|
default:
|
|
|
|
if (field.characterSet == Charsets.BINARY)
|
|
|
|
return "packet.readLengthCodedBuffer();";
|
|
|
|
else
|
|
|
|
return "packet.readLengthCodedString();";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = compile;
|