Skip to content

Commit 5569e02

Browse files
committed
Add reverse type lookup for small performance gain
closes #2170
1 parent d916479 commit 5569e02

File tree

6 files changed

+181
-49
lines changed

6 files changed

+181
-49
lines changed

Changes.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ you spot any mistakes.
66

77
## HEAD
88

9+
* Add reverse type lookup for small performance gain #2170
910
* Fix `connection.threadId` missing on handshake failure
1011
* Fix duplicate packet name in debug output
1112
* Fix no password support for old password protocol

lib/protocol/constants/types.js

Lines changed: 72 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,72 @@
1-
// Manually extracted from mysql-5.7.9/include/mysql.h.pp
2-
// some more info here: http://dev.mysql.com/doc/refman/5.5/en/c-api-prepared-statement-type-codes.html
3-
exports.DECIMAL = 0x00; // aka DECIMAL (http://dev.mysql.com/doc/refman/5.0/en/precision-math-decimal-changes.html)
4-
exports.TINY = 0x01; // aka TINYINT, 1 byte
5-
exports.SHORT = 0x02; // aka SMALLINT, 2 bytes
6-
exports.LONG = 0x03; // aka INT, 4 bytes
7-
exports.FLOAT = 0x04; // aka FLOAT, 4-8 bytes
8-
exports.DOUBLE = 0x05; // aka DOUBLE, 8 bytes
9-
exports.NULL = 0x06; // NULL (used for prepared statements, I think)
10-
exports.TIMESTAMP = 0x07; // aka TIMESTAMP
11-
exports.LONGLONG = 0x08; // aka BIGINT, 8 bytes
12-
exports.INT24 = 0x09; // aka MEDIUMINT, 3 bytes
13-
exports.DATE = 0x0a; // aka DATE
14-
exports.TIME = 0x0b; // aka TIME
15-
exports.DATETIME = 0x0c; // aka DATETIME
16-
exports.YEAR = 0x0d; // aka YEAR, 1 byte (don't ask)
17-
exports.NEWDATE = 0x0e; // aka ?
18-
exports.VARCHAR = 0x0f; // aka VARCHAR (?)
19-
exports.BIT = 0x10; // aka BIT, 1-8 byte
20-
exports.TIMESTAMP2 = 0x11; // aka TIMESTAMP with fractional seconds
21-
exports.DATETIME2 = 0x12; // aka DATETIME with fractional seconds
22-
exports.TIME2 = 0x13; // aka TIME with fractional seconds
23-
exports.JSON = 0xf5; // aka JSON
24-
exports.NEWDECIMAL = 0xf6; // aka DECIMAL
25-
exports.ENUM = 0xf7; // aka ENUM
26-
exports.SET = 0xf8; // aka SET
27-
exports.TINY_BLOB = 0xf9; // aka TINYBLOB, TINYTEXT
28-
exports.MEDIUM_BLOB = 0xfa; // aka MEDIUMBLOB, MEDIUMTEXT
29-
exports.LONG_BLOB = 0xfb; // aka LONGBLOG, LONGTEXT
30-
exports.BLOB = 0xfc; // aka BLOB, TEXT
31-
exports.VAR_STRING = 0xfd; // aka VARCHAR, VARBINARY
32-
exports.STRING = 0xfe; // aka CHAR, BINARY
33-
exports.GEOMETRY = 0xff; // aka GEOMETRY
1+
/**
2+
* MySQL type constants
3+
*
4+
* Extracted from version 5.7.19
5+
*
6+
* !! Generated by generate-type-constants.js, do not modify by hand !!
7+
*/
8+
9+
exports.DECIMAL = 0;
10+
exports.TINY = 1;
11+
exports.SHORT = 2;
12+
exports.LONG = 3;
13+
exports.FLOAT = 4;
14+
exports.DOUBLE = 5;
15+
exports.NULL = 6;
16+
exports.TIMESTAMP = 7;
17+
exports.LONGLONG = 8;
18+
exports.INT24 = 9;
19+
exports.DATE = 10;
20+
exports.TIME = 11;
21+
exports.DATETIME = 12;
22+
exports.YEAR = 13;
23+
exports.NEWDATE = 14;
24+
exports.VARCHAR = 15;
25+
exports.BIT = 16;
26+
exports.TIMESTAMP2 = 17;
27+
exports.DATETIME2 = 18;
28+
exports.TIME2 = 19;
29+
exports.JSON = 245;
30+
exports.NEWDECIMAL = 246;
31+
exports.ENUM = 247;
32+
exports.SET = 248;
33+
exports.TINY_BLOB = 249;
34+
exports.MEDIUM_BLOB = 250;
35+
exports.LONG_BLOB = 251;
36+
exports.BLOB = 252;
37+
exports.VAR_STRING = 253;
38+
exports.STRING = 254;
39+
exports.GEOMETRY = 255;
40+
41+
// Lookup-by-number table
42+
exports[0] = 'DECIMAL';
43+
exports[1] = 'TINY';
44+
exports[2] = 'SHORT';
45+
exports[3] = 'LONG';
46+
exports[4] = 'FLOAT';
47+
exports[5] = 'DOUBLE';
48+
exports[6] = 'NULL';
49+
exports[7] = 'TIMESTAMP';
50+
exports[8] = 'LONGLONG';
51+
exports[9] = 'INT24';
52+
exports[10] = 'DATE';
53+
exports[11] = 'TIME';
54+
exports[12] = 'DATETIME';
55+
exports[13] = 'YEAR';
56+
exports[14] = 'NEWDATE';
57+
exports[15] = 'VARCHAR';
58+
exports[16] = 'BIT';
59+
exports[17] = 'TIMESTAMP2';
60+
exports[18] = 'DATETIME2';
61+
exports[19] = 'TIME2';
62+
exports[245] = 'JSON';
63+
exports[246] = 'NEWDECIMAL';
64+
exports[247] = 'ENUM';
65+
exports[248] = 'SET';
66+
exports[249] = 'TINY_BLOB';
67+
exports[250] = 'MEDIUM_BLOB';
68+
exports[251] = 'LONG_BLOB';
69+
exports[252] = 'BLOB';
70+
exports[253] = 'VAR_STRING';
71+
exports[254] = 'STRING';
72+
exports[255] = 'GEOMETRY';

lib/protocol/packets/Field.js

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ function Field(options) {
99
this.db = options.packet.db;
1010
this.table = options.packet.table;
1111
this.name = options.packet.name;
12-
this.type = typeToString(options.packet.type);
12+
this.type = Types[options.packet.type];
1313
this.length = options.packet.length;
1414
}
1515

@@ -24,11 +24,3 @@ Field.prototype.buffer = function () {
2424
Field.prototype.geometry = function () {
2525
return this.parser.parseGeometryValue();
2626
};
27-
28-
function typeToString(t) {
29-
for (var k in Types) {
30-
if (Types[k] === t) return k;
31-
}
32-
33-
return undefined;
34-
}

lib/protocol/packets/RowDataPacket.js

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -123,10 +123,7 @@ function typeCast(field, parser, timeZone, supportBigNumbers, bigNumberStrings,
123123

124124
function typeMatch(type, list) {
125125
if (Array.isArray(list)) {
126-
for (var i = 0; i < list.length; i++) {
127-
if (Types[list[i]] === type) return true;
128-
}
129-
return false;
126+
return list.indexOf(Types[type]) !== -1;
130127
} else {
131128
return Boolean(list);
132129
}

test/unit/test-Mysql.js

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,24 @@ test('Mysql.Types', {
3737
assert.equal(Mysql.Types, common.Types);
3838
},
3939

40-
'string names to integer values': function() {
40+
'contains string to integer values': function() {
4141
var types = Object.keys(Mysql.Types);
4242
assert.ok(types.length > 0);
4343
types.forEach(function (type) {
44-
assert.ok(/^[A-Z_]+/.test(type));
45-
assert.equal(typeof Mysql.Types[type], 'number');
44+
if (!/^[0-9]+$/.test(type)) {
45+
assert.ok(/^[A-Z_]+/.test(type));
46+
assert.equal(typeof Mysql.Types[type], 'number');
47+
}
48+
});
49+
},
50+
51+
'contains integer values to string names': function() {
52+
var types = Object.keys(Mysql.Types);
53+
assert.ok(types.length > 0);
54+
types.forEach(function (type) {
55+
if (/^[0-9]+$/.test(type)) {
56+
assert.equal(typeof Mysql.Types[type], 'string');
57+
}
4658
});
4759
}
4860
});

tool/generate-type-constants.js

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
#!/usr/bin/env node
2+
var fs = require('fs');
3+
var path = require('path');
4+
var script = path.basename(__filename);
5+
6+
var srcDir = process.argv[2];
7+
if (!srcDir) {
8+
var args = [];
9+
args[0] = process.argv[0].indexOf(' ') !== -1
10+
? '"' + process.argv[0] + '"'
11+
: process.argv[0];
12+
args[1] = process.argv[1].indexOf(' ') !== -1
13+
? '"' + process.argv[1] + '"'
14+
: process.argv[1];
15+
args[2] = path.join('path', 'to', 'mysql', 'src');
16+
console.error('Usage: ' + args.join(' '));
17+
process.exit(1);
18+
}
19+
20+
var types = extractTypes(srcDir);
21+
var targetFile = path.join(__dirname, '..', 'lib', 'protocol', 'constants', 'types.js');
22+
var stream = fs.createWriteStream(targetFile);
23+
var version = extractMySqlVersion(srcDir);
24+
25+
stream.write('/**\n * MySQL type constants\n *\n * Extracted from version ' + version + '\n *\n * !! Generated by ' + script + ', do not modify by hand !!\n */\n\n');
26+
27+
var alignment = types.reduce(maxLength, 0);
28+
for (var i = 0; i < types.length; i++) {
29+
if (i in types) {
30+
stream.write('exports.' + types[i] + (new Array(alignment - types[i].length + 1)).join(' ') + ' = ' + i + ';\n');
31+
}
32+
}
33+
34+
stream.write('\n// Lookup-by-number table\n');
35+
36+
var alignment = String(types.length).length;
37+
for (var i = 0; i < types.length; i++) {
38+
if (i in types) {
39+
stream.write('exports[' + i + ']' + (new Array(alignment - String(i).length + 1)).join(' ') + ' = \'' + types[i] + '\';\n');
40+
}
41+
}
42+
43+
console.log('Wrote constants to ' + targetFile);
44+
45+
function extractMySqlVersion(srcDir) {
46+
var versionFile = path.join(srcDir, 'VERSION');
47+
var contents = fs.readFileSync(versionFile, 'utf-8');
48+
var dictionary = Object.create(null);
49+
50+
contents.split('\n').forEach(function (line) {
51+
var pair = line.split('=');
52+
var key = pair[0];
53+
var val = pair.slice(1).join('=').trimRight();
54+
dictionary[key] = val;
55+
});
56+
57+
return dictionary.MYSQL_VERSION_MAJOR + '.' +
58+
dictionary.MYSQL_VERSION_MINOR + '.' +
59+
dictionary.MYSQL_VERSION_PATCH;
60+
}
61+
62+
function extractTypes(srcDir) {
63+
var headerFile = path.join(srcDir, 'include', 'mysql.h.pp');
64+
var contents = fs.readFileSync(headerFile, 'utf-8');
65+
var enumRegexp = /typedef enum enum_field_types {([^}]*)}/m;
66+
var match = enumRegexp.exec(contents);
67+
var regexp = /([A-Z0-9_]+)(?: *= *([0-9]+))?/g;
68+
69+
if (!match) {
70+
throw new Error('Cannot locate enum enum_field_types in "' + headerFile + '"');
71+
}
72+
73+
var enumContents = match[1];
74+
var index = 0;
75+
var types = [];
76+
77+
while ((match = regexp.exec(enumContents))) {
78+
var name = match[1];
79+
var num = Number(match[2]) || index++;
80+
81+
if (name.indexOf('MYSQL_TYPE_') === 0) {
82+
types[num] = name.substring(11);
83+
}
84+
}
85+
86+
return types;
87+
}
88+
89+
function maxLength(max, value) {
90+
return Math.max(max, value.length);
91+
}

0 commit comments

Comments
 (0)