compile_binary_parser.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. var vm = require('vm');
  2. var FieldFlags = require('./constants/field_flags.js');
  3. var Charsets = require('./constants/charsets.js');
  4. var Types = require('./constants/types.js');
  5. var srcEscape = require('./helpers').srcEscape;
  6. var typeNames = [];
  7. for (var t in Types) {
  8. typeNames[Types[t]] = t;
  9. }
  10. function compile(fields, options, config) {
  11. var result = [];
  12. var i=0;
  13. var nullBitmapLength = Math.floor((fields.length + 7 + 2) / 8);
  14. result.push('(function(){ return function BinaryRow(packet) {');
  15. if (options.rowsAsArray)
  16. result.push(' var result = new Array(' + fields.length + ');');
  17. var resultTables = {};
  18. var resultTablesArray = [];
  19. if (options.nestTables === true) {
  20. for (i = 0; i < fields.length; i++) {
  21. resultTables[fields[i].table] = 1;
  22. }
  23. resultTablesArray = Object.keys(resultTables);
  24. for (i = 0; i < resultTablesArray.length; i++) {
  25. result.push(' this[' + srcEscape(resultTablesArray[i]) + '] = {};');
  26. }
  27. }
  28. result.push(' var statusByte = packet.readInt8();');
  29. for (i=0; i < nullBitmapLength; ++i)
  30. result.push(' var nullBitmaskByte' + i + ' = packet.readInt8();');
  31. var lvalue = '';
  32. var currentFieldNullBit = 4;
  33. var nullByteIndex = 0;
  34. var fieldName = '';
  35. var tableName = '';
  36. for (i = 0; i < fields.length; i++) {
  37. fieldName = srcEscape(fields[i].name);
  38. result.push(' // ' + fieldName + ': '+ typeNames[fields[i].columnType]);
  39. if (typeof options.nestTables == 'string') {
  40. tableName = srcEscape(fields[i].table);
  41. lvalue = [' this[', srcEscape(fields[i].table + options.nestTables + fields[i].name), ']'].join('');
  42. } else if (options.nestTables === true) {
  43. tableName = srcEscape(fields[i].table);
  44. lvalue = [' this[', tableName, '][', fieldName, ']'].join('');
  45. } else if (options.rowsAsArray) {
  46. lvalue = ' result[' + i.toString(10) + ']';
  47. } else
  48. lvalue = ' this[' + srcEscape(fields[i].name) + ']';
  49. // TODO: this used to be an optimisation ( if column marked as NOT_NULL don't include code to check null
  50. // bitmap at all, but it seems that we can't rely on this flag, see #178
  51. // TODO: benchmark performance difference
  52. //
  53. //if (fields[i].flags & FieldFlags.NOT_NULL) { // don't need to check null bitmap if field can't be null.
  54. // result.push(lvalue + ' = ' + readCodeFor(fields[i], config));
  55. //} else if (fields[i].columnType == Types.NULL) {
  56. // result.push(lvalue + ' = null;');
  57. //} else {
  58. result.push(' if (nullBitmaskByte' + nullByteIndex + ' & ' + currentFieldNullBit + ')');
  59. result.push(' ' + lvalue + ' = null;');
  60. result.push(' else');
  61. result.push(' ' + lvalue + ' = ' + readCodeFor(fields[i], config));
  62. //}
  63. currentFieldNullBit *= 2;
  64. if (currentFieldNullBit == 0x100)
  65. {
  66. currentFieldNullBit = 1;
  67. nullByteIndex++;
  68. }
  69. }
  70. if (options.rowsAsArray)
  71. result.push(' return result;');
  72. result.push('}; })()');
  73. var src = result.join('\n');
  74. if (config.debug) {
  75. console.log('Compiled binary protocol row parser:');
  76. var cardinal = require('cardinal');
  77. console.log(cardinal.highlight(src));
  78. }
  79. return vm.runInThisContext(src);
  80. }
  81. function readCodeFor(field, config) {
  82. var unsigned = field.flags & FieldFlags.UNSIGNED;
  83. switch(field.columnType) {
  84. case Types.TINY:
  85. return unsigned ? "packet.readInt8();" : "packet.readSInt8();";
  86. case Types.SHORT:
  87. return unsigned ? "packet.readInt16();" : "packet.readSInt16();";
  88. case Types.LONG:
  89. case Types.INT24: // in binary protocol int24 is encoded in 4 bytes int32
  90. return unsigned ? "packet.readInt32();" : "packet.readSInt32();";
  91. case Types.YEAR:
  92. return "packet.readInt16()";
  93. case Types.FLOAT:
  94. return "packet.readFloat();";
  95. case Types.DOUBLE:
  96. return "packet.readDouble();";
  97. case Types.NULL:
  98. return "null;";
  99. case Types.DATE:
  100. case Types.DATETIME:
  101. case Types.TIMESTAMP:
  102. case Types.NEWDATE:
  103. if (config.dateStrings)
  104. return "packet.readDateTimeString();";
  105. return "packet.readDateTime();";
  106. case Types.TIME:
  107. return "packet.readTimeString()";
  108. case Types.DECIMAL:
  109. case Types.NEWDECIMAL:
  110. return "packet.readLengthCodedString();";
  111. case Types.GEOMETRY:
  112. return "packet.parseGeometryValue();";
  113. case Types.LONGLONG: // TODO: 8 bytes. Implement as two 4 bytes read for now (it's out of JavaScript int precision!)
  114. return unsigned ? "packet.readInt64();" : "packet.readSInt64();";
  115. default:
  116. if (field.characterSet == Charsets.BINARY)
  117. return "packet.readLengthCodedBuffer();";
  118. else
  119. return "packet.readLengthCodedString();";
  120. }
  121. }
  122. module.exports = compile;