diff --git a/Makefile b/Makefile index a3cf21c21..c9f15e0cc 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,5 @@ +SHELL := /bin/bash + test-simple: @find test/simple/test-*.js | xargs -n 1 -t node test-system: diff --git a/Readme.md b/Readme.md index 6e2b4bd02..a993a1d33 100644 --- a/Readme.md +++ b/Readme.md @@ -2,11 +2,17 @@ ## Purpose -A node.js module implementing the -[MySQL protocol](http://forge.mysql.com/wiki/MySQL_Internals_ClientServer_Protocol). +A pure node.js JavaScript Client implementing the [MySQL protocol](http://forge.mysql.com/wiki/MySQL_Internals_ClientServer_Protocol). + +## Current status + +This module was developed for [Transloadit](http://transloadit.com/), a service focused on uploading +and encoding images and videos. It is currently used in production there, but since the service is not +very heavy on database interaction your milage may vary. ## Contributors +* Felix Geisendörfer ([felixge](http://github.com/felixge/node-mysql/commits/master?author=felixge)) - Author and maintainer * Bert Belder ([piscisaureus](http://github.com/felixge/node-mysql/commits/master?author=piscisaureus)) * Alan Gutierrez ([bigeasy](http://github.com/felixge/node-mysql/commits/master?author=bigeasy)) * Brian ([mscdex](http://github.com/felixge/node-mysql/commits/master?author=mscdex)) @@ -15,18 +21,16 @@ A node.js module implementing the ## Sponsors -* [Joyent](http://www.joyent.com/) -* [pinkbike.com](http://pinkbike.com/) +* [Joyent](http://www.joyent.com/) - Main sponsor, you should check out their [node.js hosting](https://no.de/). +* [pinkbike.com](http://pinkbike.com/) - The most awesome biking site there is -I'm working on this driver because I need it for my own startup -([transloadit.com][transloadit]), but it's a big project (~100-200 hours) with -obvious benefits to other companies who are using MySql. +This is a rather large project requiring a significant amount of my limited resources. -So if your company could benefit from a well-engineered node.js mysql driver, -I would greatly appriciate any sponsorship you may be able to provide. All -sponsors will get lifetime display in this readme, priority support on problems, -and votes on roadmap decisions. If you are interested, contact me at -[felix@debuggable.com](mailto:felix@debuggable.com) for details. +If your company could benefit from a well-engineered non-blocking mysql driver, and +wants to support this project, I would greatly appriciate any sponsorship you may be +able to provide. All sponsors will get lifetime display in this readme, priority +support on problems, and votes on roadmap decisions. If you are interested, contact +me at [felix@debuggable.com](mailto:felix@debuggable.com) for details. Of course I'm also happy about code contributions. If you're interested in working on features, just get in touch so we can talk about API design and @@ -60,22 +64,24 @@ However, using a current version of node is encouraged. ## Tutorial var Client = require('mysql').Client, - client = new Client(); + client = new Client(), + TEST_DATABASE = 'nodejs_mysl_test', + TEST_TABLE = 'test'; client.user = 'root'; client.password = 'root'; client.connect(); - client.query('CREATE DATABASE '+TEST_CONFIG.database, function() { - if (err && err.errorNumber != Client.ERROR_DB_CREATE_EXISTS) { + client.query('CREATE DATABASE '+TEST_DATABASE, function(err) { + if (err && err.number != Client.ERROR_DB_CREATE_EXISTS) { throw err; } }); // If no callback is provided, any errors will be emitted as `'error'` // events by the client - client.query('USE '+TEST_CONFIG.database); + client.query('USE '+TEST_DATABASE); client.query( 'CREATE TEMPORARY TABLE '+TEST_TABLE+ @@ -83,13 +89,13 @@ However, using a current version of node is encouraged. 'title VARCHAR(255), '+ 'text TEXT, '+ 'created DATETIME, '+ - 'PRIMARY KEY (id));', + 'PRIMARY KEY (id))' ); client.query( 'INSERT INTO '+TEST_TABLE+' '+ 'SET title = ?, text = ?, created = ?', - ['super cool', 'this is a nice text', '2010-08-16 10:00:23'], + ['super cool', 'this is a nice text', '2010-08-16 10:00:23'] ); var query = client.query( @@ -100,7 +106,7 @@ However, using a current version of node is encouraged. client.query( 'SELECT * FROM '+TEST_TABLE, - gently.expect(function selectCb(err, results, fields) { + function selectCb(err, results, fields) { if (err) { throw err; } @@ -108,7 +114,7 @@ However, using a current version of node is encouraged. console.log(results); console.log(fields); client.end(); - }) + } ); ## API @@ -159,6 +165,9 @@ query. This method returns a `Query` object which can be used to stream incoming row data. +**Warning:** `sql` statements with multiple queries separated by semicolons +are not supported yet. + ### client.ping([cb]) Sends a ping command to the server. @@ -219,6 +228,20 @@ itself will be made available soon. Emitted once the query is finished. In case there is no result set, a `result` parameter is provided which contains the information from the mysql OK packet. +## FAQ + +### How do I compile this module? + +This module is written entirely in JavaScript. There is no dependency on external +C libraries such as libmysql. That means you don't have to compile this module +at all. + +### How can I retrieve the id from the last inserted record? + + client.query('INSERT INTO my_table SET title = ?', function(err, info) { + console.log(info.insertId); + }); + ## Todo At this point the module is ready to be tried out, but a lot of things are yet to be done: @@ -231,6 +254,7 @@ At this point the module is ready to be tried out, but a lot of things are yet t * Performance profiling * Handle re-connect after bad credential error (should query queue be kept?) * Deal with stale connections / other potential network issues +* Decide how to handle queries with multiple statements ## License diff --git a/benchmark/node-mysql/insert-select.js b/benchmark/node-mysql/insert.js similarity index 70% rename from benchmark/node-mysql/insert-select.js rename to benchmark/node-mysql/insert.js index 9e5d2bd25..d6ef00a17 100644 --- a/benchmark/node-mysql/insert-select.js +++ b/benchmark/node-mysql/insert.js @@ -10,8 +10,9 @@ client.query('CREATE DATABASE '+TEST_DB, function(err) { } }); client.query('USE '+TEST_DB); +client.query('DROP TABLE IF EXISTS '+TEST_TABLE); client.query( - 'CREATE TEMPORARY TABLE '+TEST_TABLE+' ('+ + 'CREATE TABLE '+TEST_TABLE+' ('+ 'id INT(11) AUTO_INCREMENT, '+ 'title VARCHAR(255), '+ 'text TEXT, '+ @@ -31,18 +32,8 @@ client.query( insertsPerSecond = inserts / duration; console.log('%d inserts / second', insertsPerSecond.toFixed(2)); - - client.typeCast = false; - var selectStart = +new Date; - client - .query('SELECT * FROM '+TEST_TABLE) - .on('row', function(row) {}) - .on('end', function() { - var duration = (+new Date - selectStart) / 1000, - rowsPerSecond = inserts / duration; - console.log('%d rows / second', rowsPerSecond.toFixed(2)); - client.end(); - }); + console.log('%d ms', +new Date - start); + client.end(); } }); } diff --git a/benchmark/node-mysql/select.js b/benchmark/node-mysql/select.js new file mode 100644 index 000000000..696d2b3cf --- /dev/null +++ b/benchmark/node-mysql/select.js @@ -0,0 +1,40 @@ +// Last v8 profile when running this test for 500k rows: +// https://gist.github.com/f85c38010c038e5efe2e +require('../../test/common'); +var Client = require('mysql/client'), + client = Client(TEST_CONFIG), + rows = 0; + +client.typeCast = false; + +client.connect(); +client.query('CREATE DATABASE '+TEST_DB, function(err) { + if (err && err.number != Client.ERROR_DB_CREATE_EXISTS) { + throw err; + } +}); + +client.query('USE '+TEST_DB); +var selectStart = +new Date; + +function query() { + client + .query('SELECT * FROM '+TEST_TABLE) + .on('row', function(row) { + rows++; + }) + .on('end', function() { + if (rows < 10000) { + query(); + return; + } + + var duration = (+new Date - selectStart) / 1000, + rowsPerSecond = rows / duration; + console.log('%d rows / second', rowsPerSecond.toFixed(2)); + console.log('%d ms', +new Date - selectStart); + client.end(); + }); +} + +query(); diff --git a/benchmark/php/insert-select.php b/benchmark/php/insert-select.php index d63018662..c3c3a512d 100644 --- a/benchmark/php/insert-select.php +++ b/benchmark/php/insert-select.php @@ -27,6 +27,7 @@ $duration = (microtime(true) - $start); $insertsPerSecond = $INSERTS / $duration; echo sprintf("%d inserts / second\n", $insertsPerSecond); +echo sprintf("%d ms\n", $duration * 1000); $start = microtime(true); $q = mysql_query('SELECT * FROM '.$table); @@ -35,4 +36,5 @@ $duration = (microtime(true) - $start); $rowsPerSecond = $INSERTS / $duration; echo sprintf("%d rows / second\n", $rowsPerSecond); +echo sprintf("%d ms\n", $duration * 1000); ?> diff --git a/lib/mysql/client.js b/lib/mysql/client.js index 4d7580e48..a3e405fec 100644 --- a/lib/mysql/client.js +++ b/lib/mysql/client.js @@ -7,7 +7,7 @@ var sys = require('./sys'), OutgoingPacket = require('./outgoing_packet'), Query = require('./query'), EventEmitter = require('events').EventEmitter, - netBinding = process.binding('net'); + netConstants = require('./net_constants'); function Client(properties) { if (!(this instanceof Client)) { @@ -50,7 +50,7 @@ Client.prototype.connect = function(cb) { connection .on('error', function(err) { - if (err.errno & (netBinding.ECONNREFUSED | netBinding.ENOTFOUND)) { + if (err.errno & (netConstants.ECONNREFUSED | netConstants.ENOTFOUND)) { if (cb) { cb(err); return; @@ -94,7 +94,10 @@ Client.prototype.query = function(sql, params, cb) { cb = arguments[1]; } - var query = new Query({typeCast: this.typeCast}); + var query = new Query({ + typeCast: this.typeCast, + sql: sql + }); if (cb) { var rows = [], fields = {}; @@ -351,11 +354,6 @@ Client._packetToUserObject = function(packet) { } return userObject; - - packet.message = packet.errorMessage; - delete packet.errorMessage; - packet.number = packet.errorNumber; - delete packet.errorNumber; }; Client.prototype._debugPacket = function(packet) { diff --git a/lib/mysql/net_constants.js b/lib/mysql/net_constants.js new file mode 100644 index 000000000..1355709ca --- /dev/null +++ b/lib/mysql/net_constants.js @@ -0,0 +1,22 @@ +// Constants have been changing through various node versions. +// First we try to look for them in the old location (in the net binding) +// If this fails, we try to get them from the constants module which +// was added in a later version. If that fails we raise an error; + +var NET_CONSTANTS_NOT_FOUND = 'Unable to detect constants location, please report this.'; + +module.exports = process.binding('net'); + +if ('ECONNREFUSED' in module.exports) { + return; +} + +try { + module.exports = require('constants'); +} catch (e) { + throw new Error(NET_CONSTANTS_NOT_FOUND); +} + +if (!('ECONNREFUSED' in module.exports)) { + throw new Error(NET_CONSTANTS_NOT_FOUND); +} diff --git a/lib/mysql/parser.js b/lib/mysql/parser.js index b737209d9..32e5ed34e 100644 --- a/lib/mysql/parser.js +++ b/lib/mysql/parser.js @@ -85,7 +85,7 @@ Parser.prototype.write = function(buffer) { switch (state) { // PACKET HEADER - case Parser.PACKET_LENGTH: + case 0: // PACKET_LENGTH: if (!packet) { packet = this.packet = new EventEmitter(); packet.index = 0; @@ -101,7 +101,7 @@ Parser.prototype.write = function(buffer) { advance(); } break; - case Parser.PACKET_NUMBER: + case 1: // PACKET_NUMBER: // 1 byte packet.number = c; @@ -120,7 +120,7 @@ Parser.prototype.write = function(buffer) { break; // GREETING_PACKET - case Parser.GREETING_PROTOCOL_VERSION: + case 2: // GREETING_PROTOCOL_VERSION: // Nice undocumented MySql gem, the initial greeting can be an error // packet. Happens for too many connections errors. if (c === 0xff) { @@ -134,7 +134,7 @@ Parser.prototype.write = function(buffer) { packet.protocolVersion = c; advance(); break; - case Parser.GREETING_SERVER_VERSION: + case 3: // GREETING_SERVER_VERSION: if (packet.index == 0) { packet.serverVersion = ''; } @@ -146,7 +146,7 @@ Parser.prototype.write = function(buffer) { advance(); } break; - case Parser.GREETING_THREAD_ID: + case 4: // GREETING_THREAD_ID: if (packet.index == 0) { packet.threadId = 0; } @@ -158,7 +158,7 @@ Parser.prototype.write = function(buffer) { advance(); } break; - case Parser.GREETING_SCRAMBLE_BUFF_1: + case 5: // GREETING_SCRAMBLE_BUFF_1: if (packet.index == 0) { packet.scrambleBuffer = new Buffer(8 + 12); } @@ -170,11 +170,11 @@ Parser.prototype.write = function(buffer) { advance(); } break; - case Parser.GREETING_FILLER_1: + case 6: // GREETING_FILLER_1: // 1 byte - 0x00 advance(); break; - case Parser.GREETING_SERVER_CAPABILITIES: + case 7: // GREETING_SERVER_CAPABILITIES: if (packet.index == 0) { packet.serverCapabilities = 0; } @@ -185,11 +185,11 @@ Parser.prototype.write = function(buffer) { advance(); } break; - case Parser.GREETING_SERVER_LANGUAGE: + case 8: // GREETING_SERVER_LANGUAGE: packet.serverLanguage = c; advance(); break; - case Parser.GREETING_SERVER_STATUS: + case 9: // GREETING_SERVER_STATUS: if (packet.index == 0) { packet.serverStatus = 0; } @@ -201,13 +201,13 @@ Parser.prototype.write = function(buffer) { advance(); } break; - case Parser.GREETING_FILLER_2: + case 10: // GREETING_FILLER_2: // 13 bytes - 0x00 if (packet.index == 12) { advance(); } break; - case Parser.GREETING_SCRAMBLE_BUFF_2: + case 11: // GREETING_SCRAMBLE_BUFF_2: // 12 bytes - not 13 bytes like the protocol spec says ... if (packet.index < 12) { packet.scrambleBuffer[packet.index + 8] = c; @@ -215,7 +215,7 @@ Parser.prototype.write = function(buffer) { break; // OK_PACKET, ERROR_PACKET, or RESULT_SET_HEADER_PACKET - case Parser.FIELD_COUNT: + case 12: // FIELD_COUNT: if (packet.index == 0) { if (c === 0xff) { packet.type = Parser.ERROR_PACKET; @@ -244,7 +244,7 @@ Parser.prototype.write = function(buffer) { break; // ERROR_PACKET - case Parser.ERROR_NUMBER: + case 13: // ERROR_NUMBER: if (packet.index == 0) { packet.errorNumber = 0; } @@ -263,13 +263,13 @@ Parser.prototype.write = function(buffer) { advance(); } break; - case Parser.ERROR_SQL_STATE_MARKER: + case 14: // ERROR_SQL_STATE_MARKER: // 1 character - always # packet.sqlStateMarker = String.fromCharCode(c); packet.sqlState = ''; advance(); break; - case Parser.ERROR_SQL_STATE: + case 15: // ERROR_SQL_STATE: // 5 characters if (packet.index < 5) { packet.sqlState += String.fromCharCode(c); @@ -279,20 +279,20 @@ Parser.prototype.write = function(buffer) { advance(Parser.ERROR_MESSAGE); } break; - case Parser.ERROR_MESSAGE: + case 16: // ERROR_MESSAGE: if (packet.received <= packet.length) { packet.errorMessage = (packet.errorMessage || '') + String.fromCharCode(c); } break; // OK_PACKET - case Parser.AFFECTED_ROWS: + case 17: // AFFECTED_ROWS: packet.affectedRows = lengthCoded(packet.affectedRows); break; - case Parser.INSERT_ID: + case 18: // INSERT_ID: packet.insertId = lengthCoded(packet.insertId); break; - case Parser.SERVER_STATUS: + case 19: // SERVER_STATUS: if (packet.index == 0) { packet.serverStatus = 0; } @@ -304,7 +304,7 @@ Parser.prototype.write = function(buffer) { advance(); } break; - case Parser.WARNING_COUNT: + case 20: // WARNING_COUNT: if (packet.index == 0) { packet.warningCount = 0; } @@ -317,23 +317,23 @@ Parser.prototype.write = function(buffer) { advance(); } break; - case Parser.MESSAGE: + case 21: // MESSAGE: if (packet.received <= packet.length) { packet.message += String.fromCharCode(c); } break; // RESULT_SET_HEADER_PACKET - case Parser.EXTRA_LENGTH: + case 22: // EXTRA_LENGTH: packet.extra = ''; self._lengthCodedStringLength = lengthCoded(self._lengthCodedStringLength); break; - case Parser.EXTRA_STRING: + case 23: // EXTRA_STRING: packet.extra += String.fromCharCode(c); break; // FIELD_PACKET or EOF_PACKET - case Parser.FIELD_CATALOG_LENGTH: + case 24: // FIELD_CATALOG_LENGTH: if (packet.index == 0) { if (c === 0xfe) { packet.type = Parser.EOF_PACKET; @@ -344,7 +344,7 @@ Parser.prototype.write = function(buffer) { } self._lengthCodedStringLength = lengthCoded(self._lengthCodedStringLength); break; - case Parser.FIELD_CATALOG_STRING: + case 25: // FIELD_CATALOG_STRING: if (packet.index == 0) { packet.catalog = ''; } @@ -354,13 +354,13 @@ Parser.prototype.write = function(buffer) { advance(); } break; - case Parser.FIELD_DB_LENGTH: + case 26: // FIELD_DB_LENGTH: self._lengthCodedStringLength = lengthCoded(self._lengthCodedStringLength); if (self._lengthCodedStringLength == 0) { advance(); } break; - case Parser.FIELD_DB_STRING: + case 27: // FIELD_DB_STRING: if (packet.index == 0) { packet.db = ''; } @@ -370,13 +370,13 @@ Parser.prototype.write = function(buffer) { advance(); } break; - case Parser.FIELD_TABLE_LENGTH: + case 28: // FIELD_TABLE_LENGTH: self._lengthCodedStringLength = lengthCoded(self._lengthCodedStringLength); if (self._lengthCodedStringLength == 0) { advance(); } break; - case Parser.FIELD_TABLE_STRING: + case 29: // FIELD_TABLE_STRING: if (packet.index == 0) { packet.table = ''; } @@ -386,13 +386,13 @@ Parser.prototype.write = function(buffer) { advance(); } break; - case Parser.FIELD_ORIGINAL_TABLE_LENGTH: + case 30: // FIELD_ORIGINAL_TABLE_LENGTH: self._lengthCodedStringLength = lengthCoded(self._lengthCodedStringLength); if (self._lengthCodedStringLength == 0) { advance(); } break; - case Parser.FIELD_ORIGINAL_TABLE_STRING: + case 31: // FIELD_ORIGINAL_TABLE_STRING: if (packet.index == 0) { packet.originalTable = ''; } @@ -402,10 +402,10 @@ Parser.prototype.write = function(buffer) { advance(); } break; - case Parser.FIELD_NAME_LENGTH: + case 32: // FIELD_NAME_LENGTH: self._lengthCodedStringLength = lengthCoded(self._lengthCodedStringLength); break; - case Parser.FIELD_NAME_STRING: + case 33: // FIELD_NAME_STRING: if (packet.index == 0) { packet.name = ''; } @@ -415,13 +415,13 @@ Parser.prototype.write = function(buffer) { advance(); } break; - case Parser.FIELD_ORIGINAL_NAME_LENGTH: + case 34: // FIELD_ORIGINAL_NAME_LENGTH: self._lengthCodedStringLength = lengthCoded(self._lengthCodedStringLength); if (self._lengthCodedStringLength == 0) { advance(); } break; - case Parser.FIELD_ORIGINAL_NAME_STRING: + case 35: // FIELD_ORIGINAL_NAME_STRING: if (packet.index == 0) { packet.originalName = ''; } @@ -431,11 +431,11 @@ Parser.prototype.write = function(buffer) { advance(); } break; - case Parser.FIELD_FILLER_1: + case 36: // FIELD_FILLER_1: // 1 bytes - 0x00 advance(); break; - case Parser.FIELD_CHARSET_NR: + case 37: // FIELD_CHARSET_NR: if (packet.index == 0) { packet.charsetNumber = 0; } @@ -447,7 +447,7 @@ Parser.prototype.write = function(buffer) { advance(); } break; - case Parser.FIELD_LENGTH: + case 38: // FIELD_LENGTH: if (packet.index == 0) { packet.fieldLength = 0; } @@ -459,11 +459,11 @@ Parser.prototype.write = function(buffer) { advance(); } break; - case Parser.FIELD_TYPE: + case 39: // FIELD_TYPE: // 1 byte packet.fieldType = c; advance(); - case Parser.FIELD_FLAGS: + case 40: // FIELD_FLAGS: if (packet.index == 0) { packet.flags = 0; } @@ -475,23 +475,23 @@ Parser.prototype.write = function(buffer) { advance(); } break; - case Parser.FIELD_DECIMALS: + case 41: // FIELD_DECIMALS: // 1 byte packet.decimals = c; advance(); break; - case Parser.FIELD_FILLER_2: + case 42: // FIELD_FILLER_2: // 2 bytes - 0x00 if (packet.index == 1) { advance(); } break; - case Parser.FIELD_DEFAULT: + case 43: // FIELD_DEFAULT: // TODO: Only occurs for mysql_list_fields() break; // EOF_PACKET - case Parser.EOF_WARNING_COUNT: + case 44: // EOF_WARNING_COUNT: if (packet.index == 0) { packet.warningCount = 0; } @@ -503,7 +503,7 @@ Parser.prototype.write = function(buffer) { advance(); } break; - case Parser.EOF_SERVER_STATUS: + case 45: // EOF_SERVER_STATUS: if (packet.index == 0) { packet.serverStatus = 0; } @@ -519,7 +519,7 @@ Parser.prototype.write = function(buffer) { } } break; - case Parser.COLUMN_VALUE_LENGTH: + case 46: // COLUMN_VALUE_LENGTH: if (packet.index == 0) { packet.columnLength = 0; packet.type = Parser.ROW_DATA_PACKET; @@ -548,7 +548,7 @@ Parser.prototype.write = function(buffer) { } } break; - case Parser.COLUMN_VALUE_STRING: + case 47: // COLUMN_VALUE_STRING: var remaining = packet.columnLength - packet.index, read; if (i + remaining > buffer.length) { read = buffer.length - i; diff --git a/lib/mysql/query.js b/lib/mysql/query.js index 983bc8c4f..3bcd01e61 100644 --- a/lib/mysql/query.js +++ b/lib/mysql/query.js @@ -8,6 +8,7 @@ var sys = require('./sys'), function Query(properties) { EventEmitter.call(this); + this.sql = null; this.typeCast = true; for (var key in properties) { @@ -30,6 +31,7 @@ Query.prototype._handlePacket = function(packet) { this.emit('end', Client._packetToUserObject(packet)); break; case Parser.ERROR_PACKET: + packet.sql = this.sql; this.emit('error', Client._packetToUserObject(packet)); break; case Parser.FIELD_PACKET: @@ -52,56 +54,60 @@ Query.prototype._handlePacket = function(packet) { } break; case Parser.ROW_DATA_PACKET: - var row = this._row = {}, - field = this._fields[0]; + var row = this._row = {}, field; this._rowIndex = 0; - row[field.name] = ''; packet.on('data', function(buffer, remaining) { + if (!field) { + field = self._fields[self._rowIndex]; + row[field.name] = ''; + } + if (buffer) { row[field.name] += buffer.toString('utf-8'); } else { row[field.name] = null; } - if (remaining == 0) { - self._rowIndex++; - if (self.typeCast && buffer !== null) { - switch (field.fieldType) { - case Query.FIELD_TYPE_TIMESTAMP: - case Query.FIELD_TYPE_DATE: - case Query.FIELD_TYPE_DATETIME: - case Query.FIELD_TYPE_NEWDATE: - row[field.name] = new Date(row[field.name]+'Z'); - break; - case Query.FIELD_TYPE_TINY: - case Query.FIELD_TYPE_SHORT: - case Query.FIELD_TYPE_LONG: - case Query.FIELD_TYPE_LONGLONG: - case Query.FIELD_TYPE_INT24: - case Query.FIELD_TYPE_YEAR: - row[field.name] = parseInt(row[field.name], 10); - break; - case Query.FIELD_TYPE_DECIMAL: - case Query.FIELD_TYPE_FLOAT: - case Query.FIELD_TYPE_DOUBLE: - case Query.FIELD_TYPE_NEWDECIMAL: - row[field.name] = parseFloat(row[field.name]); - break; - } - } + if (remaining !== 0) { + return; + } - if (self._rowIndex == self._fields.length) { - delete self._row; - delete self._rowIndex; - self.emit('row', row); - return; + self._rowIndex++; + if (self.typeCast && buffer !== null) { + switch (field.fieldType) { + case Query.FIELD_TYPE_TIMESTAMP: + case Query.FIELD_TYPE_DATE: + case Query.FIELD_TYPE_DATETIME: + case Query.FIELD_TYPE_NEWDATE: + row[field.name] = new Date(row[field.name]+'Z'); + break; + case Query.FIELD_TYPE_TINY: + case Query.FIELD_TYPE_SHORT: + case Query.FIELD_TYPE_LONG: + case Query.FIELD_TYPE_LONGLONG: + case Query.FIELD_TYPE_INT24: + case Query.FIELD_TYPE_YEAR: + row[field.name] = parseInt(row[field.name], 10); + break; + case Query.FIELD_TYPE_DECIMAL: + case Query.FIELD_TYPE_FLOAT: + case Query.FIELD_TYPE_DOUBLE: + case Query.FIELD_TYPE_NEWDECIMAL: + row[field.name] = parseFloat(row[field.name]); + break; } + } - field = self._fields[self._rowIndex]; - row[field.name] = ''; + if (self._rowIndex == self._fields.length) { + delete self._row; + delete self._rowIndex; + self.emit('row', row); + return; } + + field = null; }); break; } diff --git a/package.json b/package.json index 3150ad4d8..ace6ea2b8 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { "name" : "mysql" -, "version": "0.8.0" +, "version": "0.9.0" , "devDependencies": {"gently": ">=0.8.0"} , "main" : "./lib/mysql" , "scripts" : { "test" : "make test" } diff --git a/test/common.js b/test/common.js index 47c8e88da..191182ee4 100644 --- a/test/common.js +++ b/test/common.js @@ -2,7 +2,8 @@ var path = require('path'); require.paths.unshift(path.dirname(__dirname)+'/lib'); var sys = require('mysql/sys'); -if (module.parent.filename.match(/test\/system/)) { +var parent = module.parent.filename; +if (parent.match(/test\/system/) || parent.match(/benchmark/)) { try { global.TEST_CONFIG = require('./config'); } catch (e) { diff --git a/test/simple/test-client.js b/test/simple/test-client.js index ca5cbee45..e5d6e099c 100644 --- a/test/simple/test-client.js +++ b/test/simple/test-client.js @@ -4,7 +4,7 @@ var StreamStub = GENTLY.stub('net', 'Stream'), OutgoingPacketStub = GENTLY.stub('./outgoing_packet'), QueryStub = GENTLY.stub('./query'), Parser = require('mysql/parser'), - netBinding = process.binding('net'); + netConstants = require('mysql/net_constants'); for (var k in Parser) { ParserStub[k] = Parser[k]; @@ -119,7 +119,7 @@ test(function connect() { (function testConnectionRefusedError() { var ERR = new Error('ouch'); - ERR.errno = netBinding.ECONNREFUSED; + ERR.errno = netConstants.ECONNREFUSED; CB_DELEGATE = gently.expect(function connectCallback(err) { assert.strictEqual(err, ERR); @@ -197,6 +197,7 @@ test(function query() { QUERY = this; assert.equal(properties.typeCast, client.typeCast); + assert.equal(properties.sql, FORMATED_SQL); var events = ['error', 'field', 'row', 'end']; gently.expect(QUERY, 'on', events.length, function (event, fn) { diff --git a/test/simple/test-query.js b/test/simple/test-query.js index 7a64cccd2..a62a089cd 100644 --- a/test/simple/test-query.js +++ b/test/simple/test-query.js @@ -16,6 +16,7 @@ function test(test) { test(function constructor() { assert.ok(query instanceof EventEmitter); assert.strictEqual(query.typeCast, true); + assert.strictEqual(query.sql, null); assert.equal(new Query({foo: 'bar'}).foo, 'bar'); }); @@ -41,6 +42,7 @@ test(function _handlePacket() { gently.expect(ClientStub, '_packetToUserObject', function (packet) { assert.strictEqual(packet, PACKET); + assert.strictEqual(packet.sql, query.sql); return USER_OBJECT; }); @@ -49,6 +51,7 @@ test(function _handlePacket() { assert.strictEqual(packet, USER_OBJECT); }); + query.sql = 'SELECT bla FROM foo'; query._handlePacket(PACKET); })(); diff --git a/test/system/test-client-connection-error.js b/test/system/test-client-connection-error.js index b61f48fdc..0201a876b 100644 --- a/test/system/test-client-connection-error.js +++ b/test/system/test-client-connection-error.js @@ -2,8 +2,9 @@ require('../common'); var Client = require('mysql').Client, client = Client(TEST_CONFIG), gently = new Gently(), - ECONNREFUSED = process.binding('net').ECONNREFUSED; - ENOTFOUND = process.binding('net').ENOTFOUND; + netConstants = require('mysql/net_constants'); + ECONNREFUSED = netConstants.ECONNREFUSED, + ENOTFOUND = netConstants.ENOTFOUND; client.host = 'BADHOST'; diff --git a/test/system/test-client-query-error.js b/test/system/test-client-query-error.js index 90e0f0f8c..82ec38592 100644 --- a/test/system/test-client-query-error.js +++ b/test/system/test-client-query-error.js @@ -1,5 +1,7 @@ require('../common'); -var Client = require('mysql').Client; +var Client = require('mysql').Client, + INVALID_QUERY_1 = 'first invalid #*&% query', + INVALID_QUERY_2 = 'another #*&% wrong query'; (function testErrorCallback() { // The query callback should receive an error, @@ -12,8 +14,9 @@ var Client = require('mysql').Client; if (error) throw error; })); - client.query('invalid #*&% query', [], gently.expect(function queryCb(error) { + client.query(INVALID_QUERY_1, [], gently.expect(function queryCb(error) { assert.ok(error); + assert.strictEqual(error.sql, INVALID_QUERY_1); client.end(); gently.verify('testErrorCallback'); })); @@ -31,10 +34,11 @@ var Client = require('mysql').Client; if (error) throw error; })); - query = client.query('invalid #*&% query'); + query = client.query(INVALID_QUERY_2); query.on('error', gently.expect(function errCb(error) { assert.ok(error); + assert.strictEqual(error.sql, INVALID_QUERY_2); client.end(); gently.verify('testQueryError'); })); @@ -52,10 +56,11 @@ var Client = require('mysql').Client; if (error) throw error; })); - query = client.query('invalid #*&% query'); + query = client.query(INVALID_QUERY_1); client.on('error', gently.expect(function errCb(error) { assert.ok(error); + assert.strictEqual(error.sql, INVALID_QUERY_1); client.end(); gently.verify('testClientError'); })); @@ -76,13 +81,14 @@ var Client = require('mysql').Client; if (error) throw error; })); - query = client.query('invalid #*&% query'); + query = client.query(INVALID_QUERY_2); query.on('error', dummyHandler); query.removeListener('error', dummyHandler); client.on('error', gently.expect(function errCb(error) { assert.ok(error); + assert.strictEqual(error.sql, INVALID_QUERY_2); client.end(); gently.verify('testRemoveListener'); })); @@ -99,8 +105,9 @@ var Client = require('mysql').Client; if (error) throw error; })); - client.query('invalid #*&% query', [], gently.expect(function queryCb(error) { + client.query(INVALID_QUERY_1, [], gently.expect(function queryCb(error) { assert.ok(error); + assert.strictEqual(error.sql, INVALID_QUERY_1); })); client.query('SHOW STATUS', [], gently.expect(function queryCb(error, rows, fields) { @@ -109,12 +116,14 @@ var Client = require('mysql').Client; assert.equal(Object.keys(fields).length, 2); })); - client.query('invalid #*&% query', [], gently.expect(function queryCb(error) { + client.query(INVALID_QUERY_1, [], gently.expect(function queryCb(error) { assert.ok(error); + assert.strictEqual(error.sql, INVALID_QUERY_1); })); - client.query('invalid #*&% query', [], gently.expect(function errCb(error) { + client.query(INVALID_QUERY_2, [], gently.expect(function errCb(error) { assert.ok(error); + assert.strictEqual(error.sql, INVALID_QUERY_2); client.end(); gently.verify('testSerialError'); }));
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: