[Pkg-javascript-commits] [node-mysql] 01/20: Imported Upstream version 0.9.6
Mike Gabriel
sunweaver at debian.org
Wed Aug 20 13:05:38 UTC 2014
This is an automated email from the git hooks/post-receive script.
sunweaver pushed a commit to branch master
in repository node-mysql.
commit 375f9b2205c7401f181aa377310ddbb0490256c7
Author: Mike Gabriel <mike.gabriel at das-netzwerkteam.de>
Date: Mon May 20 00:50:22 2013 +0200
Imported Upstream version 0.9.6
---
.gitignore | 4 +
.travis.yml | 4 +
License | 19 +
Makefile | 13 +
Readme.md | 300 +++++++++
benchmark/node-mysql/insert.js | 42 ++
benchmark/node-mysql/select.js | 40 ++
benchmark/php/insert-select.php | 40 ++
index.js | 1 +
lib/auth.js | 164 +++++
lib/client.js | 461 ++++++++++++++
lib/constants.js | 673 +++++++++++++++++++++
lib/mysql.js | 18 +
lib/outgoing_packet.js | 79 +++
lib/parser.js | 650 ++++++++++++++++++++
lib/query.js | 140 +++++
package.json | 27 +
test/common.js | 23 +
test/config.template.js | 8 +
test/fixture/columnia.sql | 54 ++
test/fixture/libmysql_password.c | 127 ++++
test/integration/Client/commands/test-ping.js | 11 +
.../integration/Client/commands/test-statistics.js | 10 +
.../Client/commands/test-useDatabase.js | 13 +
.../Client/connection/test-automatic-connect.js | 11 +
.../test-automatic-reconnect-on-timeout.js | 24 +
.../Client/connection/test-bad-credentials.js.js | 24 +
.../test-connection-errors-go-to-callback.js.js | 25 +
.../connection/test-reconnect-closed-client.js.js | 27 +
.../test-should-delegate-to-client-if-needed.js | 17 +
.../Query/errors/test-should-emit-error-event.js | 18 +
...test-should-not-leave-client-in-broken-state.js | 37 ++
.../Query/errors/test-should-raise-callback.js | 16 +
.../integration/Query/misc/test-column-ordering.js | 36 ++
.../integration/Query/results/test-empty-string.js | 14 +
test/integration/Query/results/test-long-fields.js | 43 ++
test/integration/Query/results/test-null-value.js | 15 +
.../Query/results/test-real-world-usage.js | 51 ++
.../Query/results/test-virtual-fields.js | 16 +
test/run.js | 1 +
test/unit/legacy/common.js | 25 +
test/unit/legacy/test-auth.js | 118 ++++
test/unit/legacy/test-client.js | 95 +++
test/unit/legacy/test-outgoing-packet.js | 134 ++++
test/unit/legacy/test-parser.js | 387 ++++++++++++
test/unit/legacy/test-query.js | 68 +++
test/unit/test-client.js | 33 +
test/unit/test-mysql.js | 10 +
tool/pcap-mysql.js | 48 ++
49 files changed, 4214 insertions(+)
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..63c9273
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+/node_modules
+/test/config.js
+*.swo
+*.un~
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..f1d0f13
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,4 @@
+language: node_js
+node_js:
+ - 0.4
+ - 0.6
diff --git a/License b/License
new file mode 100644
index 0000000..37f77ee
--- /dev/null
+++ b/License
@@ -0,0 +1,19 @@
+Copyright (c) 2010 Felix Geisendörfer (felix at debuggable.com)
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
\ No newline at end of file
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..5b7340c
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,13 @@
+SHELL := /bin/bash
+NODE = node
+
+test:
+ @$(NODE) test/run.js
+benchmark-node-mysql:
+ @find benchmark/node-mysql/*.js | xargs -n 1 -t node
+benchmark-php:
+ @find benchmark/php/*.php | xargs -n 1 -t php
+benchmark-all: benchmark-node-mysql benchmark-php
+benchmark: benchmark-node-mysql
+
+.PHONY: test benchmark
diff --git a/Readme.md b/Readme.md
new file mode 100644
index 0000000..e801a13
--- /dev/null
+++ b/Readme.md
@@ -0,0 +1,300 @@
+# node-mysql
+
+[![Build Status](https://secure.travis-ci.org/felixge/node-mysql.png)](http://travis-ci.org/felixge/node-mysql)
+
+## Purpose
+
+A pure node.js JavaScript Client implementing the [MySQL protocol](http://forge.mysql.com/wiki/MySQL_Internals_ClientServer_Protocol).
+
+## Support this module
+
+If you like this module, check out and spread the word about our service
+[transloadit.com][]. We provide file uploading and encoding functionality to
+other applications, and have performed billions of queries with this module so
+far.
+
+[transloadit.com]: http://transloadit.com/
+
+## Installation
+
+```
+npm install mysql
+```
+
+**Important**: If you are upgrading from 0.9.1 or below, there have been
+backwards incompatible changes in the API. Please read the [upgrading guide][].
+
+[upgrading guide]: https://github.com/felixge/node-mysql/wiki/Upgrading-to-0.9.2+
+
+## Usage
+
+``` javascript
+var mysql = require('mysql');
+var TEST_DATABASE = 'nodejs_mysql_test';
+var TEST_TABLE = 'test';
+var client = mysql.createClient({
+ user: 'root',
+ password: 'root',
+});
+
+client.query('CREATE DATABASE '+TEST_DATABASE, function(err) {
+ if (err && err.number != mysql.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_DATABASE);
+
+client.query(
+ 'CREATE TEMPORARY TABLE '+TEST_TABLE+
+ '(id INT(11) AUTO_INCREMENT, '+
+ 'title VARCHAR(255), '+
+ 'text TEXT, '+
+ 'created DATETIME, '+
+ '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']
+);
+
+var query = client.query(
+ 'INSERT INTO '+TEST_TABLE+' '+
+ 'SET title = ?, text = ?, created = ?',
+ ['another entry', 'because 2 entries make a better test', '2010-08-16 12:42:15']
+);
+
+client.query(
+ 'SELECT * FROM '+TEST_TABLE,
+ function selectCb(err, results, fields) {
+ if (err) {
+ throw err;
+ }
+
+ console.log(results);
+ console.log(fields);
+ client.end();
+ }
+);
+```
+
+## API
+
+### mysql.createClient([options])
+
+Creates a new client instance. Any client property can be set using the
+`options` object.
+
+### client.host = 'localhost'
+
+The host to connect to.
+
+### client.port = 3306
+
+The port to connect to.
+
+### client.user = null
+
+The username to authenticate as.
+
+### client.password = null
+
+The password to use.
+
+### client.database = null
+
+The name of the database to connect to (optional).
+
+### client.debug = false
+
+Prints incoming and outgoing packets, useful for development / testing purposes.
+
+### client.flags = Client.defaultFlags
+
+Connection flags send to the server.
+
+### client.query(sql, [params, cb])
+
+Sends a `sql` query to the server. `'?'` characters can be used as placeholders
+for an array of `params` that will be safely escaped before sending the final
+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.
+
+### client.useDatabase(database, [cb])
+
+Same as issuing a `'USE <database>'` query.
+
+### client.statistics([cb])
+
+Returns some server statistics provided by MySql.
+
+### client.format(sql, params)
+
+Allows to safely insert a list of `params` into a `sql` string using the
+placeholder mechanism described above.
+
+### client.escape(val)
+
+Escapes a single `val` for use inside of a sql string.
+
+### client.destroy()
+
+Forces the client connection / socket to be destroyed right away.
+
+### client.end([cb])
+
+Schedule a COM_QUIT packet for closing the connection. All currently queued
+queries will still execute before the graceful termination of the connection
+is attempted.
+
+### client event: 'error' (err)
+
+When the client has no callback / delegate for an error, it is emitted with this
+event instead.
+
+### new mysql.Query()
+
+Query objects are not meant to be invoked manually. To get a query object, use
+the `client.query` API.
+
+### query event: 'error' (err)
+
+Emitted when mysql returns an error packet for the query.
+
+### query event: 'field' (field)
+
+Emitted upon receiving a field packet from mysql.
+
+### query event: 'row' (row)
+
+Emitted upon receiving a row. An option for streaming the contents of the row
+itself will be made available soon.
+
+### query event: 'end' ([result])
+
+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);
+ });
+
+### How can I find out the number of rows affected by the last query?
+
+ client.query('UPDATE my_table SET title = ?', function(err, info) {
+ console.log(info.affectedRows);
+ });
+
+## Todo
+
+At this point the module is ready to be tried out, but a lot of things are yet to be done:
+
+* Implement retry
+* Pause / resume
+* Remaining mysql commands
+* Prepared Statements
+* Packet's > 16 MB
+* Compression
+* Decide how to handle queries with multiple statements
+* Transactions
+
+A stop-gap solution to support multiple statements and transactions is
+available. Check it out here: http://github.com/bminer/node-mysql-queues
+
+## Contributors
+
+[Click here][contributors] for a full list of contributors.
+
+[contributors]: https://github.com/felixge/node-mysql/contributors
+
+## Sponsors
+
+* [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
+
+This is a rather large project requiring a significant amount of my limited resources.
+
+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 at debuggable.com](mailto:felix at 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
+testing.
+
+[transloadit]: http://transloadit.com/
+
+## Changelog
+
+### v0.9.5
+
+* Fix #142 Driver stalls upon reconnect attempt that's immediately closed
+* Add travis build
+* Switch to urun as a test runner
+* Switch to utest for unit tests
+* Remove fast-or-slow dependency for tests
+* Split integration tests into individual files again
+
+### v0.9.4
+
+* Expose package.json as `mysql.PACKAGE` (#104)
+
+### v0.9.3
+
+* Set default `client.user` to root
+* Fix #91: Client#format should not mutate params array
+* Fix #94: TypeError in client.js
+* Parse decimals as string (vadimg)
+
+### v0.9.2
+
+* The underlaying socket connection is now managed implicitly rather than explicitly.
+* Check the [upgrading guide][] for a full list of changes.
+
+### v0.9.1
+
+* Fix issue #49 / `client.escape()` throwing exceptions on objects. (Nick Payne)
+* Drop < v0.4.x compatibility. From now on you need node v0.4.x to use this module.
+
+### Older releases
+
+These releases were done before starting to maintain the above Changelog:
+
+* [v0.9.0](https://github.com/felixge/node-mysql/compare/v0.8.0...v0.9.0)
+* [v0.8.0](https://github.com/felixge/node-mysql/compare/v0.7.0...v0.8.0)
+* [v0.7.0](https://github.com/felixge/node-mysql/compare/v0.6.0...v0.7.0)
+* [v0.6.0](https://github.com/felixge/node-mysql/compare/v0.5.0...v0.6.0)
+* [v0.5.0](https://github.com/felixge/node-mysql/compare/v0.4.0...v0.5.0)
+* [v0.4.0](https://github.com/felixge/node-mysql/compare/v0.3.0...v0.4.0)
+* [v0.3.0](https://github.com/felixge/node-mysql/compare/v0.2.0...v0.3.0)
+* [v0.2.0](https://github.com/felixge/node-mysql/compare/v0.1.0...v0.2.0)
+* [v0.1.0](https://github.com/felixge/node-mysql/commits/v0.1.0)
+
+## License
+
+node-mysql is licensed under the MIT license.
diff --git a/benchmark/node-mysql/insert.js b/benchmark/node-mysql/insert.js
new file mode 100644
index 0000000..d6ef00a
--- /dev/null
+++ b/benchmark/node-mysql/insert.js
@@ -0,0 +1,42 @@
+require('../../test/common');
+var Client = require('mysql/client'),
+ client = Client(TEST_CONFIG);
+
+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);
+client.query('DROP TABLE IF EXISTS '+TEST_TABLE);
+client.query(
+ 'CREATE TABLE '+TEST_TABLE+' ('+
+ 'id INT(11) AUTO_INCREMENT, '+
+ 'title VARCHAR(255), '+
+ 'text TEXT, '+
+ 'created DATETIME, '+
+ 'PRIMARY KEY (id));',
+ function(err) {
+ if (err) throw err;
+
+ var start = +new Date, inserts = 0, total = 10000;
+ function insertOne() {
+ client.query('INSERT INTO '+TEST_TABLE+' SET title = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"', function() {
+ inserts++;
+ if (inserts < total) {
+ insertOne();
+ } else {
+ var duration = (+new Date - start) / 1000,
+ insertsPerSecond = inserts / duration;
+
+ console.log('%d inserts / second', insertsPerSecond.toFixed(2));
+ console.log('%d ms', +new Date - start);
+ client.end();
+ }
+ });
+ }
+ insertOne();
+ }
+);
diff --git a/benchmark/node-mysql/select.js b/benchmark/node-mysql/select.js
new file mode 100644
index 0000000..696d2b3
--- /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
new file mode 100644
index 0000000..c3c3a51
--- /dev/null
+++ b/benchmark/php/insert-select.php
@@ -0,0 +1,40 @@
+<?php
+$INSERTS = 10000;
+
+$config = array(
+ 'host' => 'localhost',
+ 'port' => 3306,
+ 'user' => 'root',
+ 'password' => 'root',
+ 'db' => 'node_mysql_test',
+ 'table' => 'post',
+);
+extract($config);
+
+$connection = mysql_connect($host, $user, $password);
+mysql_query('USE '.$db, $connection);
+mysql_query('CREATE TEMPORARY TABLE '.$table.' ('.
+'id INT(11) AUTO_INCREMENT, '.
+'title VARCHAR(255), '.
+'text TEXT, '.
+'created DATETIME, '.
+'PRIMARY KEY (id));', $connection);
+
+$start = microtime(true);
+for ($i = 0; $i < $INSERTS; $i++) {
+ mysql_query('INSERT INTO '.$table.' SET title = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";', $connection);
+}
+$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);
+while ($a = mysql_fetch_assoc($q)) {
+}
+$duration = (microtime(true) - $start);
+$rowsPerSecond = $INSERTS / $duration;
+echo sprintf("%d rows / second\n", $rowsPerSecond);
+echo sprintf("%d ms\n", $duration * 1000);
+?>
diff --git a/index.js b/index.js
new file mode 100644
index 0000000..782e199
--- /dev/null
+++ b/index.js
@@ -0,0 +1 @@
+module.exports = require('./lib/mysql');
diff --git a/lib/auth.js b/lib/auth.js
new file mode 100644
index 0000000..0380aab
--- /dev/null
+++ b/lib/auth.js
@@ -0,0 +1,164 @@
+var Buffer = require('buffer').Buffer;
+var crypto = require('crypto');
+
+function sha1(msg) {
+ var hash = crypto.createHash('sha1');
+ hash.update(msg);
+ // hash.digest() does not output buffers yet
+ return hash.digest('binary');
+};
+exports.sha1 = sha1;
+
+function xor(a, b) {
+ a = new Buffer(a, 'binary');
+ b = new Buffer(b, 'binary');
+ var result = new Buffer(a.length);
+ for (var i = 0; i < a.length; i++) {
+ result[i] = (a[i] ^ b[i]);
+ }
+ return result;
+};
+exports.xor = xor;
+
+exports.token = function(password, scramble) {
+ if (!password) {
+ return new Buffer(0);
+ }
+
+ var stage1 = sha1(password);
+ var stage2 = sha1(stage1);
+ var stage3 = sha1(scramble.toString('binary') + stage2);
+ return xor(stage3, stage1);
+};
+
+// This is a port of sql/password.c:hash_password which needs to be used for
+// pre-4.1 passwords.
+exports.hashPassword = function(password) {
+ var nr = [0x5030, 0x5735],
+ add = 7,
+ nr2 = [0x1234, 0x5671],
+ result = new Buffer(8);
+
+ if (typeof password == 'string'){
+ password = new Buffer(password);
+ }
+
+ for (var i = 0; i < password.length; i++) {
+ var c = password[i];
+ if (c == 32 || c == 9) {
+ // skip space in password
+ continue;
+ }
+
+ // nr^= (((nr & 63)+add)*c)+ (nr << 8);
+ // nr = xor(nr, add(mul(add(and(nr, 63), add), c), shl(nr, 8)))
+ nr = this.xor32(nr, this.add32(this.mul32(this.add32(this.and32(nr, [0,63]), [0,add]), [0,c]), this.shl32(nr, 8)));
+
+ // nr2+=(nr2 << 8) ^ nr;
+ // nr2 = add(nr2, xor(shl(nr2, 8), nr))
+ nr2 = this.add32(nr2, this.xor32(this.shl32(nr2, 8), nr));
+
+ // add+=tmp;
+ add += c;
+ }
+
+ this.int31Write(result, nr, 0);
+ this.int31Write(result, nr2, 4);
+
+ return result;
+};
+
+exports.randomInit = function(seed1, seed2) {
+ return {
+ max_value: 0x3FFFFFFF,
+ max_value_dbl: 0x3FFFFFFF,
+ seed1: seed1 % 0x3FFFFFFF,
+ seed2: seed2 % 0x3FFFFFFF
+ };
+};
+
+exports.myRnd = function(r){
+ r.seed1 = (r.seed1 * 3 + r.seed2) % r.max_value;
+ r.seed2 = (r.seed1 + r.seed2 + 33) % r.max_value;
+
+ return r.seed1 / r.max_value_dbl;
+};
+
+exports.scramble323 = function(message, password) {
+ var to = new Buffer(8),
+ hashPass = this.hashPassword(password),
+ hashMessage = this.hashPassword(message.slice(0, 8)),
+ seed1 = this.int32Read(hashPass, 0) ^ this.int32Read(hashMessage, 0),
+ seed2 = this.int32Read(hashPass, 4) ^ this.int32Read(hashMessage, 4),
+ r = this.randomInit(seed1, seed2);
+
+ for (var i = 0; i < 8; i++){
+ to[i] = Math.floor(this.myRnd(r) * 31) + 64;
+ }
+ var extra = (Math.floor(this.myRnd(r) * 31));
+
+ for (var i = 0; i < 8; i++){
+ to[i] ^= extra;
+ }
+
+ return to;
+};
+
+exports.fmt32 = function(x){
+ var a = x[0].toString(16),
+ b = x[1].toString(16);
+
+ if (a.length == 1) a = '000'+a;
+ if (a.length == 2) a = '00'+a;
+ if (a.length == 3) a = '0'+a;
+ if (b.length == 1) b = '000'+b;
+ if (b.length == 2) b = '00'+b;
+ if (b.length == 3) b = '0'+b;
+ return '' + a + '/' + b;
+};
+
+exports.xor32 = function(a,b){
+ return [a[0] ^ b[0], a[1] ^ b[1]];
+};
+
+exports.add32 = function(a,b){
+ var w1 = a[1] + b[1],
+ w2 = a[0] + b[0] + ((w1 & 0xFFFF0000) >> 16);
+
+ return [w2 & 0xFFFF, w1 & 0xFFFF];
+};
+
+exports.mul32 = function(a,b){
+ // based on this example of multiplying 32b ints using 16b
+ // http://www.dsprelated.com/showmessage/89790/1.php
+ var w1 = a[1] * b[1],
+ w2 = (((a[1] * b[1]) >> 16) & 0xFFFF) + ((a[0] * b[1]) & 0xFFFF) + (a[1] * b[0] & 0xFFFF);
+
+ return [w2 & 0xFFFF, w1 & 0xFFFF];
+};
+
+exports.and32 = function(a,b){
+ return [a[0] & b[0], a[1] & b[1]];
+};
+
+exports.shl32 = function(a,b){
+ // assume b is 16 or less
+ var w1 = a[1] << b,
+ w2 = (a[0] << b) | ((w1 & 0xFFFF0000) >> 16);
+
+ return [w2 & 0xFFFF, w1 & 0xFFFF];
+};
+
+exports.int31Write = function(buffer, number, offset) {
+ buffer[offset] = (number[0] >> 8) & 0x7F;
+ buffer[offset + 1] = (number[0]) & 0xFF;
+ buffer[offset + 2] = (number[1] >> 8) & 0xFF;
+ buffer[offset + 3] = (number[1]) & 0xFF;
+};
+
+exports.int32Read = function(buffer, offset){
+ return (buffer[offset] << 24)
+ + (buffer[offset+1] << 16)
+ + (buffer[offset+2] << 8)
+ + (buffer[offset+3]);
+};
diff --git a/lib/client.js b/lib/client.js
new file mode 100644
index 0000000..d6c9560
--- /dev/null
+++ b/lib/client.js
@@ -0,0 +1,461 @@
+var util = require('util');
+var Socket = require('net').Socket;
+var auth = require('./auth');
+var constants = require('./constants');
+var Parser = require('./parser');
+var OutgoingPacket = require('./outgoing_packet');
+var Query = require('./query');
+var EventEmitter = require('events').EventEmitter;
+
+function Client() {
+ if (!(this instanceof Client) || arguments.length) {
+ throw new Error('deprecated: use mysql.createClient() instead');
+ }
+
+ EventEmitter.call(this);
+
+ this.host = 'localhost';
+ this.port = 3306;
+ this.user = 'root';
+ this.password = null;
+ this.database = '';
+
+ this.typeCast = true;
+ this.flags = Client.defaultFlags;
+ this.maxPacketSize = 0x01000000;
+ this.charsetNumber = constants.UTF8_UNICODE_CI;
+ this.debug = false;
+ this.ending = false;
+ this.connected = false;
+
+ this._greeting = null;
+ this._queue = [];
+ this._socket = null;
+ this._parser = null;
+};
+util.inherits(Client, EventEmitter);
+module.exports = Client;
+
+Client.prototype.connect = function() {
+ throw new Error('deprecated: connect() is now done automatically.');
+};
+
+Client.prototype._connect = function() {
+ this.destroy();
+
+ var socket = this._socket = new Socket();
+ var parser = this._parser = new Parser();
+ var self = this;
+
+ socket
+ .on('error', this._connectionErrorHandler())
+ .on('data', parser.write.bind(parser))
+ .on('end', function() {
+ if (self.ending) {
+ // @todo destroy()?
+ self.connected = false;
+ self.ending = false;
+
+ if (self._queue.length) {
+ self._connect();
+ }
+
+ return;
+ }
+
+ if (!self.connected) {
+ this.emit('error', new Error('reconnection attempt failed before connection was fully set up'));
+ return;
+ }
+
+ self._connect();
+ })
+ .connect(this.port, this.host);
+
+ parser.on('packet', this._handlePacket.bind(this));
+};
+
+Client.prototype.query = function(sql, params, cb) {
+ if (Array.isArray(params)) {
+ sql = this.format(sql, params);
+ } else {
+ cb = arguments[1];
+ }
+
+ var query = new Query({
+ typeCast: this.typeCast,
+ sql: sql
+ });
+
+ var self = this;
+ if (cb) {
+ var rows = [], fields = {};
+ query
+ .on('error', function(err) {
+ cb(err);
+ self._dequeue();
+ })
+ .on('field', function(field) {
+ fields[field.name] = field;
+ })
+ .on('row', function(row) {
+ rows.push(row);
+ })
+ .on('end', function(result) {
+ if (result) {
+ cb(null, result);
+ } else {
+ cb(null, rows, fields);
+ }
+
+ self._dequeue();
+ });
+ } else {
+ query
+ .on('error', function(err) {
+ if (query.listeners('error').length <= 1) {
+ self.emit('error', err);
+ }
+ self._dequeue();
+ })
+ .on('end', function(result) {
+ self._dequeue();
+ });
+ }
+
+ this._enqueue(function query() {
+ var packet = new OutgoingPacket(1 + Buffer.byteLength(sql, 'utf-8'));
+
+ packet.writeNumber(1, constants.COM_QUERY);
+ packet.write(sql, 'utf-8');
+ self.write(packet);
+ }, query);
+
+ return query;
+};
+
+Client.prototype.write = function(packet) {
+ if (this.debug) {
+ console.log('-> %s', packet.buffer.inspect());
+ }
+
+ this._socket.write(packet.buffer);
+};
+
+Client.prototype.format = function(sql, params) {
+ var escape = this.escape;
+ params = params.concat();
+
+ sql = sql.replace(/\?/g, function() {
+ if (params.length == 0) {
+ throw new Error('too few parameters given');
+ }
+
+ return escape(params.shift());
+ });
+
+ if (params.length) {
+ throw new Error('too many parameters given');
+ }
+
+ return sql;
+};
+
+Client.prototype.escape = function(val) {
+ var escape = this.escape;
+
+ if (val === undefined || val === null) {
+ return 'NULL';
+ }
+
+ switch (typeof val) {
+ case 'boolean': return (val) ? 'true' : 'false';
+ case 'number': return val+'';
+ }
+
+ if (Array.isArray(val)) {
+ var sanitized = val.map( function( v ) { return escape( v ); } );
+ return "'" + sanitized.join( "','" ) + "'";
+ }
+
+ if (typeof val === 'object') {
+ val = (typeof val.toISOString === 'function')
+ ? val.toISOString()
+ : val.toString();
+ }
+
+ val = val.replace(/[\0\n\r\b\t\\\'\"\x1a]/g, function(s) {
+ switch(s) {
+ case "\0": return "\\0";
+ case "\n": return "\\n";
+ case "\r": return "\\r";
+ case "\b": return "\\b";
+ case "\t": return "\\t";
+ case "\x1a": return "\\Z";
+ default: return "\\"+s;
+ }
+ });
+ return "'"+val+"'";
+};
+
+Client.prototype.ping = function(cb) {
+ var self = this;
+ this._enqueue(function ping() {
+ var packet = new OutgoingPacket(1);
+ packet.writeNumber(1, constants.COM_PING);
+ self.write(packet);
+ }, cb);
+};
+
+Client.prototype.statistics = function(cb) {
+ var self = this;
+ this._enqueue(function statistics() {
+ var packet = new OutgoingPacket(1);
+ packet.writeNumber(1, constants.COM_STATISTICS);
+ self.write(packet);
+ }, cb);
+};
+
+Client.prototype.useDatabase = function(database, cb) {
+ var self = this;
+ this._enqueue(function useDatabase() {
+ var packet = new OutgoingPacket(1 + Buffer.byteLength(database, 'utf-8'));
+ packet.writeNumber(1, constants.COM_INIT_DB);
+ packet.write(database, 'utf-8');
+ self.write(packet);
+ }, cb);
+};
+
+Client.prototype.destroy = function() {
+ if (this._socket) {
+ this._socket.destroy();
+ }
+
+ this._socket = null;
+ this._parser = null;
+ this.connected = false;
+}
+
+Client.prototype.end = function(cb) {
+ var self = this;
+
+ this.ending = true;
+
+ this._enqueue(function end() {
+ var packet = new OutgoingPacket(1);
+ packet.writeNumber(1, constants.COM_QUIT);
+ self.write(packet);
+
+ // @todo handle clean shut down properly
+ if (cb) {
+ self._socket.on('end', cb);
+ }
+
+ self._dequeue();
+ }, cb);
+};
+
+Client.prototype._enqueue = function(fn, delegate) {
+ if (!this._socket) {
+ this._connect();
+ }
+
+ this._queue.push({fn: fn, delegate: delegate});
+ if (this._queue.length === 1 && this.connected) {
+ fn();
+ }
+};
+
+Client.prototype._dequeue = function() {
+ this._queue.shift();
+
+ if (!this._queue.length) {
+ return;
+ }
+
+ if (!this.connected) {
+ this._connect();
+ return;
+ }
+
+ this._queue[0].fn();
+};
+
+Client.prototype._handlePacket = function(packet) {
+ if (this.debug) {
+ this._debugPacket(packet);
+ }
+
+ if (packet.type == Parser.GREETING_PACKET) {
+ this._sendAuth(packet);
+ return;
+ }
+
+ if (packet.type == Parser.USE_OLD_PASSWORD_PROTOCOL_PACKET) {
+ this._sendOldAuth(this._greeting);
+ return;
+ }
+
+ if (!this.connected) {
+ if (packet.type != Parser.ERROR_PACKET) {
+ this.connected = true;
+
+ if (this._queue.length) this._queue[0].fn();
+ return;
+ }
+
+ this._connectionErrorHandler()(Client._packetToUserObject(packet));
+ return;
+ }
+
+ // @TODO Simplify the code below and above as well
+ var type = packet.type;
+ var task = this._queue[0];
+ var delegate = (task)
+ ? task.delegate
+ : null;
+
+ if (delegate instanceof Query) {
+ delegate._handlePacket(packet);
+ return;
+ }
+
+ if (type != Parser.ERROR_PACKET) {
+ this.connected = true;
+ if (delegate) {
+ delegate(null, Client._packetToUserObject(packet));
+ }
+ } else {
+ packet = Client._packetToUserObject(packet);
+ if (delegate) {
+ delegate(packet);
+ } else {
+ this.emit('error', packet);
+ }
+ }
+ this._dequeue();
+};
+
+Client.prototype._connectionErrorHandler = function() {
+ return function(err) {
+ this.destroy();
+
+ var task = this._queue[0];
+ var delegate = (task)
+ ? task.delegate
+ : null;
+
+ if (delegate instanceof Query) {
+ delegate.emit('error', err);
+ return;
+ }
+
+ if (!delegate) {
+ this.emit('error', err);
+ } else {
+ delegate(err);
+ this._queue.shift();
+ }
+
+ if (this._queue.length) {
+ this._connect();
+ }
+ }.bind(this);
+};
+
+Client.prototype._sendAuth = function(greeting) {
+ var token = auth.token(this.password, greeting.scrambleBuffer);
+ var packetSize = (
+ 4 + 4 + 1 + 23 +
+ this.user.length + 1 +
+ token.length + 1 +
+ this.database.length + 1
+ );
+ var packet = new OutgoingPacket(packetSize, greeting.number+1);
+
+ packet.writeNumber(4, this.flags);
+ packet.writeNumber(4, this.maxPacketSize);
+ packet.writeNumber(1, this.charsetNumber);
+ packet.writeFiller(23);
+ packet.writeNullTerminated(this.user);
+ packet.writeLengthCoded(token);
+ packet.writeNullTerminated(this.database);
+
+ this.write(packet);
+
+ // Keep a reference to the greeting packet. We might receive a
+ // USE_OLD_PASSWORD_PROTOCOL_PACKET as a response, in which case we will need
+ // the greeting packet again. See _sendOldAuth()
+ this._greeting = greeting;
+};
+
+Client._packetToUserObject = function(packet) {
+ var userObject = (packet.type == Parser.ERROR_PACKET)
+ ? new Error()
+ : {};
+
+ for (var key in packet) {
+ var newKey = key;
+ if (key == 'type' || key == 'number' || key == 'length' || key == 'received') {
+ continue;
+ }
+
+ if (key == 'errorMessage') {
+ newKey = 'message';
+ } else if (key == 'errorNumber') {
+ newKey = 'number';
+ }
+
+ userObject[newKey] = packet[key];
+ }
+
+ return userObject;
+};
+
+Client.prototype._debugPacket = function(packet) {
+ var packetName = null;
+ for (var key in Parser) {
+ if (!key.match(/_PACKET$/)) {
+ continue;
+ }
+
+ if (Parser[key] == packet.type) {
+ packetName = key;
+ break;
+ }
+ }
+ console.log('<- %s: %j', packetName, packet);
+};
+
+Client.prototype._sendOldAuth = function(greeting) {
+ var token = auth.scramble323(greeting.scrambleBuffer, this.password);
+ var packetSize = (
+ token.length + 1
+ );
+ var packet = new OutgoingPacket(packetSize, greeting.number+3);
+
+ // I could not find any official documentation for this, but from sniffing
+ // the mysql command line client, I think this is the right way to send the
+ // scrambled token after receiving the USE_OLD_PASSWORD_PROTOCOL_PACKET.
+ packet.write(token);
+ packet.writeFiller(1);
+
+ this.write(packet);
+};
+
+Client.defaultFlags =
+ constants.CLIENT_LONG_PASSWORD
+ | constants.CLIENT_FOUND_ROWS
+ | constants.CLIENT_LONG_FLAG
+ | constants.CLIENT_CONNECT_WITH_DB
+ | constants.CLIENT_ODBC
+ | constants.CLIENT_LOCAL_FILES
+ | constants.CLIENT_IGNORE_SPACE
+ | constants.CLIENT_PROTOCOL_41
+ | constants.CLIENT_INTERACTIVE
+ | constants.CLIENT_IGNORE_SIGPIPE
+ | constants.CLIENT_TRANSACTIONS
+ | constants.CLIENT_RESERVED
+ | constants.CLIENT_SECURE_CONNECTION
+ | constants.CLIENT_MULTI_STATEMENTS
+ | constants.CLIENT_MULTI_RESULTS;
diff --git a/lib/constants.js b/lib/constants.js
new file mode 100644
index 0000000..b5bdfc0
--- /dev/null
+++ b/lib/constants.js
@@ -0,0 +1,673 @@
+var hashish = require('hashish');
+
+// Connections Flags
+hashish.update(exports, {
+ CLIENT_LONG_PASSWORD : 1,
+ CLIENT_FOUND_ROWS : 2,
+ CLIENT_LONG_FLAG : 4,
+ CLIENT_CONNECT_WITH_DB : 8,
+ CLIENT_NO_SCHEMA : 16,
+ CLIENT_COMPRESS : 32,
+ CLIENT_ODBC : 64,
+ CLIENT_LOCAL_FILES : 128,
+ CLIENT_IGNORE_SPACE : 256,
+ CLIENT_PROTOCOL_41 : 512,
+ CLIENT_INTERACTIVE : 1024,
+ CLIENT_SSL : 2048,
+ CLIENT_IGNORE_SIGPIPE : 4096,
+ CLIENT_TRANSACTIONS : 8192,
+ CLIENT_RESERVED : 16384,
+ CLIENT_SECURE_CONNECTION : 32768,
+ CLIENT_MULTI_STATEMENTS : 65536,
+ CLIENT_MULTI_RESULTS : 131072,
+});
+
+// Commands
+hashish.update(exports, {
+ COM_SLEEP : 0x00,
+ COM_QUIT : 0x01,
+ COM_INIT_DB : 0x02,
+ COM_QUERY : 0x03,
+ COM_FIELD_LIST : 0x04,
+ COM_CREATE_DB : 0x05,
+ COM_DROP_DB : 0x06,
+ COM_REFRESH : 0x07,
+ COM_SHUTDOWN : 0x08,
+ COM_STATISTICS : 0x09,
+ COM_PROCESS_INFO : 0x0a,
+ COM_CONNECT : 0x0b,
+ COM_PROCESS_KILL : 0x0c,
+ COM_DEBUG : 0x0d,
+ COM_PING : 0x0e,
+ COM_TIME : 0x0f,
+ COM_DELAYED_INSERT : 0x10,
+ COM_CHANGE_USER : 0x11,
+ COM_BINLOG_DUMP : 0x12,
+ COM_TABLE_DUMP : 0x13,
+ COM_CONNECT_OUT : 0x14,
+ COM_REGISTER_SLAVE : 0x15,
+ COM_STMT_PREPARE : 0x16,
+ COM_STMT_EXECUTE : 0x17,
+ COM_STMT_SEND_LONG_DATA : 0x18,
+ COM_STMT_CLOSE : 0x19,
+ COM_STMT_RESET : 0x1a,
+ COM_SET_OPTION : 0x1b,
+ COM_STMT_FETCH : 0x1c,
+});
+
+// Collations / Charsets
+hashish.update(exports, {
+ BIG5_CHINESE_CI : 1,
+ LATIN2_CZECH_CS : 2,
+ DEC8_SWEDISH_CI : 3,
+ CP850_GENERAL_CI : 4,
+ LATIN1_GERMAN1_CI : 5,
+ HP8_ENGLISH_CI : 6,
+ KOI8R_GENERAL_CI : 7,
+ LATIN1_SWEDISH_CI : 8,
+ LATIN2_GENERAL_CI : 9,
+ SWE7_SWEDISH_CI : 10,
+ ASCII_GENERAL_CI : 11,
+ UJIS_JAPANESE_CI : 12,
+ SJIS_JAPANESE_CI : 13,
+ CP1251_BULGARIAN_CI : 14,
+ LATIN1_DANISH_CI : 15,
+ HEBREW_GENERAL_CI : 16,
+ TIS620_THAI_CI : 18,
+ EUCKR_KOREAN_CI : 19,
+ LATIN7_ESTONIAN_CS : 20,
+ LATIN2_HUNGARIAN_CI : 21,
+ KOI8U_GENERAL_CI : 22,
+ CP1251_UKRAINIAN_CI : 23,
+ GB2312_CHINESE_CI : 24,
+ GREEK_GENERAL_CI : 25,
+ CP1250_GENERAL_CI : 26,
+ LATIN2_CROATIAN_CI : 27,
+ GBK_CHINESE_CI : 28,
+ CP1257_LITHUANIAN_CI : 29,
+ LATIN5_TURKISH_CI : 30,
+ LATIN1_GERMAN2_CI : 31,
+ ARMSCII8_GENERAL_CI : 32,
+ UTF8_GENERAL_CI : 33,
+ CP1250_CZECH_CS : 34,
+ UCS2_GENERAL_CI : 35,
+ CP866_GENERAL_CI : 36,
+ KEYBCS2_GENERAL_CI : 37,
+ MACCE_GENERAL_CI : 38,
+ MACROMAN_GENERAL_CI : 39,
+ CP852_GENERAL_CI : 40,
+ LATIN7_GENERAL_CI : 41,
+ LATIN7_GENERAL_CS : 42,
+ MACCE_BIN : 43,
+ CP1250_CROATIAN_CI : 44,
+ LATIN1_BIN : 47,
+ LATIN1_GENERAL_CI : 48,
+ LATIN1_GENERAL_CS : 49,
+ CP1251_BIN : 50,
+ CP1251_GENERAL_CI : 51,
+ CP1251_GENERAL_CS : 52,
+ MACROMAN_BIN : 53,
+ CP1256_GENERAL_CI : 57,
+ CP1257_BIN : 58,
+ CP1257_GENERAL_CI : 59,
+ BINARY : 63,
+ ARMSCII8_BIN : 64,
+ ASCII_BIN : 65,
+ CP1250_BIN : 66,
+ CP1256_BIN : 67,
+ CP866_BIN : 68,
+ DEC8_BIN : 69,
+ GREEK_BIN : 70,
+ HEBREW_BIN : 71,
+ HP8_BIN : 72,
+ KEYBCS2_BIN : 73,
+ KOI8R_BIN : 74,
+ KOI8U_BIN : 75,
+ LATIN2_BIN : 77,
+ LATIN5_BIN : 78,
+ LATIN7_BIN : 79,
+ CP850_BIN : 80,
+ CP852_BIN : 81,
+ SWE7_BIN : 82,
+ UTF8_BIN : 83,
+ BIG5_BIN : 84,
+ EUCKR_BIN : 85,
+ GB2312_BIN : 86,
+ GBK_BIN : 87,
+ SJIS_BIN : 88,
+ TIS620_BIN : 89,
+ UCS2_BIN : 90,
+ UJIS_BIN : 91,
+ GEOSTD8_GENERAL_CI : 92,
+ GEOSTD8_BIN : 93,
+ LATIN1_SPANISH_CI : 94,
+ CP932_JAPANESE_CI : 95,
+ CP932_BIN : 96,
+ EUCJPMS_JAPANESE_CI : 97,
+ EUCJPMS_BIN : 98,
+ CP1250_POLISH_CI : 99,
+ UCS2_UNICODE_CI : 128,
+ UCS2_ICELANDIC_CI : 129,
+ UCS2_LATVIAN_CI : 130,
+ UCS2_ROMANIAN_CI : 131,
+ UCS2_SLOVENIAN_CI : 132,
+ UCS2_POLISH_CI : 133,
+ UCS2_ESTONIAN_CI : 134,
+ UCS2_SPANISH_CI : 135,
+ UCS2_SWEDISH_CI : 136,
+ UCS2_TURKISH_CI : 137,
+ UCS2_CZECH_CI : 138,
+ UCS2_DANISH_CI : 139,
+ UCS2_LITHUANIAN_CI : 140,
+ UCS2_SLOVAK_CI : 141,
+ UCS2_SPANISH2_CI : 142,
+ UCS2_ROMAN_CI : 143,
+ UCS2_PERSIAN_CI : 144,
+ UCS2_ESPERANTO_CI : 145,
+ UCS2_HUNGARIAN_CI : 146,
+ UTF8_UNICODE_CI : 192,
+ UTF8_ICELANDIC_CI : 193,
+ UTF8_LATVIAN_CI : 194,
+ UTF8_ROMANIAN_CI : 195,
+ UTF8_SLOVENIAN_CI : 196,
+ UTF8_POLISH_CI : 197,
+ UTF8_ESTONIAN_CI : 198,
+ UTF8_SPANISH_CI : 199,
+ UTF8_SWEDISH_CI : 200,
+ UTF8_TURKISH_CI : 201,
+ UTF8_CZECH_CI : 202,
+ UTF8_DANISH_CI : 203,
+ UTF8_LITHUANIAN_CI : 204,
+ UTF8_SLOVAK_CI : 205,
+ UTF8_SPANISH2_CI : 206,
+ UTF8_ROMAN_CI : 207,
+ UTF8_PERSIAN_CI : 208,
+ UTF8_ESPERANTO_CI : 209,
+ UTF8_HUNGARIAN_CI : 210,
+});
+
+// Error numbers
+// from: http://dev.mysql.com/doc/refman/5.0/en/error-messages-server.html
+hashish.update(exports, {
+ ERROR_HASHCHK : 1000,
+ ERROR_NISAMCHK : 1001,
+ ERROR_NO : 1002,
+ ERROR_YES : 1003,
+ ERROR_CANT_CREATE_FILE : 1004,
+ ERROR_CANT_CREATE_TABLE : 1005,
+ ERROR_CANT_CREATE_DB : 1006,
+ ERROR_DB_CREATE_EXISTS : 1007,
+ ERROR_DB_DROP_EXISTS : 1008,
+ ERROR_DB_DROP_DELETE : 1009,
+ ERROR_DB_DROP_RMDIR : 1010,
+ ERROR_CANT_DELETE_FILE : 1011,
+ ERROR_CANT_FIND_SYSTEM_REC : 1012,
+ ERROR_CANT_GET_STAT : 1013,
+ ERROR_CANT_GET_WD : 1014,
+ ERROR_CANT_LOCK : 1015,
+ ERROR_CANT_OPEN_FILE : 1016,
+ ERROR_FILE_NOT_FOUND : 1017,
+ ERROR_CANT_READ_DIR : 1018,
+ ERROR_CANT_SET_WD : 1019,
+ ERROR_CHECKREAD : 1020,
+ ERROR_DISK_FULL : 1021,
+ ERROR_DUP_KEY : 1022,
+ ERROR_ERROR_ON_CLOSE : 1023,
+ ERROR_ERROR_ON_READ : 1024,
+ ERROR_ERROR_ON_RENAME : 1025,
+ ERROR_ERROR_ON_WRITE : 1026,
+ ERROR_FILE_USED : 1027,
+ ERROR_FILSORT_ABORT : 1028,
+ ERROR_FORM_NOT_FOUND : 1029,
+ ERROR_GET_ERRNO : 1030,
+ ERROR_ILLEGAL_HA : 1031,
+ ERROR_KEY_NOT_FOUND : 1032,
+ ERROR_NOT_FORM_FILE : 1033,
+ ERROR_NOT_KEYFILE : 1034,
+ ERROR_OLD_KEYFILE : 1035,
+ ERROR_OPEN_AS_READONLY : 1036,
+ ERROR_OUTOFMEMORY : 1037,
+ ERROR_OUT_OF_SORTMEMORY : 1038,
+ ERROR_UNEXPECTED_EOF : 1039,
+ ERROR_CON_COUNT_ERROR : 1040,
+ ERROR_OUT_OF_RESOURCES : 1041,
+ ERROR_BAD_HOST_ERROR : 1042,
+ ERROR_HANDSHAKE_ERROR : 1043,
+ ERROR_DBACCESS_DENIED_ERROR : 1044,
+ ERROR_ACCESS_DENIED_ERROR : 1045,
+ ERROR_NO_DB_ERROR : 1046,
+ ERROR_UNKNOWN_COM_ERROR : 1047,
+ ERROR_BAD_NULL_ERROR : 1048,
+ ERROR_BAD_DB_ERROR : 1049,
+ ERROR_TABLE_EXISTS_ERROR : 1050,
+ ERROR_BAD_TABLE_ERROR : 1051,
+ ERROR_NON_UNIQ_ERROR : 1052,
+ ERROR_SERVERROR_SHUTDOWN : 1053,
+ ERROR_BAD_FIELD_ERROR : 1054,
+ ERROR_WRONG_FIELD_WITH_GROUP : 1055,
+ ERROR_WRONG_GROUP_FIELD : 1056,
+ ERROR_WRONG_SUM_SELECT : 1057,
+ ERROR_WRONG_VALUE_COUNT : 1058,
+ ERROR_TOO_LONG_IDENT : 1059,
+ ERROR_DUP_FIELDNAME : 1060,
+ ERROR_DUP_KEYNAME : 1061,
+ ERROR_DUP_ENTRY : 1062,
+ ERROR_WRONG_FIELD_SPEC : 1063,
+ ERROR_PARSE_ERROR : 1064,
+ ERROR_EMPTY_QUERY : 1065,
+ ERROR_NONUNIQ_TABLE : 1066,
+ ERROR_INVALID_DEFAULT : 1067,
+ ERROR_MULTIPLE_PRI_KEY : 1068,
+ ERROR_TOO_MANY_KEYS : 1069,
+ ERROR_TOO_MANY_KEY_PARTS : 1070,
+ ERROR_TOO_LONG_KEY : 1071,
+ ERROR_KEY_COLUMN_DOES_NOT_EXITS : 1072,
+ ERROR_BLOB_USED_AS_KEY : 1073,
+ ERROR_TOO_BIG_FIELDLENGTH : 1074,
+ ERROR_WRONG_AUTO_KEY : 1075,
+ ERROR_READY : 1076,
+ ERROR_NORMAL_SHUTDOWN : 1077,
+ ERROR_GOT_SIGNAL : 1078,
+ ERROR_SHUTDOWN_COMPLETE : 1079,
+ ERROR_FORCING_CLOSE : 1080,
+ ERROR_IPSOCK_ERROR : 1081,
+ ERROR_NO_SUCH_INDEX : 1082,
+ ERROR_WRONG_FIELD_TERMINATORS : 1083,
+ ERROR_BLOBS_AND_NO_TERMINATED : 1084,
+ ERROR_TEXTFILE_NOT_READABLE : 1085,
+ ERROR_FILE_EXISTS_ERROR : 1086,
+ ERROR_LOAD_INFO : 1087,
+ ERROR_ALTERROR_INFO : 1088,
+ ERROR_WRONG_SUB_KEY : 1089,
+ ERROR_CANT_REMOVE_ALL_FIELDS : 1090,
+ ERROR_CANT_DROP_FIELD_OR_KEY : 1091,
+ ERROR_INSERT_INFO : 1092,
+ ERROR_UPDATE_TABLE_USED : 1093,
+ ERROR_NO_SUCH_THREAD : 1094,
+ ERROR_KILL_DENIED_ERROR : 1095,
+ ERROR_NO_TABLES_USED : 1096,
+ ERROR_TOO_BIG_SET : 1097,
+ ERROR_NO_UNIQUE_LOGFILE : 1098,
+ ERROR_TABLE_NOT_LOCKED_FOR_WRITE : 1099,
+ ERROR_TABLE_NOT_LOCKED : 1100,
+ ERROR_BLOB_CANT_HAVE_DEFAULT : 1101,
+ ERROR_WRONG_DB_NAME : 1102,
+ ERROR_WRONG_TABLE_NAME : 1103,
+ ERROR_TOO_BIG_SELECT : 1104,
+ ERROR_UNKNOWN_ERROR : 1105,
+ ERROR_UNKNOWN_PROCEDURE : 1106,
+ ERROR_WRONG_PARAMCOUNT_TO_PROCEDURE : 1107,
+ ERROR_WRONG_PARAMETERS_TO_PROCEDURE : 1108,
+ ERROR_UNKNOWN_TABLE : 1109,
+ ERROR_FIELD_SPECIFIED_TWICE : 1110,
+ ERROR_INVALID_GROUP_FUNC_USE : 1111,
+ ERROR_UNSUPPORTED_EXTENSION : 1112,
+ ERROR_TABLE_MUST_HAVE_COLUMNS : 1113,
+ ERROR_RECORD_FILE_FULL : 1114,
+ ERROR_UNKNOWN_CHARACTERROR_SET : 1115,
+ ERROR_TOO_MANY_TABLES : 1116,
+ ERROR_TOO_MANY_FIELDS : 1117,
+ ERROR_TOO_BIG_ROWSIZE : 1118,
+ ERROR_STACK_OVERRUN : 1119,
+ ERROR_WRONG_OUTERROR_JOIN : 1120,
+ ERROR_NULL_COLUMN_IN_INDEX : 1121,
+ ERROR_CANT_FIND_UDF : 1122,
+ ERROR_CANT_INITIALIZE_UDF : 1123,
+ ERROR_UDF_NO_PATHS : 1124,
+ ERROR_UDF_EXISTS : 1125,
+ ERROR_CANT_OPEN_LIBRARY : 1126,
+ ERROR_CANT_FIND_DL_ENTRY : 1127,
+ ERROR_FUNCTION_NOT_DEFINED : 1128,
+ ERROR_HOST_IS_BLOCKED : 1129,
+ ERROR_HOST_NOT_PRIVILEGED : 1130,
+ ERROR_PASSWORD_ANONYMOUS_USER : 1131,
+ ERROR_PASSWORD_NOT_ALLOWED : 1132,
+ ERROR_PASSWORD_NO_MATCH : 1133,
+ ERROR_UPDATE_INFO : 1134,
+ ERROR_CANT_CREATE_THREAD : 1135,
+ ERROR_WRONG_VALUE_COUNT_ON_ROW : 1136,
+ ERROR_CANT_REOPEN_TABLE : 1137,
+ ERROR_INVALID_USE_OF_NULL : 1138,
+ ERROR_REGEXP_ERROR : 1139,
+ ERROR_MIX_OF_GROUP_FUNC_AND_FIELDS : 1140,
+ ERROR_NONEXISTING_GRANT : 1141,
+ ERROR_TABLEACCESS_DENIED_ERROR : 1142,
+ ERROR_COLUMNACCESS_DENIED_ERROR : 1143,
+ ERROR_ILLEGAL_GRANT_FOR_TABLE : 1144,
+ ERROR_GRANT_WRONG_HOST_OR_USER : 1145,
+ ERROR_NO_SUCH_TABLE : 1146,
+ ERROR_NONEXISTING_TABLE_GRANT : 1147,
+ ERROR_NOT_ALLOWED_COMMAND : 1148,
+ ERROR_SYNTAX_ERROR : 1149,
+ ERROR_DELAYED_CANT_CHANGE_LOCK : 1150,
+ ERROR_TOO_MANY_DELAYED_THREADS : 1151,
+ ERROR_ABORTING_CONNECTION : 1152,
+ ERROR_NET_PACKET_TOO_LARGE : 1153,
+ ERROR_NET_READ_ERROR_FROM_PIPE : 1154,
+ ERROR_NET_FCNTL_ERROR : 1155,
+ ERROR_NET_PACKETS_OUT_OF_ORDER : 1156,
+ ERROR_NET_UNCOMPRESS_ERROR : 1157,
+ ERROR_NET_READ_ERROR : 1158,
+ ERROR_NET_READ_INTERRUPTED : 1159,
+ ERROR_NET_ERROR_ON_WRITE : 1160,
+ ERROR_NET_WRITE_INTERRUPTED : 1161,
+ ERROR_TOO_LONG_STRING : 1162,
+ ERROR_TABLE_CANT_HANDLE_BLOB : 1163,
+ ERROR_TABLE_CANT_HANDLE_AUTO_INCREMENT : 1164,
+ ERROR_DELAYED_INSERT_TABLE_LOCKED : 1165,
+ ERROR_WRONG_COLUMN_NAME : 1166,
+ ERROR_WRONG_KEY_COLUMN : 1167,
+ ERROR_WRONG_MRG_TABLE : 1168,
+ ERROR_DUP_UNIQUE : 1169,
+ ERROR_BLOB_KEY_WITHOUT_LENGTH : 1170,
+ ERROR_PRIMARY_CANT_HAVE_NULL : 1171,
+ ERROR_TOO_MANY_ROWS : 1172,
+ ERROR_REQUIRES_PRIMARY_KEY : 1173,
+ ERROR_NO_RAID_COMPILED : 1174,
+ ERROR_UPDATE_WITHOUT_KEY_IN_SAFE_MODE : 1175,
+ ERROR_KEY_DOES_NOT_EXITS : 1176,
+ ERROR_CHECK_NO_SUCH_TABLE : 1177,
+ ERROR_CHECK_NOT_IMPLEMENTED : 1178,
+ ERROR_CANT_DO_THIS_DURING_AN_TRANSACTION : 1179,
+ ERROR_ERROR_DURING_COMMIT : 1180,
+ ERROR_ERROR_DURING_ROLLBACK : 1181,
+ ERROR_ERROR_DURING_FLUSH_LOGS : 1182,
+ ERROR_ERROR_DURING_CHECKPOINT : 1183,
+ ERROR_NEW_ABORTING_CONNECTION : 1184,
+ ERROR_DUMP_NOT_IMPLEMENTED : 1185,
+ ERROR_FLUSH_MASTERROR_BINLOG_CLOSED : 1186,
+ ERROR_INDEX_REBUILD : 1187,
+ ERROR_MASTER : 1188,
+ ERROR_MASTERROR_NET_READ : 1189,
+ ERROR_MASTERROR_NET_WRITE : 1190,
+ ERROR_FT_MATCHING_KEY_NOT_FOUND : 1191,
+ ERROR_LOCK_OR_ACTIVE_TRANSACTION : 1192,
+ ERROR_UNKNOWN_SYSTEM_VARIABLE : 1193,
+ ERROR_CRASHED_ON_USAGE : 1194,
+ ERROR_CRASHED_ON_REPAIR : 1195,
+ ERROR_WARNING_NOT_COMPLETE_ROLLBACK : 1196,
+ ERROR_TRANS_CACHE_FULL : 1197,
+ ERROR_SLAVE_MUST_STOP : 1198,
+ ERROR_SLAVE_NOT_RUNNING : 1199,
+ ERROR_BAD_SLAVE : 1200,
+ ERROR_MASTERROR_INFO : 1201,
+ ERROR_SLAVE_THREAD : 1202,
+ ERROR_TOO_MANY_USERROR_CONNECTIONS : 1203,
+ ERROR_SET_CONSTANTS_ONLY : 1204,
+ ERROR_LOCK_WAIT_TIMEOUT : 1205,
+ ERROR_LOCK_TABLE_FULL : 1206,
+ ERROR_READ_ONLY_TRANSACTION : 1207,
+ ERROR_DROP_DB_WITH_READ_LOCK : 1208,
+ ERROR_CREATE_DB_WITH_READ_LOCK : 1209,
+ ERROR_WRONG_ARGUMENTS : 1210,
+ ERROR_NO_PERMISSION_TO_CREATE_USER : 1211,
+ ERROR_UNION_TABLES_IN_DIFFERENT_DIR : 1212,
+ ERROR_LOCK_DEADLOCK : 1213,
+ ERROR_TABLE_CANT_HANDLE_FT : 1214,
+ ERROR_CANNOT_ADD_FOREIGN : 1215,
+ ERROR_NO_REFERENCED_ROW : 1216,
+ ERROR_ROW_IS_REFERENCED : 1217,
+ ERROR_CONNECT_TO_MASTER : 1218,
+ ERROR_QUERY_ON_MASTER : 1219,
+ ERROR_ERROR_WHEN_EXECUTING_COMMAND : 1220,
+ ERROR_WRONG_USAGE : 1221,
+ ERROR_WRONG_NUMBERROR_OF_COLUMNS_IN_SELECT : 1222,
+ ERROR_CANT_UPDATE_WITH_READLOCK : 1223,
+ ERROR_MIXING_NOT_ALLOWED : 1224,
+ ERROR_DUP_ARGUMENT : 1225,
+ ERROR_USERROR_LIMIT_REACHED : 1226,
+ ERROR_SPECIFIC_ACCESS_DENIED_ERROR : 1227,
+ ERROR_LOCAL_VARIABLE : 1228,
+ ERROR_GLOBAL_VARIABLE : 1229,
+ ERROR_NO_DEFAULT : 1230,
+ ERROR_WRONG_VALUE_FOR_VAR : 1231,
+ ERROR_WRONG_TYPE_FOR_VAR : 1232,
+ ERROR_VAR_CANT_BE_READ : 1233,
+ ERROR_CANT_USE_OPTION_HERE : 1234,
+ ERROR_NOT_SUPPORTED_YET : 1235,
+ ERROR_MASTERROR_FATAL_ERROR_READING_BINLOG : 1236,
+ ERROR_SLAVE_IGNORED_TABLE : 1237,
+ ERROR_INCORRECT_GLOBAL_LOCAL_VAR : 1238,
+ ERROR_WRONG_FK_DEF : 1239,
+ ERROR_KEY_REF_DO_NOT_MATCH_TABLE_REF : 1240,
+ ERROR_OPERAND_COLUMNS : 1241,
+ ERROR_SUBQUERY_NO_1_ROW : 1242,
+ ERROR_UNKNOWN_STMT_HANDLER : 1243,
+ ERROR_CORRUPT_HELP_DB : 1244,
+ ERROR_CYCLIC_REFERENCE : 1245,
+ ERROR_AUTO_CONVERT : 1246,
+ ERROR_ILLEGAL_REFERENCE : 1247,
+ ERROR_DERIVED_MUST_HAVE_ALIAS : 1248,
+ ERROR_SELECT_REDUCED : 1249,
+ ERROR_TABLENAME_NOT_ALLOWED_HERE : 1250,
+ ERROR_NOT_SUPPORTED_AUTH_MODE : 1251,
+ ERROR_SPATIAL_CANT_HAVE_NULL : 1252,
+ ERROR_COLLATION_CHARSET_MISMATCH : 1253,
+ ERROR_SLAVE_WAS_RUNNING : 1254,
+ ERROR_SLAVE_WAS_NOT_RUNNING : 1255,
+ ERROR_TOO_BIG_FOR_UNCOMPRESS : 1256,
+ ERROR_ZLIB_Z_MEM_ERROR : 1257,
+ ERROR_ZLIB_Z_BUF_ERROR : 1258,
+ ERROR_ZLIB_Z_DATA_ERROR : 1259,
+ ERROR_CUT_VALUE_GROUP_CONCAT : 1260,
+ ERROR_WARN_TOO_FEW_RECORDS : 1261,
+ ERROR_WARN_TOO_MANY_RECORDS : 1262,
+ ERROR_WARN_NULL_TO_NOTNULL : 1263,
+ ERROR_WARN_DATA_OUT_OF_RANGE : 1264,
+ WARN_DATA_TRUNCATED : 1265,
+ ERROR_WARN_USING_OTHERROR_HANDLER : 1266,
+ ERROR_CANT_AGGREGATE_2COLLATIONS : 1267,
+ ERROR_DROP_USER : 1268,
+ ERROR_REVOKE_GRANTS : 1269,
+ ERROR_CANT_AGGREGATE_3COLLATIONS : 1270,
+ ERROR_CANT_AGGREGATE_NCOLLATIONS : 1271,
+ ERROR_VARIABLE_IS_NOT_STRUCT : 1272,
+ ERROR_UNKNOWN_COLLATION : 1273,
+ ERROR_SLAVE_IGNORED_SSL_PARAMS : 1274,
+ ERROR_SERVERROR_IS_IN_SECURE_AUTH_MODE : 1275,
+ ERROR_WARN_FIELD_RESOLVED : 1276,
+ ERROR_BAD_SLAVE_UNTIL_COND : 1277,
+ ERROR_MISSING_SKIP_SLAVE : 1278,
+ ERROR_UNTIL_COND_IGNORED : 1279,
+ ERROR_WRONG_NAME_FOR_INDEX : 1280,
+ ERROR_WRONG_NAME_FOR_CATALOG : 1281,
+ ERROR_WARN_QC_RESIZE : 1282,
+ ERROR_BAD_FT_COLUMN : 1283,
+ ERROR_UNKNOWN_KEY_CACHE : 1284,
+ ERROR_WARN_HOSTNAME_WONT_WORK : 1285,
+ ERROR_UNKNOWN_STORAGE_ENGINE : 1286,
+ ERROR_WARN_DEPRECATED_SYNTAX : 1287,
+ ERROR_NON_UPDATABLE_TABLE : 1288,
+ ERROR_FEATURE_DISABLED : 1289,
+ ERROR_OPTION_PREVENTS_STATEMENT : 1290,
+ ERROR_DUPLICATED_VALUE_IN_TYPE : 1291,
+ ERROR_TRUNCATED_WRONG_VALUE : 1292,
+ ERROR_TOO_MUCH_AUTO_TIMESTAMP_COLS : 1293,
+ ERROR_INVALID_ON_UPDATE : 1294,
+ ERROR_UNSUPPORTED_PS : 1295,
+ ERROR_GET_ERRMSG : 1296,
+ ERROR_GET_TEMPORARY_ERRMSG : 1297,
+ ERROR_UNKNOWN_TIME_ZONE : 1298,
+ ERROR_WARN_INVALID_TIMESTAMP : 1299,
+ ERROR_INVALID_CHARACTERROR_STRING : 1300,
+ ERROR_WARN_ALLOWED_PACKET_OVERFLOWED : 1301,
+ ERROR_CONFLICTING_DECLARATIONS : 1302,
+ ERROR_SP_NO_RECURSIVE_CREATE : 1303,
+ ERROR_SP_ALREADY_EXISTS : 1304,
+ ERROR_SP_DOES_NOT_EXIST : 1305,
+ ERROR_SP_DROP_FAILED : 1306,
+ ERROR_SP_STORE_FAILED : 1307,
+ ERROR_SP_LILABEL_MISMATCH : 1308,
+ ERROR_SP_LABEL_REDEFINE : 1309,
+ ERROR_SP_LABEL_MISMATCH : 1310,
+ ERROR_SP_UNINIT_VAR : 1311,
+ ERROR_SP_BADSELECT : 1312,
+ ERROR_SP_BADRETURN : 1313,
+ ERROR_SP_BADSTATEMENT : 1314,
+ ERROR_UPDATE_LOG_DEPRECATED_IGNORED : 1315,
+ ERROR_UPDATE_LOG_DEPRECATED_TRANSLATED : 1316,
+ ERROR_QUERY_INTERRUPTED : 1317,
+ ERROR_SP_WRONG_NO_OF_ARGS : 1318,
+ ERROR_SP_COND_MISMATCH : 1319,
+ ERROR_SP_NORETURN : 1320,
+ ERROR_SP_NORETURNEND : 1321,
+ ERROR_SP_BAD_CURSOR_QUERY : 1322,
+ ERROR_SP_BAD_CURSOR_SELECT : 1323,
+ ERROR_SP_CURSOR_MISMATCH : 1324,
+ ERROR_SP_CURSOR_ALREADY_OPEN : 1325,
+ ERROR_SP_CURSOR_NOT_OPEN : 1326,
+ ERROR_SP_UNDECLARED_VAR : 1327,
+ ERROR_SP_WRONG_NO_OF_FETCH_ARGS : 1328,
+ ERROR_SP_FETCH_NO_DATA : 1329,
+ ERROR_SP_DUP_PARAM : 1330,
+ ERROR_SP_DUP_VAR : 1331,
+ ERROR_SP_DUP_COND : 1332,
+ ERROR_SP_DUP_CURS : 1333,
+ ERROR_SP_CANT_ALTER : 1334,
+ ERROR_SP_SUBSELECT_NYI : 1335,
+ ERROR_STMT_NOT_ALLOWED_IN_SF_OR_TRG : 1336,
+ ERROR_SP_VARCOND_AFTERROR_CURSHNDLR : 1337,
+ ERROR_SP_CURSOR_AFTERROR_HANDLER : 1338,
+ ERROR_SP_CASE_NOT_FOUND : 1339,
+ ERROR_FPARSERROR_TOO_BIG_FILE : 1340,
+ ERROR_FPARSERROR_BAD_HEADER : 1341,
+ ERROR_FPARSERROR_EOF_IN_COMMENT : 1342,
+ ERROR_FPARSERROR_ERROR_IN_PARAMETER : 1343,
+ ERROR_FPARSERROR_EOF_IN_UNKNOWN_PARAMETER : 1344,
+ ERROR_VIEW_NO_EXPLAIN : 1345,
+ ERROR_FRM_UNKNOWN_TYPE : 1346,
+ ERROR_WRONG_OBJECT : 1347,
+ ERROR_NONUPDATEABLE_COLUMN : 1348,
+ ERROR_VIEW_SELECT_DERIVED : 1349,
+ ERROR_VIEW_SELECT_CLAUSE : 1350,
+ ERROR_VIEW_SELECT_VARIABLE : 1351,
+ ERROR_VIEW_SELECT_TMPTABLE : 1352,
+ ERROR_VIEW_WRONG_LIST : 1353,
+ ERROR_WARN_VIEW_MERGE : 1354,
+ ERROR_WARN_VIEW_WITHOUT_KEY : 1355,
+ ERROR_VIEW_INVALID : 1356,
+ ERROR_SP_NO_DROP_SP : 1357,
+ ERROR_SP_GOTO_IN_HNDLR : 1358,
+ ERROR_TRG_ALREADY_EXISTS : 1359,
+ ERROR_TRG_DOES_NOT_EXIST : 1360,
+ ERROR_TRG_ON_VIEW_OR_TEMP_TABLE : 1361,
+ ERROR_TRG_CANT_CHANGE_ROW : 1362,
+ ERROR_TRG_NO_SUCH_ROW_IN_TRG : 1363,
+ ERROR_NO_DEFAULT_FOR_FIELD : 1364,
+ ERROR_DIVISION_BY_ZERO : 1365,
+ ERROR_TRUNCATED_WRONG_VALUE_FOR_FIELD : 1366,
+ ERROR_ILLEGAL_VALUE_FOR_TYPE : 1367,
+ ERROR_VIEW_NONUPD_CHECK : 1368,
+ ERROR_VIEW_CHECK_FAILED : 1369,
+ ERROR_PROCACCESS_DENIED_ERROR : 1370,
+ ERROR_RELAY_LOG_FAIL : 1371,
+ ERROR_PASSWD_LENGTH : 1372,
+ ERROR_UNKNOWN_TARGET_BINLOG : 1373,
+ ERROR_IO_ERR_LOG_INDEX_READ : 1374,
+ ERROR_BINLOG_PURGE_PROHIBITED : 1375,
+ ERROR_FSEEK_FAIL : 1376,
+ ERROR_BINLOG_PURGE_FATAL_ERR : 1377,
+ ERROR_LOG_IN_USE : 1378,
+ ERROR_LOG_PURGE_UNKNOWN_ERR : 1379,
+ ERROR_RELAY_LOG_INIT : 1380,
+ ERROR_NO_BINARY_LOGGING : 1381,
+ ERROR_RESERVED_SYNTAX : 1382,
+ ERROR_WSAS_FAILED : 1383,
+ ERROR_DIFF_GROUPS_PROC : 1384,
+ ERROR_NO_GROUP_FOR_PROC : 1385,
+ ERROR_ORDERROR_WITH_PROC : 1386,
+ ERROR_LOGGING_PROHIBIT_CHANGING_OF : 1387,
+ ERROR_NO_FILE_MAPPING : 1388,
+ ERROR_WRONG_MAGIC : 1389,
+ ERROR_PS_MANY_PARAM : 1390,
+ ERROR_KEY_PART_0 : 1391,
+ ERROR_VIEW_CHECKSUM : 1392,
+ ERROR_VIEW_MULTIUPDATE : 1393,
+ ERROR_VIEW_NO_INSERT_FIELD_LIST : 1394,
+ ERROR_VIEW_DELETE_MERGE_VIEW : 1395,
+ ERROR_CANNOT_USER : 1396,
+ ERROR_XAERROR_NOTA : 1397,
+ ERROR_XAERROR_INVAL : 1398,
+ ERROR_XAERROR_RMFAIL : 1399,
+ ERROR_XAERROR_OUTSIDE : 1400,
+ ERROR_XAERROR_RMERR : 1401,
+ ERROR_XA_RBROLLBACK : 1402,
+ ERROR_NONEXISTING_PROC_GRANT : 1403,
+ ERROR_PROC_AUTO_GRANT_FAIL : 1404,
+ ERROR_PROC_AUTO_REVOKE_FAIL : 1405,
+ ERROR_DATA_TOO_LONG : 1406,
+ ERROR_SP_BAD_SQLSTATE : 1407,
+ ERROR_STARTUP : 1408,
+ ERROR_LOAD_FROM_FIXED_SIZE_ROWS_TO_VAR : 1409,
+ ERROR_CANT_CREATE_USERROR_WITH_GRANT : 1410,
+ ERROR_WRONG_VALUE_FOR_TYPE : 1411,
+ ERROR_TABLE_DEF_CHANGED : 1412,
+ ERROR_SP_DUP_HANDLER : 1413,
+ ERROR_SP_NOT_VAR_ARG : 1414,
+ ERROR_SP_NO_RETSET : 1415,
+ ERROR_CANT_CREATE_GEOMETRY_OBJECT : 1416,
+ ERROR_FAILED_ROUTINE_BREAK_BINLOG : 1417,
+ ERROR_BINLOG_UNSAFE_ROUTINE : 1418,
+ ERROR_BINLOG_CREATE_ROUTINE_NEED_SUPER : 1419,
+ ERROR_EXEC_STMT_WITH_OPEN_CURSOR : 1420,
+ ERROR_STMT_HAS_NO_OPEN_CURSOR : 1421,
+ ERROR_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG : 1422,
+ ERROR_NO_DEFAULT_FOR_VIEW_FIELD : 1423,
+ ERROR_SP_NO_RECURSION : 1424,
+ ERROR_TOO_BIG_SCALE : 1425,
+ ERROR_TOO_BIG_PRECISION : 1426,
+ ERROR_M_BIGGERROR_THAN_D : 1427,
+ ERROR_WRONG_LOCK_OF_SYSTEM_TABLE : 1428,
+ ERROR_CONNECT_TO_FOREIGN_DATA_SOURCE : 1429,
+ ERROR_QUERY_ON_FOREIGN_DATA_SOURCE : 1430,
+ ERROR_FOREIGN_DATA_SOURCE_DOESNT_EXIST : 1431,
+ ERROR_FOREIGN_DATA_STRING_INVALID_CANT_CREATE : 1432,
+ ERROR_FOREIGN_DATA_STRING_INVALID : 1433,
+ ERROR_CANT_CREATE_FEDERATED_TABLE : 1434,
+ ERROR_TRG_IN_WRONG_SCHEMA : 1435,
+ ERROR_STACK_OVERRUN_NEED_MORE : 1436,
+ ERROR_TOO_LONG_BODY : 1437,
+ ERROR_WARN_CANT_DROP_DEFAULT_KEYCACHE : 1438,
+ ERROR_TOO_BIG_DISPLAYWIDTH : 1439,
+ ERROR_XAERROR_DUPID : 1440,
+ ERROR_DATETIME_FUNCTION_OVERFLOW : 1441,
+ ERROR_CANT_UPDATE_USED_TABLE_IN_SF_OR_TRG : 1442,
+ ERROR_VIEW_PREVENT_UPDATE : 1443,
+ ERROR_PS_NO_RECURSION : 1444,
+ ERROR_SP_CANT_SET_AUTOCOMMIT : 1445,
+ ERROR_MALFORMED_DEFINER : 1446,
+ ERROR_VIEW_FRM_NO_USER : 1447,
+ ERROR_VIEW_OTHERROR_USER : 1448,
+ ERROR_NO_SUCH_USER : 1449,
+ ERROR_FORBID_SCHEMA_CHANGE : 1450,
+ ERROR_ROW_IS_REFERENCED_2 : 1451,
+ ERROR_NO_REFERENCED_ROW_2 : 1452,
+ ERROR_SP_BAD_VAR_SHADOW : 1453,
+ ERROR_TRG_NO_DEFINER : 1454,
+ ERROR_OLD_FILE_FORMAT : 1455,
+ ERROR_SP_RECURSION_LIMIT : 1456,
+ ERROR_SP_PROC_TABLE_CORRUPT : 1457,
+ ERROR_SP_WRONG_NAME : 1458,
+ ERROR_TABLE_NEEDS_UPGRADE : 1459,
+ ERROR_SP_NO_AGGREGATE : 1460,
+ ERROR_MAX_PREPARED_STMT_COUNT_REACHED : 1461,
+ ERROR_VIEW_RECURSIVE : 1462,
+ ERROR_NON_GROUPING_FIELD_USED : 1463,
+ ERROR_TABLE_CANT_HANDLE_SPKEYS : 1464,
+ ERROR_NO_TRIGGERS_ON_SYSTEM_SCHEMA : 1465,
+ ERROR_REMOVED_SPACES : 1466,
+ ERROR_AUTOINC_READ_FAILED : 1467,
+ ERROR_USERNAME : 1468,
+ ERROR_HOSTNAME : 1469,
+ ERROR_WRONG_STRING_LENGTH : 1470,
+ ERROR_NON_INSERTABLE_TABLE : 1471,
+ ERROR_ADMIN_WRONG_MRG_TABLE : 1472,
+ ERROR_TOO_HIGH_LEVEL_OF_NESTING_FOR_SELECT : 1473,
+ ERROR_NAME_BECOMES_EMPTY : 1474,
+ ERROR_AMBIGUOUS_FIELD_TERM : 1475,
+ ERROR_LOAD_DATA_INVALID_COLUMN : 1476,
+ ERROR_LOG_PURGE_NO_FILE : 1477,
+ ERROR_XA_RBTIMEOUT : 1478,
+ ERROR_XA_RBDEADLOCK : 1479,
+ ERROR_TOO_MANY_CONCURRENT_TRXS : 1480,
+});
diff --git a/lib/mysql.js b/lib/mysql.js
new file mode 100644
index 0000000..91ff2b7
--- /dev/null
+++ b/lib/mysql.js
@@ -0,0 +1,18 @@
+var mysql = exports;
+var hashish = require('hashish');
+var Client = exports.Client = require('./client');
+var constants = require('./constants');
+var fs = require('fs');
+
+mysql.PACKAGE = (function() {
+ var json = fs.readFileSync(__dirname + '/../package.json', 'utf8');
+ return JSON.parse(json);
+})();
+
+mysql.createClient = function(config) {
+ var client = new Client();
+ hashish.update(client, config || {});
+ return client;
+};
+
+hashish.update(exports, constants);
diff --git a/lib/outgoing_packet.js b/lib/outgoing_packet.js
new file mode 100644
index 0000000..b46a7d4
--- /dev/null
+++ b/lib/outgoing_packet.js
@@ -0,0 +1,79 @@
+var Buffer = require('buffer').Buffer;
+
+function OutgoingPacket(size, num) {
+ this.buffer = new Buffer(size + 3 + 1);
+ this.index = 0;
+ this.writeNumber(3, size);
+ this.writeNumber(1, num || 0);
+};
+module.exports = OutgoingPacket;
+
+OutgoingPacket.prototype.writeNumber = function(bytes, number) {
+ for (var i = 0; i < bytes; i++) {
+ this.buffer[this.index++] = (number >> (i * 8)) & 0xff;
+ }
+};
+
+OutgoingPacket.prototype.writeFiller = function(bytes) {
+ for (var i = 0; i < bytes; i++) {
+ this.buffer[this.index++] = 0;
+ }
+};
+
+OutgoingPacket.prototype.write = function(bufferOrString, encoding) {
+ if (typeof bufferOrString == 'string') {
+ this.index += this.buffer.write(bufferOrString, this.index, encoding);
+ return;
+ }
+
+ bufferOrString.copy(this.buffer, this.index, 0);
+ this.index += bufferOrString.length;
+};
+
+OutgoingPacket.prototype.writeNullTerminated = function(bufferOrString, encoding) {
+ this.write(bufferOrString, encoding);
+ this.buffer[this.index++] = 0;
+};
+
+OutgoingPacket.prototype.writeLengthCoded = function(bufferOrStringOrNumber, encoding) {
+ if (bufferOrStringOrNumber === null) {
+ this.buffer[this.index++] = 251;
+ return;
+ }
+
+ if (typeof bufferOrStringOrNumber == 'number') {
+ if (bufferOrStringOrNumber <= 250) {
+ this.buffer[this.index++] = bufferOrStringOrNumber;
+ return;
+ }
+
+ // @todo support 8-byte numbers and simplify this
+ if (bufferOrStringOrNumber < 0xffff) {
+ this.buffer[this.index++] = 252;
+ this.buffer[this.index++] = (bufferOrStringOrNumber >> 0) & 0xff;
+ this.buffer[this.index++] = (bufferOrStringOrNumber >> 8) & 0xff;
+ } else if (bufferOrStringOrNumber < 0xffffff) {
+ this.buffer[this.index++] = 253;
+ this.buffer[this.index++] = (bufferOrStringOrNumber >> 0) & 0xff;
+ this.buffer[this.index++] = (bufferOrStringOrNumber >> 8) & 0xff;
+ this.buffer[this.index++] = (bufferOrStringOrNumber >> 16) & 0xff;
+ } else {
+ throw new Error('8 byte length coded numbers not supported yet');
+ }
+ return;
+ }
+
+ if (bufferOrStringOrNumber instanceof Buffer) {
+ this.writeLengthCoded(bufferOrStringOrNumber.length);
+ this.write(bufferOrStringOrNumber);
+ return;
+ }
+
+ if (typeof bufferOrStringOrNumber == 'string') {
+ this.writeLengthCoded(Buffer.byteLength(bufferOrStringOrNumber, encoding));
+ this.write(bufferOrStringOrNumber, encoding);
+ return;
+ }
+
+ throw new Error('passed argument not a buffer, string or number: '+bufferOrStringOrNumber);
+};
diff --git a/lib/parser.js b/lib/parser.js
new file mode 100644
index 0000000..c36d3ea
--- /dev/null
+++ b/lib/parser.js
@@ -0,0 +1,650 @@
+var util = require('util');
+var Buffer = require('buffer').Buffer;
+var EventEmitter = require('events').EventEmitter;
+var POWS = [1, 256, 65536, 16777216];
+
+function Parser() {
+ EventEmitter.call(this);
+
+ this.state = Parser.PACKET_LENGTH;
+ this.packet = null;
+ this.greeted = false;
+ this.authenticated = false;
+ this.receivingFieldPackets = false;
+ this.receivingRowPackets = false;
+
+ this._lengthCodedLength = null;
+ this._lengthCodedStringLength = null;
+};
+util.inherits(Parser, EventEmitter);
+module.exports = Parser;
+
+Parser.prototype.write = function(buffer) {
+ var i = 0,
+ c = null,
+ self = this,
+ state = this.state,
+ length = buffer.length,
+ packet = this.packet,
+ advance = function(newState) {
+ self.state = state = (newState === undefined)
+ ? self.state + 1
+ : newState;
+
+ packet.index = -1;
+ },
+ lengthCoded = function(val, nextState) {
+ if (self._lengthCodedLength === null) {
+ if (c === Parser.LENGTH_CODED_16BIT_WORD) {
+ self._lengthCodedLength = 2;
+ } else if (c === Parser.LENGTH_CODED_24BIT_WORD) {
+ self._lengthCodedLength = 3;
+ } else if (c === Parser.LENGTH_CODED_64BIT_WORD) {
+ self._lengthCodedLength = 8;
+ } else if (c === Parser.LENGTH_CODED_NULL) {
+ advance(nextState);
+ return null;
+ } else if (c < Parser.LENGTH_CODED_NULL) {
+ advance(nextState);
+ return c;
+ }
+
+ return 0;
+ }
+
+ if (c) {
+ val += POWS[packet.index - 1] * c;
+ }
+
+ if (packet.index === self._lengthCodedLength) {
+ self._lengthCodedLength = null;
+ advance(nextState);
+ }
+
+ return val;
+ },
+ emitPacket = function() {
+ self.packet = null;
+ self.state = state = Parser.PACKET_LENGTH;
+ self.greeted = true;
+ delete packet.index;
+ self.emit('packet', packet);
+ packet = null;
+ };
+
+ for (; i < length; i++) {
+ c = buffer[i];
+
+ if (state > Parser.PACKET_NUMBER) {
+ packet.received++;
+ }
+
+ switch (state) {
+ // PACKET HEADER
+ case 0: // PACKET_LENGTH:
+ if (!packet) {
+ packet = this.packet = new EventEmitter();
+ packet.index = 0;
+ packet.length = 0;
+ packet.received = 0;
+ packet.number = 0;
+ }
+
+ // 3 bytes - Little endian
+ packet.length += POWS[packet.index] * c;
+
+ if (packet.index == 2) {
+ advance();
+ }
+ break;
+ case 1: // PACKET_NUMBER:
+ // 1 byte
+ packet.number = c;
+
+ if (!this.greeted) {
+ advance(Parser.GREETING_PROTOCOL_VERSION);
+ break;
+ }
+
+ if (this.receivingFieldPackets) {
+ advance(Parser.FIELD_CATALOG_LENGTH);
+ } else if (this.receivingRowPackets) {
+ advance(Parser.COLUMN_VALUE_LENGTH);
+ } else {
+ advance(Parser.FIELD_COUNT);
+ }
+ break;
+
+ // GREETING_PACKET
+ 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) {
+ packet.type = Parser.ERROR_PACKET;
+ advance(Parser.ERROR_NUMBER);
+ break;
+ }
+
+ // 1 byte
+ packet.type = Parser.GREETING_PACKET;
+ packet.protocolVersion = c;
+ advance();
+ break;
+ case 3: // GREETING_SERVER_VERSION:
+ if (packet.index == 0) {
+ packet.serverVersion = '';
+ }
+
+ // Null-Terminated String
+ if (c != 0) {
+ packet.serverVersion += String.fromCharCode(c);
+ } else {
+ advance();
+ }
+ break;
+ case 4: // GREETING_THREAD_ID:
+ if (packet.index == 0) {
+ packet.threadId = 0;
+ }
+
+ // 4 bytes = probably Little endian, protocol docs are not clear
+ packet.threadId += POWS[packet.index] * c;
+
+ if (packet.index == 3) {
+ advance();
+ }
+ break;
+ case 5: // GREETING_SCRAMBLE_BUFF_1:
+ if (packet.index == 0) {
+ packet.scrambleBuffer = new Buffer(8 + 12);
+ }
+
+ // 8 bytes
+ packet.scrambleBuffer[packet.index] = c;
+
+ if (packet.index == 7) {
+ advance();
+ }
+ break;
+ case 6: // GREETING_FILLER_1:
+ // 1 byte - 0x00
+ advance();
+ break;
+ case 7: // GREETING_SERVER_CAPABILITIES:
+ if (packet.index == 0) {
+ packet.serverCapabilities = 0;
+ }
+ // 2 bytes = probably Little endian, protocol docs are not clear
+ packet.serverCapabilities += POWS[packet.index] * c;
+
+ if (packet.index == 1) {
+ advance();
+ }
+ break;
+ case 8: // GREETING_SERVER_LANGUAGE:
+ packet.serverLanguage = c;
+ advance();
+ break;
+ case 9: // GREETING_SERVER_STATUS:
+ if (packet.index == 0) {
+ packet.serverStatus = 0;
+ }
+
+ // 2 bytes = probably Little endian, protocol docs are not clear
+ packet.serverStatus += POWS[packet.index] * c;
+
+ if (packet.index == 1) {
+ advance();
+ }
+ break;
+ case 10: // GREETING_FILLER_2:
+ // 13 bytes - 0x00
+ if (packet.index == 12) {
+ advance();
+ }
+ break;
+ 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;
+ }
+ break;
+
+ // OK_PACKET, ERROR_PACKET, or RESULT_SET_HEADER_PACKET
+ case 12: // FIELD_COUNT:
+ if (packet.index == 0) {
+ if (c === 0xff) {
+ packet.type = Parser.ERROR_PACKET;
+ advance(Parser.ERROR_NUMBER);
+ break;
+ }
+
+ if (c == 0xfe && !this.authenticated) {
+ packet.type = Parser.USE_OLD_PASSWORD_PROTOCOL_PACKET;
+ break;
+ }
+
+ if (c === 0x00) {
+ // after the first OK PACKET, we are authenticated
+ this.authenticated = true;
+ packet.type = Parser.OK_PACKET;
+ advance(Parser.AFFECTED_ROWS);
+ break;
+ }
+ }
+
+ this.receivingFieldPackets = true;
+ packet.type = Parser.RESULT_SET_HEADER_PACKET;
+ packet.fieldCount = lengthCoded(packet.fieldCount, Parser.EXTRA_LENGTH);
+
+ break;
+
+ // ERROR_PACKET
+ case 13: // ERROR_NUMBER:
+ if (packet.index == 0) {
+ packet.errorNumber = 0;
+ }
+
+ // 2 bytes = Little endian
+ packet.errorNumber += POWS[packet.index] * c;
+
+ if (packet.index == 1) {
+ if (!this.greeted) {
+ // Turns out error packets are confirming to the 4.0 protocol when
+ // not greeted yet. Oh MySql, you are such a thing of beauty ...
+ advance(Parser.ERROR_MESSAGE);
+ break;
+ }
+
+ advance();
+ }
+ break;
+ case 14: // ERROR_SQL_STATE_MARKER:
+ // 1 character - always #
+ packet.sqlStateMarker = String.fromCharCode(c);
+ packet.sqlState = '';
+ advance();
+ break;
+ case 15: // ERROR_SQL_STATE:
+ // 5 characters
+ if (packet.index < 5) {
+ packet.sqlState += String.fromCharCode(c);
+ }
+
+ if (packet.index == 4) {
+ advance(Parser.ERROR_MESSAGE);
+ }
+ break;
+ case 16: // ERROR_MESSAGE:
+ if (packet.received <= packet.length) {
+ packet.errorMessage = (packet.errorMessage || '') + String.fromCharCode(c);
+ }
+ break;
+
+ // OK_PACKET
+ case 17: // AFFECTED_ROWS:
+ packet.affectedRows = lengthCoded(packet.affectedRows);
+ break;
+ case 18: // INSERT_ID:
+ packet.insertId = lengthCoded(packet.insertId);
+ break;
+ case 19: // SERVER_STATUS:
+ if (packet.index == 0) {
+ packet.serverStatus = 0;
+ }
+
+ // 2 bytes - Little endian
+ packet.serverStatus += POWS[packet.index] * c;
+
+ if (packet.index == 1) {
+ advance();
+ }
+ break;
+ case 20: // WARNING_COUNT:
+ if (packet.index == 0) {
+ packet.warningCount = 0;
+ }
+
+ // 2 bytes - Little endian
+ packet.warningCount += POWS[packet.index] * c;
+
+ if (packet.index == 1) {
+ packet.message = '';
+ advance();
+ }
+ break;
+ case 21: // MESSAGE:
+ if (packet.received <= packet.length) {
+ packet.message += String.fromCharCode(c);
+ }
+ break;
+
+ // RESULT_SET_HEADER_PACKET
+ case 22: // EXTRA_LENGTH:
+ packet.extra = '';
+ self._lengthCodedStringLength = lengthCoded(self._lengthCodedStringLength);
+ break;
+ case 23: // EXTRA_STRING:
+ packet.extra += String.fromCharCode(c);
+ break;
+
+ // FIELD_PACKET or EOF_PACKET
+ case 24: // FIELD_CATALOG_LENGTH:
+ if (packet.index == 0) {
+ if (c === 0xfe) {
+ packet.type = Parser.EOF_PACKET;
+ advance(Parser.EOF_WARNING_COUNT);
+ break;
+ }
+ packet.type = Parser.FIELD_PACKET;
+ }
+ self._lengthCodedStringLength = lengthCoded(self._lengthCodedStringLength);
+ break;
+ case 25: // FIELD_CATALOG_STRING:
+ if (packet.index == 0) {
+ packet.catalog = '';
+ }
+ packet.catalog += String.fromCharCode(c);
+
+ if (packet.index + 1 === self._lengthCodedStringLength) {
+ advance();
+ }
+ break;
+ case 26: // FIELD_DB_LENGTH:
+ self._lengthCodedStringLength = lengthCoded(self._lengthCodedStringLength);
+ if (self._lengthCodedStringLength == 0) {
+ advance();
+ }
+ break;
+ case 27: // FIELD_DB_STRING:
+ if (packet.index == 0) {
+ packet.db = '';
+ }
+ packet.db += String.fromCharCode(c);
+
+ if (packet.index + 1 === self._lengthCodedStringLength) {
+ advance();
+ }
+ break;
+ case 28: // FIELD_TABLE_LENGTH:
+ self._lengthCodedStringLength = lengthCoded(self._lengthCodedStringLength);
+ if (self._lengthCodedStringLength == 0) {
+ advance();
+ }
+ break;
+ case 29: // FIELD_TABLE_STRING:
+ if (packet.index == 0) {
+ packet.table = '';
+ }
+ packet.table += String.fromCharCode(c);
+
+ if (packet.index + 1 === self._lengthCodedStringLength) {
+ advance();
+ }
+ break;
+ case 30: // FIELD_ORIGINAL_TABLE_LENGTH:
+ self._lengthCodedStringLength = lengthCoded(self._lengthCodedStringLength);
+ if (self._lengthCodedStringLength == 0) {
+ advance();
+ }
+ break;
+ case 31: // FIELD_ORIGINAL_TABLE_STRING:
+ if (packet.index == 0) {
+ packet.originalTable = '';
+ }
+ packet.originalTable += String.fromCharCode(c);
+
+ if (packet.index + 1 === self._lengthCodedStringLength) {
+ advance();
+ }
+ break;
+ case 32: // FIELD_NAME_LENGTH:
+ self._lengthCodedStringLength = lengthCoded(self._lengthCodedStringLength);
+ break;
+ case 33: // FIELD_NAME_STRING:
+ if (packet.index == 0) {
+ packet.name = '';
+ }
+ packet.name += String.fromCharCode(c);
+
+ if (packet.index + 1 === self._lengthCodedStringLength) {
+ advance();
+ }
+ break;
+ case 34: // FIELD_ORIGINAL_NAME_LENGTH:
+ self._lengthCodedStringLength = lengthCoded(self._lengthCodedStringLength);
+ if (self._lengthCodedStringLength == 0) {
+ advance();
+ }
+ break;
+ case 35: // FIELD_ORIGINAL_NAME_STRING:
+ if (packet.index == 0) {
+ packet.originalName = '';
+ }
+ packet.originalName += String.fromCharCode(c);
+
+ if (packet.index + 1 === self._lengthCodedStringLength) {
+ advance();
+ }
+ break;
+ case 36: // FIELD_FILLER_1:
+ // 1 bytes - 0x00
+ advance();
+ break;
+ case 37: // FIELD_CHARSET_NR:
+ if (packet.index == 0) {
+ packet.charsetNumber = 0;
+ }
+
+ // 2 bytes - Little endian
+ packet.charsetNumber += Math.pow(256, packet.index) * c;
+
+ if (packet.index == 1) {
+ advance();
+ }
+ break;
+ case 38: // FIELD_LENGTH:
+ if (packet.index == 0) {
+ packet.fieldLength = 0;
+ }
+
+ // 4 bytes - Little endian
+ packet.fieldLength += Math.pow(256, packet.index) * c;
+
+ if (packet.index == 3) {
+ advance();
+ }
+ break;
+ case 39: // FIELD_TYPE:
+ // 1 byte
+ packet.fieldType = c;
+ advance();
+ case 40: // FIELD_FLAGS:
+ if (packet.index == 0) {
+ packet.flags = 0;
+ }
+
+ // 2 bytes - Little endian
+ packet.flags += Math.pow(256, packet.index) * c;
+
+ if (packet.index == 1) {
+ advance();
+ }
+ break;
+ case 41: // FIELD_DECIMALS:
+ // 1 byte
+ packet.decimals = c;
+ advance();
+ break;
+ case 42: // FIELD_FILLER_2:
+ // 2 bytes - 0x00
+ if (packet.index == 1) {
+ advance();
+ }
+ break;
+ case 43: // FIELD_DEFAULT:
+ // TODO: Only occurs for mysql_list_fields()
+ break;
+
+ // EOF_PACKET
+ case 44: // EOF_WARNING_COUNT:
+ if (packet.index == 0) {
+ packet.warningCount = 0;
+ }
+
+ // 2 bytes - Little endian
+ packet.warningCount += Math.pow(256, packet.index) * c;
+
+ if (packet.index == 1) {
+ advance();
+ }
+ break;
+ case 45: // EOF_SERVER_STATUS:
+ if (packet.index == 0) {
+ packet.serverStatus = 0;
+ }
+
+ // 2 bytes - Little endian
+ packet.serverStatus += Math.pow(256, packet.index) * c;
+
+ if (packet.index == 1) {
+ if (this.receivingFieldPackets) {
+ this.receivingFieldPackets = false;
+ this.receivingRowPackets = true;
+ } else {
+ }
+ }
+ break;
+ case 46: // COLUMN_VALUE_LENGTH:
+ if (packet.index == 0) {
+ packet.columnLength = 0;
+ packet.type = Parser.ROW_DATA_PACKET;
+ }
+
+ if (packet.received == 1) {
+ if (c === 0xfe) {
+ packet.type = Parser.EOF_PACKET;
+ this.receivingRowPackets = false;
+ advance(Parser.EOF_WARNING_COUNT);
+ break;
+ }
+ this.emit('packet', packet);
+ }
+
+ packet.columnLength = lengthCoded(packet.columnLength);
+
+ if (!packet.columnLength && !this._lengthCodedLength) {
+ packet.emit('data', (packet.columnLength === null ? null : new Buffer(0)), 0);
+ if (packet.received < packet.length) {
+ advance(Parser.COLUMN_VALUE_LENGTH);
+ } else {
+ self.packet = packet = null;
+ self.state = state = Parser.PACKET_LENGTH;
+ continue;
+ }
+ }
+ break;
+ case 47: // COLUMN_VALUE_STRING:
+ var remaining = packet.columnLength - packet.index, read;
+ if (i + remaining > buffer.length) {
+ read = buffer.length - i;
+ packet.index += read;
+ packet.emit('data', buffer.slice(i, buffer.length), remaining - read);
+ // the -1 offsets are because these values are also manipulated by the loop itself
+ packet.received += read - 1;
+ i = buffer.length;
+ } else {
+ packet.emit('data', buffer.slice(i, i + remaining), 0);
+ i += remaining - 1;
+ packet.received += remaining - 1;
+ advance(Parser.COLUMN_VALUE_LENGTH);
+ // advance() sets this to -1, but packet.index++ is skipped, so we need to manually fix
+ packet.index = 0;
+ }
+
+ if (packet.received == packet.length) {
+ self.packet = packet = null;
+ self.state = state = Parser.PACKET_LENGTH;
+ }
+
+ continue;
+ }
+
+ packet.index++;
+
+ if (state > Parser.PACKET_NUMBER && packet.received === packet.length) {
+ emitPacket();
+ }
+ }
+};
+
+
+Parser.LENGTH_CODED_NULL = 251;
+Parser.LENGTH_CODED_16BIT_WORD= 252;
+Parser.LENGTH_CODED_24BIT_WORD= 253;
+Parser.LENGTH_CODED_64BIT_WORD= 254;
+
+// Parser states
+var s = 0;
+Parser.PACKET_LENGTH = s++;
+Parser.PACKET_NUMBER = s++;
+Parser.GREETING_PROTOCOL_VERSION = s++;
+Parser.GREETING_SERVER_VERSION = s++;
+Parser.GREETING_THREAD_ID = s++;
+Parser.GREETING_SCRAMBLE_BUFF_1 = s++;
+Parser.GREETING_FILLER_1 = s++;
+Parser.GREETING_SERVER_CAPABILITIES = s++;
+Parser.GREETING_SERVER_LANGUAGE = s++;
+Parser.GREETING_SERVER_STATUS = s++;
+Parser.GREETING_FILLER_2 = s++;
+Parser.GREETING_SCRAMBLE_BUFF_2 = s++;
+Parser.FIELD_COUNT = s++;
+Parser.ERROR_NUMBER = s++;
+Parser.ERROR_SQL_STATE_MARKER = s++;
+Parser.ERROR_SQL_STATE = s++;
+Parser.ERROR_MESSAGE = s++;
+Parser.AFFECTED_ROWS = s++;
+Parser.INSERT_ID = s++;
+Parser.SERVER_STATUS = s++;
+Parser.WARNING_COUNT = s++;
+Parser.MESSAGE = s++;
+Parser.EXTRA_LENGTH = s++;
+Parser.EXTRA_STRING = s++;
+Parser.FIELD_CATALOG_LENGTH = s++;
+Parser.FIELD_CATALOG_STRING = s++;
+Parser.FIELD_DB_LENGTH = s++;
+Parser.FIELD_DB_STRING = s++;
+Parser.FIELD_TABLE_LENGTH = s++;
+Parser.FIELD_TABLE_STRING = s++;
+Parser.FIELD_ORIGINAL_TABLE_LENGTH = s++;
+Parser.FIELD_ORIGINAL_TABLE_STRING = s++;
+Parser.FIELD_NAME_LENGTH = s++;
+Parser.FIELD_NAME_STRING = s++;
+Parser.FIELD_ORIGINAL_NAME_LENGTH = s++;
+Parser.FIELD_ORIGINAL_NAME_STRING = s++;
+Parser.FIELD_FILLER_1 = s++;
+Parser.FIELD_CHARSET_NR = s++;
+Parser.FIELD_LENGTH = s++;
+Parser.FIELD_TYPE = s++;
+Parser.FIELD_FLAGS = s++;
+Parser.FIELD_DECIMALS = s++;
+Parser.FIELD_FILLER_2 = s++;
+Parser.FIELD_DEFAULT = s++;
+Parser.EOF_WARNING_COUNT = s++;
+Parser.EOF_SERVER_STATUS = s++;
+Parser.COLUMN_VALUE_LENGTH = s++;
+Parser.COLUMN_VALUE_STRING = s++;
+
+// Packet types
+var p = 0;
+Parser.GREETING_PACKET = p++;
+Parser.OK_PACKET = p++;
+Parser.ERROR_PACKET = p++;
+Parser.RESULT_SET_HEADER_PACKET = p++;
+Parser.FIELD_PACKET = p++;
+Parser.EOF_PACKET = p++;
+Parser.ROW_DATA_PACKET = p++;
+Parser.ROW_DATA_BINARY_PACKET = p++;
+Parser.OK_FOR_PREPARED_STATEMENT_PACKET = p++;
+Parser.PARAMETER_PACKET = p++;
+Parser.USE_OLD_PASSWORD_PROTOCOL_PACKET = p++;
diff --git a/lib/query.js b/lib/query.js
new file mode 100755
index 0000000..72e66c6
--- /dev/null
+++ b/lib/query.js
@@ -0,0 +1,140 @@
+var util = require('util');
+var EventEmitter = require('events').EventEmitter;
+var Parser = require('./parser');
+var Client;
+
+function Query(properties) {
+ EventEmitter.call(this);
+
+ this.sql = null;
+ this.typeCast = true;
+
+ for (var key in properties) {
+ this[key] = properties[key];
+ }
+};
+util.inherits(Query, EventEmitter);
+module.exports = Query;
+
+Query.prototype._handlePacket = function(packet) {
+ var self = this;
+
+ // We can't do this require() on top of the file.
+ // That's because there is circular dependency and we're overwriting
+ // module.exports
+ Client = Client || require('./client');
+
+ switch (packet.type) {
+ case Parser.OK_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:
+ if (!this._fields) {
+ this._fields = [];
+ }
+
+ this._fields.push(packet);
+ this.emit('field', packet);
+ break;
+ case Parser.EOF_PACKET:
+ if (!this._eofs) {
+ this._eofs = 1;
+ } else {
+ this._eofs++;
+ }
+
+ if (this._eofs == 2) {
+ this.emit('end');
+ }
+ break;
+ case Parser.ROW_DATA_PACKET:
+ var row = this._row = {}, field;
+
+ this._rowIndex = 0;
+
+ 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) {
+ 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]);
+ 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_FLOAT:
+ case Query.FIELD_TYPE_DOUBLE:
+ // decimal types cannot be parsed as floats because
+ // V8 Numbers have less precision than some MySQL Decimals
+ row[field.name] = parseFloat(row[field.name]);
+ break;
+ }
+ }
+
+ if (self._rowIndex == self._fields.length) {
+ delete self._row;
+ delete self._rowIndex;
+ self.emit('row', row);
+ return;
+ }
+
+ field = null;
+ });
+ break;
+ }
+};
+
+Query.FIELD_TYPE_DECIMAL = 0x00;
+Query.FIELD_TYPE_TINY = 0x01;
+Query.FIELD_TYPE_SHORT = 0x02;
+Query.FIELD_TYPE_LONG = 0x03;
+Query.FIELD_TYPE_FLOAT = 0x04;
+Query.FIELD_TYPE_DOUBLE = 0x05;
+Query.FIELD_TYPE_NULL = 0x06;
+Query.FIELD_TYPE_TIMESTAMP = 0x07;
+Query.FIELD_TYPE_LONGLONG = 0x08;
+Query.FIELD_TYPE_INT24 = 0x09;
+Query.FIELD_TYPE_DATE = 0x0a;
+Query.FIELD_TYPE_TIME = 0x0b;
+Query.FIELD_TYPE_DATETIME = 0x0c;
+Query.FIELD_TYPE_YEAR = 0x0d;
+Query.FIELD_TYPE_NEWDATE = 0x0e;
+Query.FIELD_TYPE_VARCHAR = 0x0f;
+Query.FIELD_TYPE_BIT = 0x10;
+Query.FIELD_TYPE_NEWDECIMAL = 0xf6;
+Query.FIELD_TYPE_ENUM = 0xf7;
+Query.FIELD_TYPE_SET = 0xf8;
+Query.FIELD_TYPE_TINY_BLOB = 0xf9;
+Query.FIELD_TYPE_MEDIUM_BLOB = 0xfa;
+Query.FIELD_TYPE_LONG_BLOB = 0xfb;
+Query.FIELD_TYPE_BLOB = 0xfc;
+Query.FIELD_TYPE_VAR_STRING = 0xfd;
+Query.FIELD_TYPE_STRING = 0xfe;
+Query.FIELD_TYPE_GEOMETRY = 0xff;
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..5f84004
--- /dev/null
+++ b/package.json
@@ -0,0 +1,27 @@
+{
+ "name": "mysql",
+ "version": "0.9.6",
+ "author": "Felix Geisendörfer <felix at debuggable.com> (http://debuggable.com/)",
+ "description": "A pure node.js JavaScript Client implementing the MySQL protocol.",
+ "homepage": "https://github.com/felixge/node-mysql",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/felixge/node-mysql.git"
+ },
+ "main": "./index",
+ "scripts": {
+ "test": "make test"
+ },
+ "dependencies": {
+ "hashish": "0.0.4"
+ },
+ "devDependencies": {
+ "gently": "0.8.0",
+ "urun": "0.0.4",
+ "utest": "0.0.3"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "optionalDependencies": {}
+}
\ No newline at end of file
diff --git a/test/common.js b/test/common.js
new file mode 100644
index 0000000..d71b58d
--- /dev/null
+++ b/test/common.js
@@ -0,0 +1,23 @@
+var mysql = require('..');
+var path = require('path');
+
+var root = path.join(__dirname, '../');
+exports.dir = {
+ root: root,
+ lib: root + '/lib',
+ fixture: root + '/test/fixture',
+};
+
+exports.TEST_DB = 'node_mysql_test';
+exports.TEST_TABLE = 'posts';
+
+exports.createClient = function() {
+ try {
+ var config = require('./config');
+ } catch (e) {
+ console.log('Skipping. See test/config.template.js for more information.');
+ process.exit(0);
+ }
+
+ return mysql.createClient(config);
+};
diff --git a/test/config.template.js b/test/config.template.js
new file mode 100644
index 0000000..e927ab8
--- /dev/null
+++ b/test/config.template.js
@@ -0,0 +1,8 @@
+// Copy this file to test/config.js and fill in your own credentials in order
+// to run the system test suite.
+module.exports = {
+ host: 'localhost',
+ port: 3306,
+ user: 'root',
+ password: 'root'
+};
diff --git a/test/fixture/columnia.sql b/test/fixture/columnia.sql
new file mode 100644
index 0000000..c770e48
--- /dev/null
+++ b/test/fixture/columnia.sql
@@ -0,0 +1,54 @@
+CREATE TABLE `columnia` (
+ `f0` bigint(20) unsigned NOT NULL DEFAULT '0',
+ `field0001` bigint(20) unsigned NOT NULL DEFAULT '0',
+ `field002` bigint(20) unsigned NOT NULL DEFAULT '0',
+ `field0000000003` bigint(20) unsigned DEFAULT NULL,
+ `field00004` bigint(20) unsigned NOT NULL DEFAULT '0',
+ `field000000000005` bigint(20) unsigned DEFAULT NULL,
+ `field0000000000006` bigint(20) unsigned NOT NULL,
+ `field000007` text NOT NULL,
+ `field0000000008` text NOT NULL,
+ `field009` int(10) NOT NULL DEFAULT '0',
+ `field00010` text NOT NULL,
+ `field000000000011` text NOT NULL,
+ `field00000012` text NOT NULL,
+ `field0000013` text NOT NULL,
+ `field00000000000014` text NOT NULL,
+ `field0000000015` text NOT NULL,
+ `field000016` text NOT NULL,
+ `field000000000017` text NOT NULL,
+ `field0000000000000018` text NOT NULL,
+ `field0000000000019` text NOT NULL,
+ `field0000000000020` text NOT NULL,
+ `field00000000000021` text NOT NULL,
+ `field00000000022` text NOT NULL,
+ `field000000000000000023` text NOT NULL,
+ `field000000000000024` text NOT NULL,
+ `field000000000000025` text NOT NULL,
+ `field0000000000000026` text NOT NULL,
+ `field0000000000027` text NOT NULL,
+ `field00000028` bigint(20) unsigned NOT NULL,
+ `field000000029` tinyint(1) NOT NULL,
+ `field000030` tinyint(1) NOT NULL DEFAULT '0',
+ `field31` text NOT NULL,
+ `field00000032` bigint(20) unsigned NOT NULL,
+ `fi33` int(11) NOT NULL DEFAULT '0',
+ `field00000034` bigint(20) unsigned DEFAULT NULL,
+ `field000035` text NOT NULL,
+ `field0036` int(11) NOT NULL DEFAULT '0',
+ `field0000000000000000000037` text NOT NULL,
+ `field0000000000000000000038` text NOT NULL,
+ `field0000000000000000000000000039` text NOT NULL,
+ `field0000000000000000040` text NOT NULL,
+ `field00000000000000000041` text NOT NULL,
+ `field00000000000000000000042` text NOT NULL,
+ `field00000000000000000043` text NOT NULL,
+ `field00000000000000000044` text NOT NULL,
+ `field00000000000000000000000045` text NOT NULL,
+ `field00000000000000046` text NOT NULL,
+ `field000000000000000047` text NOT NULL,
+ `field000000000000000000048` text NOT NULL,
+ `field49` text NOT NULL,
+ `field50` int(11) NOT NULL,
+ PRIMARY KEY (`f0`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
diff --git a/test/fixture/libmysql_password.c b/test/fixture/libmysql_password.c
new file mode 100644
index 0000000..3e19c83
--- /dev/null
+++ b/test/fixture/libmysql_password.c
@@ -0,0 +1,127 @@
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+
+#define SCRAMBLE_LENGTH_323 8
+
+typedef unsigned long ulong;
+typedef unsigned int uint;
+typedef unsigned char uchar;
+
+struct rand_struct {
+ unsigned long seed1,seed2,max_value;
+ double max_value_dbl;
+};
+
+void hash_password(ulong *result, const char *password, uint password_len)
+{
+ register ulong nr=1345345333L, add=7, nr2=0x12345671L;
+ ulong tmp;
+ const char *password_end= password + password_len;
+ for (; password < password_end; password++)
+ {
+ if (*password == ' ' || *password == '\t')
+ continue; /* skip space in password */
+ tmp= (ulong) (uchar) *password;
+ nr^= (((nr & 63)+add)*tmp)+ (nr << 8);
+ nr2+=(nr2 << 8) ^ nr;
+ add+=tmp;
+ }
+ result[0]=nr & (((ulong) 1L << 31) -1L); /* Don't use sign bit (str2int) */;
+ result[1]=nr2 & (((ulong) 1L << 31) -1L);
+}
+
+void randominit(struct rand_struct *rand_st, ulong seed1, ulong seed2)
+{ /* For mysql 3.21.# */
+#ifdef HAVE_purify
+ bzero((char*) rand_st,sizeof(*rand_st)); /* Avoid UMC varnings */
+#endif
+ rand_st->max_value= 0x3FFFFFFFL;
+ rand_st->max_value_dbl=(double) rand_st->max_value;
+ rand_st->seed1=seed1%rand_st->max_value ;
+ rand_st->seed2=seed2%rand_st->max_value;
+}
+
+double my_rnd(struct rand_struct *rand_st)
+{
+ rand_st->seed1=(rand_st->seed1*3+rand_st->seed2) % rand_st->max_value;
+ rand_st->seed2=(rand_st->seed1+rand_st->seed2+33) % rand_st->max_value;
+ return (((double) rand_st->seed1)/rand_st->max_value_dbl);
+}
+
+void scramble_323(char *to, const char *message, const char *password)
+{
+ struct rand_struct rand_st;
+ ulong hash_pass[2], hash_message[2];
+
+ if (password && password[0])
+ {
+ char extra, *to_start=to;
+ const char *message_end= message + SCRAMBLE_LENGTH_323;
+ hash_password(hash_pass,password, (uint) strlen(password));
+ hash_password(hash_message, message, SCRAMBLE_LENGTH_323);
+ randominit(&rand_st,hash_pass[0] ^ hash_message[0],
+ hash_pass[1] ^ hash_message[1]);
+ for (; message < message_end; message++)
+ *to++= (char) (floor(my_rnd(&rand_st)*31)+64);
+ extra=(char) (floor(my_rnd(&rand_st)*31));
+ while (to_start != to)
+ *(to_start++)^=extra;
+ }
+ *to= 0;
+}
+
+int main() {
+ const char password1[] = "root";
+ const char password2[] = "long password test";
+ const char password3[] = "saf789yasfbsd89f";
+ ulong result[2];
+ char scrm[9]; // SCRAMBLE_LENGTH_323+1
+ struct rand_struct rand_st;
+ int i;
+
+ // test hash_password
+ hash_password((ulong*)result, password1, strlen(password1));
+ printf("hash_password(\"%s\") = %08x%08x\n", password1, result[0], result[1]);
+
+ hash_password((ulong*)result, password2, strlen(password2));
+ printf("hash_password(\"%s\") = %08x%08x\n", password2, result[0], result[1]);
+
+ hash_password((ulong*)result, password3, strlen(password3));
+ printf("hash_password(\"%s\") = %08x%08x\n", password3, result[0], result[1]);
+
+
+ // test randominit
+ randominit(&rand_st, 0, 0);
+ printf("randominit(0x00000000,0x00000000) = %08x, %08x\n", rand_st.seed1, rand_st.seed2);
+
+ randominit(&rand_st, 0xFFFF, 0xFFFF);
+ printf("randominit(0x0000FFFF,0x0000FFFF) = %08x, %08x\n", rand_st.seed1, rand_st.seed2);
+
+ randominit(&rand_st, 0x50000000, 0x50000000);
+ printf("randominit(0x50000000,0x50000000) = %08x, %08x\n", rand_st.seed1, rand_st.seed2);
+
+ randominit(&rand_st, 0xFFFFFFFF, 0xFFFFFFFF);
+ printf("randominit(0xFFFFFFFF,0xFFFFFFFF) = %08x, %08x\n", rand_st.seed1, rand_st.seed2);
+
+
+ // test my_rnd
+ randominit(&rand_st, 3252345, 7149734);
+ printf("randominit(3252345, 7149734) = %08x, %08x\n", rand_st.seed1, rand_st.seed2);
+ for (i=0; i<10; i++){
+ printf("my_rnd() : %.16f\n", my_rnd(&rand_st));
+ }
+
+
+ // test scramble_323
+ scramble_323(scrm, "8bytesofstuff", "root");
+ printf("scramble323(8bytesofstuff, root): %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ scrm[0], scrm[1], scrm[2], scrm[3], scrm[4], scrm[5], scrm[6], scrm[7], scrm[8]);
+
+ scramble_323(scrm, "e8cf00cec9ec825af22", "saf789yasfbsd");
+ printf("scramble323(e8cf00cec9ec825af22, saf789yasfbsd): %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ scrm[0], scrm[1], scrm[2], scrm[3], scrm[4], scrm[5], scrm[6], scrm[7], scrm[8]);
+
+ return 23;
+}
+
diff --git a/test/integration/Client/commands/test-ping.js b/test/integration/Client/commands/test-ping.js
new file mode 100644
index 0000000..e46f6a6
--- /dev/null
+++ b/test/integration/Client/commands/test-ping.js
@@ -0,0 +1,11 @@
+var common = require('../../../common');
+var assert = require('assert');
+
+var client = common.createClient();
+client.ping(function(err, result) {
+ if (err) throw err;
+
+ assert.strictEqual(result.affectedRows, 0);
+
+ client.end();
+});
diff --git a/test/integration/Client/commands/test-statistics.js b/test/integration/Client/commands/test-statistics.js
new file mode 100644
index 0000000..f5ef320
--- /dev/null
+++ b/test/integration/Client/commands/test-statistics.js
@@ -0,0 +1,10 @@
+var common = require('../../../common');
+var assert = require('assert');
+
+var client = common.createClient();
+client.statistics(function(err, result) {
+ if (err) throw err;
+
+ assert.ok(result.extra.match(/time/i));
+ client.end();
+});
diff --git a/test/integration/Client/commands/test-useDatabase.js b/test/integration/Client/commands/test-useDatabase.js
new file mode 100644
index 0000000..8ac4f47
--- /dev/null
+++ b/test/integration/Client/commands/test-useDatabase.js
@@ -0,0 +1,13 @@
+var common = require('../../../common');
+var assert = require('assert');
+
+var client = common.createClient();
+client.useDatabase(common.TEST_DB, function(err, result) {
+ // The TEST_DB may not exist right now, so ignore errors related to that
+ if (err && err.number === mysql.ERROR_BAD_DB_ERROR) err = null;
+
+ if (err) throw err;
+
+ assert.strictEqual(result.affectedRows, 0);
+ client.end();
+});
diff --git a/test/integration/Client/connection/test-automatic-connect.js b/test/integration/Client/connection/test-automatic-connect.js
new file mode 100644
index 0000000..bded51c
--- /dev/null
+++ b/test/integration/Client/connection/test-automatic-connect.js
@@ -0,0 +1,11 @@
+var common = require('../../../common');
+var assert = require('assert');
+
+var client = common.createClient();
+client.query('SELECT 1', function(err, results) {
+ if (err) throw err;
+
+ assert.deepEqual(results, [{1: 1}]);
+
+ client.end();
+});
diff --git a/test/integration/Client/connection/test-automatic-reconnect-on-timeout.js b/test/integration/Client/connection/test-automatic-reconnect-on-timeout.js
new file mode 100644
index 0000000..b03c99c
--- /dev/null
+++ b/test/integration/Client/connection/test-automatic-reconnect-on-timeout.js
@@ -0,0 +1,24 @@
+var common = require('../../../common');
+var assert = require('assert');
+
+var client = common.createClient();
+// Not sure if we need all 3 of these, but they do the trick
+client.query('SET interactive_timeout = 1');
+client.query('SET wait_timeout = 1');
+client.query('SET net_read_timeout = 1');
+
+var result;
+client._socket.on('end', function() {
+ assert.equal(client.connected, false);
+
+ client.query('SELECT 1', function(err, _result) {
+ if (err) throw err;
+
+ result = _result;
+ client.destroy();
+ });
+});
+
+process.on('exit', function() {
+ assert.deepEqual(result, [{'1': 1}]);
+});
diff --git a/test/integration/Client/connection/test-bad-credentials.js.js b/test/integration/Client/connection/test-bad-credentials.js.js
new file mode 100644
index 0000000..a53dee3
--- /dev/null
+++ b/test/integration/Client/connection/test-bad-credentials.js.js
@@ -0,0 +1,24 @@
+var common = require('../../../common');
+var assert = require('assert');
+
+var client = common.createClient();
+client.password = 'thispassworddoesnotreallywork';
+
+var callbacks = [];
+client.query('SELECT 1', function(err) {
+ assert.ok(/access denied/i.test(err.message));
+
+ callbacks.push(1);
+});
+
+client.query('SELECT 2', function(err) {
+ assert.ok(/access denied/i.test(err.message));
+
+ callbacks.push(2);
+
+ client.destroy();
+});
+
+process.on('exit', function() {
+ assert.deepEqual(callbacks, [1, 2]);
+});
diff --git a/test/integration/Client/connection/test-connection-errors-go-to-callback.js.js b/test/integration/Client/connection/test-connection-errors-go-to-callback.js.js
new file mode 100644
index 0000000..0189f59
--- /dev/null
+++ b/test/integration/Client/connection/test-connection-errors-go-to-callback.js.js
@@ -0,0 +1,25 @@
+var common = require('../../../common');
+var assert = require('assert');
+
+var client = common.createClient();
+// Port number outside of range -> triggers connection error
+client.port = 999999999;
+
+var callbacks = [];
+client.query('SELECT 1', function(err) {
+ assert.ok(err);
+
+ callbacks.push(1);
+});
+
+client.query('SELECT 2', function(err) {
+ assert.ok(err);
+
+ callbacks.push(2);
+
+ client.destroy();
+});
+
+process.on('exit', function() {
+ assert.deepEqual(callbacks, [1, 2]);
+});
diff --git a/test/integration/Client/connection/test-reconnect-closed-client.js.js b/test/integration/Client/connection/test-reconnect-closed-client.js.js
new file mode 100644
index 0000000..32f4724
--- /dev/null
+++ b/test/integration/Client/connection/test-reconnect-closed-client.js.js
@@ -0,0 +1,27 @@
+var common = require('../../../common');
+var assert = require('assert');
+
+var client = common.createClient();
+var callbacks = [];
+client.query('SELECT 1', function(err, results) {
+ if (err) throw err;
+
+ callbacks.push(1);
+});
+
+client.end(function(err) {
+ if (err) throw err;
+
+ callbacks.push(2);
+});
+
+client.query('SELECT 1', function(err) {
+ if (err) throw err;
+
+ callbacks.push(3);
+ client.destroy();
+});
+
+process.on('exit', function() {
+ assert.deepEqual(callbacks, [1, 2, 3]);
+});
diff --git a/test/integration/Query/errors/test-should-delegate-to-client-if-needed.js b/test/integration/Query/errors/test-should-delegate-to-client-if-needed.js
new file mode 100644
index 0000000..7e93437
--- /dev/null
+++ b/test/integration/Query/errors/test-should-delegate-to-client-if-needed.js
@@ -0,0 +1,17 @@
+var common = require('../../../common');
+var assert = require('assert');
+var INVALID_QUERY = 'first invalid #*&% query';
+
+var client = common.createClient();
+var err;
+
+client.query(INVALID_QUERY);
+client .on('error', function(_err) {
+ err = _err;
+ client.destroy();
+});
+
+process.on('exit', function() {
+ assert.ok(err);
+ assert.strictEqual(err.sql, INVALID_QUERY);
+});
diff --git a/test/integration/Query/errors/test-should-emit-error-event.js b/test/integration/Query/errors/test-should-emit-error-event.js
new file mode 100644
index 0000000..7d16a92
--- /dev/null
+++ b/test/integration/Query/errors/test-should-emit-error-event.js
@@ -0,0 +1,18 @@
+var common = require('../../../common');
+var assert = require('assert');
+var INVALID_QUERY = 'first invalid #*&% query';
+
+var client = common.createClient();
+var err;
+
+client
+ .query(INVALID_QUERY)
+ .on('error', function(_err) {
+ err = _err;
+ client.destroy();
+ });
+
+process.on('exit', function() {
+ assert.ok(err);
+ assert.strictEqual(err.sql, INVALID_QUERY);
+});
diff --git a/test/integration/Query/errors/test-should-not-leave-client-in-broken-state.js b/test/integration/Query/errors/test-should-not-leave-client-in-broken-state.js
new file mode 100644
index 0000000..a8623fd
--- /dev/null
+++ b/test/integration/Query/errors/test-should-not-leave-client-in-broken-state.js
@@ -0,0 +1,37 @@
+var common = require('../../../common');
+var assert = require('assert');
+var INVALID_QUERY = 'first invalid #*&% query';
+
+var client = common.createClient();
+var callbacks = [];
+
+client.query(INVALID_QUERY, function(err) {
+ assert.strictEqual(err.sql, INVALID_QUERY);
+ callbacks.push(1);
+});
+
+client.query('SHOW STATUS', function(err, rows, fields) {
+ if (err) throw err;
+
+ assert.equal(rows.length >= 50, true);
+ assert.equal(Object.keys(fields).length, 2);
+
+ callbacks.push(2);
+});
+
+client.query(INVALID_QUERY, function(err) {
+ assert.strictEqual(err.sql, INVALID_QUERY);
+
+ callbacks.push(3);
+});
+
+client.query(INVALID_QUERY, function(err) {
+ assert.strictEqual(err.sql, INVALID_QUERY);
+
+ client.destroy();
+ callbacks.push(4);
+});
+
+process.on('exit', function() {
+ assert.deepEqual(callbacks, [1, 2, 3, 4]);
+});
diff --git a/test/integration/Query/errors/test-should-raise-callback.js b/test/integration/Query/errors/test-should-raise-callback.js
new file mode 100644
index 0000000..9ee370d
--- /dev/null
+++ b/test/integration/Query/errors/test-should-raise-callback.js
@@ -0,0 +1,16 @@
+var common = require('../../../common');
+var assert = require('assert');
+var INVALID_QUERY = 'first invalid #*&% query';
+
+var client = common.createClient();
+
+var err;
+client.query(INVALID_QUERY, function(_err) {
+ err = _err;
+ client.destroy();
+});
+
+process.on('exit', function() {
+ assert.ok(err);
+ assert.strictEqual(err.sql, INVALID_QUERY);
+});
diff --git a/test/integration/Query/misc/test-column-ordering.js b/test/integration/Query/misc/test-column-ordering.js
new file mode 100644
index 0000000..c842090
--- /dev/null
+++ b/test/integration/Query/misc/test-column-ordering.js
@@ -0,0 +1,36 @@
+var common = require('../../../common');
+var assert = require('assert');
+var fs = require('fs');
+var mysql = require(common.dir.root);
+var REPEATS = 500;
+
+var client = common.createClient();
+client.query('CREATE DATABASE ' + common.TEST_DB, function(err) {
+ if (err && err.number != mysql.ERROR_DB_CREATE_EXISTS) throw err;
+});
+client.query('USE ' + common.TEST_DB);
+
+client.query('DROP TABLE IF EXISTS columnia');
+var fixture = fs.readFileSync(common.dir.fixture + '/columnia.sql', 'utf8');
+client.query(fixture);
+
+var finished = 0;
+var self = this;
+for (var i = 0; i < REPEATS; i++) {
+ (function(i) {
+ var query = client.query("SHOW COLUMNS FROM columnia");
+
+ query.on('row', function(row) {
+ if (!row.Type) throw new Error('Column order mixed up after '+i+' queries.');
+ });
+
+ query.on('end', function() {
+ finished++;
+ if (finished === REPEATS) client.destroy();
+ });
+ })(i);
+}
+
+process.on('exit', function() {
+ assert.equal(finished, REPEATS);
+});
diff --git a/test/integration/Query/results/test-empty-string.js b/test/integration/Query/results/test-empty-string.js
new file mode 100644
index 0000000..5de5bb2
--- /dev/null
+++ b/test/integration/Query/results/test-empty-string.js
@@ -0,0 +1,14 @@
+var common = require('../../../common');
+var assert = require('assert');
+
+var client = common.createClient();
+var results;
+client.query('SELECT "" as field_a', function(err, _results) {
+ if (err) throw err;
+ results = _results;
+ client.destroy();
+});
+
+process.on('exit', function() {
+ assert.strictEqual(results[0].field_a, "");
+});
diff --git a/test/integration/Query/results/test-long-fields.js b/test/integration/Query/results/test-long-fields.js
new file mode 100644
index 0000000..21d06c3
--- /dev/null
+++ b/test/integration/Query/results/test-long-fields.js
@@ -0,0 +1,43 @@
+var common = require('../../../common');
+var assert = require('assert');
+
+var client = common.createClient();
+var field_a = makeString(250);
+var field_b = makeString(251);
+var field_c = makeString(512);
+var field_d = makeString(65537);
+var callbacks = 0;
+
+// We execute this test twice to be sure the parser is in a good state after
+// each run.
+test();
+test(true);
+
+function test(last) {
+ var sql = 'SELECT ? as field_a, ? as field_b, ? as field_c, ? as field_d';
+ var params = [field_a, field_b, field_c, field_d];
+
+ var query = client.query(sql, params, function(err, results) {
+ if (err) throw err;
+
+ assert.equal(results[0].field_a, field_a);
+ assert.equal(results[0].field_b, field_b);
+ assert.equal(results[0].field_c, field_c);
+ assert.equal(results[0].field_d, field_d);
+
+ callbacks++;
+ if (last) client.destroy();
+ });
+}
+
+function makeString(length) {
+ var str = '';
+ for (var i = 0; i < length; i++) {
+ str += 'x';
+ }
+ return str;
+
+}
+process.on('exit', function() {
+ assert.equal(callbacks, 2);
+});
diff --git a/test/integration/Query/results/test-null-value.js b/test/integration/Query/results/test-null-value.js
new file mode 100644
index 0000000..d2185ee
--- /dev/null
+++ b/test/integration/Query/results/test-null-value.js
@@ -0,0 +1,15 @@
+var common = require('../../../common');
+var assert = require('assert');
+
+var client = common.createClient();
+var results;
+client.query('SELECT NULL as field_a, NULL as field_b', function(err, _results) {
+ if (err) throw err;
+ results = _results;
+ client.destroy();
+});
+
+process.on('exit', function() {
+ assert.strictEqual(results[0].field_a, null);
+ assert.strictEqual(results[0].field_b, null);
+});
diff --git a/test/integration/Query/results/test-real-world-usage.js b/test/integration/Query/results/test-real-world-usage.js
new file mode 100644
index 0000000..b08acf5
--- /dev/null
+++ b/test/integration/Query/results/test-real-world-usage.js
@@ -0,0 +1,51 @@
+var common = require('../../../common');
+var assert = require('assert');
+var mysql = require(common.dir.root);
+
+var client = common.createClient();
+client.query('CREATE DATABASE '+common.TEST_DB, function createDbCb(err) {
+ if (err && err.number != mysql.ERROR_DB_CREATE_EXISTS) done(err);
+});
+
+client.query('USE '+common.TEST_DB);
+
+client.query(
+ 'CREATE TEMPORARY TABLE ' + common.TEST_TABLE+
+ '(id INT(11) AUTO_INCREMENT, title VARCHAR(255), text TEXT, created DATETIME, PRIMARY KEY (id));'
+);
+
+client.query(
+ 'INSERT INTO ' + common.TEST_TABLE + ' '+
+ 'SET title = ?, text = ?, created = ?',
+ ['super cool', 'this is a nice long text', '2010-08-16 10:00:23']
+);
+
+var query = client.query(
+ 'INSERT INTO '+common.TEST_TABLE+' '+
+ 'SET title = ?, text = ?, created = ?',
+ ['another entry', 'because 2 entries make a better test', null]
+);
+
+var endCalled = false;
+query.on('end', function insertOkCb(packet) {
+ endCalled = true;
+});
+
+var lastQueryReached = false;
+var query = client.query('SELECT * FROM '+common.TEST_TABLE, function selectCb(err, results, fields) {
+ assert.ok(endCalled);
+
+ lastQueryReached = true;
+
+ assert.equal(results.length, 2);
+ assert.equal(results[1].title, 'another entry');
+ assert.ok(typeof results[1].id == 'number');
+ assert.ok(results[0].created instanceof Date);
+ assert.strictEqual(results[1].created, null);
+
+ client.destroy();
+});
+
+process.on('exit', function() {
+ assert.ok(lastQueryReached);
+});
diff --git a/test/integration/Query/results/test-virtual-fields.js b/test/integration/Query/results/test-virtual-fields.js
new file mode 100644
index 0000000..2c8f29e
--- /dev/null
+++ b/test/integration/Query/results/test-virtual-fields.js
@@ -0,0 +1,16 @@
+var common = require('../../../common');
+var assert = require('assert');
+
+var client = common.createClient();
+var results;
+client.query('SELECT 1 as field_a, 2 as field_b', function(err, _results) {
+ if (err) throw err;
+
+ results = _results;
+ client.destroy();
+});
+
+process.on('exit', function() {
+ assert.equal(results[0].field_a, 1);
+ assert.equal(results[0].field_b, 2);
+});
diff --git a/test/run.js b/test/run.js
new file mode 100755
index 0000000..02d6d5c
--- /dev/null
+++ b/test/run.js
@@ -0,0 +1 @@
+require('urun')(__dirname)
diff --git a/test/unit/legacy/common.js b/test/unit/legacy/common.js
new file mode 100644
index 0000000..35a2efb
--- /dev/null
+++ b/test/unit/legacy/common.js
@@ -0,0 +1,25 @@
+var newCommon = require('../../common');
+exports.dir = newCommon.dir;
+
+var path = require('path');
+var util = require('util');
+
+global.TEST_DB = 'node_mysql_test';
+global.TEST_TABLE = 'posts';
+global.TEST_FIXTURES = path.join(__dirname, '../fixture');
+
+global.Gently = require('gently');
+global.assert = require('assert');
+global.p = function(val) {
+ util.error(util.inspect(val));
+};
+
+global.GENTLY = new Gently();
+global.HIJACKED = GENTLY.hijacked;
+
+// Stupid new feature in node that complains about gently attaching too many
+// listeners to process 'exit'. This is a workaround until I can think of a
+// better way to deal with this.
+if (process.setMaxListeners) {
+ process.setMaxListeners(Infinity);
+}
diff --git a/test/unit/legacy/test-auth.js b/test/unit/legacy/test-auth.js
new file mode 100644
index 0000000..12187ea
--- /dev/null
+++ b/test/unit/legacy/test-auth.js
@@ -0,0 +1,118 @@
+var common = require('./common');
+var auth = require(common.dir.lib + '/auth');
+
+function test(test) {
+ gently = new Gently();
+ test();
+ gently.verify(test.name);
+}
+
+test(function sha1() {
+ assert.deepEqual(
+ auth.sha1('root'),
+ new Buffer([
+ 220, 118, 233, 240, 192,
+ 0, 110, 143, 145, 158,
+ 12, 81, 92, 102, 219,
+ 186, 57, 130, 247, 133
+ ]).toString('binary')
+ );
+});
+
+test(function xor() {
+ var a = new Buffer([170, 220]), // 10101010 11011100
+ b = new Buffer([220, 170]), // 11011100 10101010
+ expected = new Buffer([118, 118]); // 01110110 01110110
+
+ assert.deepEqual(auth.xor(a.toString('binary'), b.toString('binary')), expected);
+});
+
+test(function token() {
+ var SCRAMBLE = new Buffer([0, 1, 2, 3, 4, 5]);
+
+ (function testRegular() {
+ var PASS = 'root',
+ STAGE_1 = auth.sha1(PASS),
+ TOKEN = auth.xor(
+ auth.sha1(new Buffer(SCRAMBLE + auth.sha1(STAGE_1), 'binary')),
+ STAGE_1
+ );
+
+ assert.deepEqual(auth.token('root', SCRAMBLE), TOKEN);
+ })();
+
+ (function testNoPassword() {
+ assert.deepEqual(auth.token(null, SCRAMBLE), new Buffer(0));
+ })();
+});
+
+test(function hashPassword() {
+ function verify(password, bytes){
+ var expected = new Buffer(bytes);
+ var actual = auth.hashPassword(password);
+
+ assert.deepEqual(actual, expected);
+ }
+
+ verify('root', [0x67, 0x45, 0x7E, 0x22, 0x6a, 0x1a, 0x15, 0xbd]);
+ verify('long password test', [0x6c, 0x24, 0x68, 0x41, 0x2c, 0xa6, 0x86, 0x56]);
+ verify('saf789yasfbsd89f', [0x6c, 0x9b, 0x2f, 0x07, 0x17, 0xeb, 0x95, 0xc6]);
+});
+
+
+test(function randomInit() {
+ function verify(in1, in2, out1, out2){
+ var r = auth.randomInit(in1, in2);
+ assert.equal(out1, r.seed1);
+ assert.equal(out2, r.seed2);
+ }
+
+ verify(0x00000000, 0x00000000, 0x00000000, 0x00000000);
+ verify(0x0000FFFF, 0x0000FFFF, 0x0000ffff, 0x0000ffff);
+ verify(0x50000000, 0x50000000, 0x10000001, 0x10000001);
+ verify(0xFFFFFFFF, 0xFFFFFFFF, 0x00000003, 0x00000003);
+ verify(3252345, 7149734, 0x0031a079, 0x006d18a6);
+});
+
+test(function myRnd() {
+ function verifySequence(seed1, seed2, expected){
+ var r = auth.randomInit(seed1, seed2);
+ for (var i = 0; i < expected.length; i++){
+ var n = auth.myRnd(r);
+
+ // we will test to 14 digits, since
+ // we only ever use this function mutliplied
+ // by small numbers anyway
+
+ var a = ':'+n;
+ var b = ':'+expected[i];
+
+ assert.equal(a.substr(1, 16), b.substr(1, 16));
+ }
+ }
+
+ verifySequence(3252345, 7149734, [
+ 0.0157456556481734,
+ 0.0696413620092360,
+ 0.3009698738353047,
+ 0.2959253138824602,
+ 0.5767169786400320,
+ 0.9958089822864243,
+ 0.2488940062456708,
+ 0.2570431151027261,
+ 0.5385335875102631,
+ 0.9215386229767824,
+ ]);
+});
+
+test(function scramble323() {
+ function verify(message, password, bytes){
+ var expected = new Buffer(bytes);
+ var actual = auth.scramble323(new Buffer(message), password);
+
+ assert.deepEqual(actual, expected);
+ }
+
+ verify('8bytesofstuff', 'root', [0x5a, 0x4d, 0x46, 0x47, 0x43, 0x53, 0x58, 0x5f]);
+ verify('e8cf00cec9ec825af22', 'saf789yasfbsd', [0x4d, 0x54, 0x5b, 0x47, 0x5f, 0x52, 0x4d, 0x45]);
+});
diff --git a/test/unit/legacy/test-client.js b/test/unit/legacy/test-client.js
new file mode 100644
index 0000000..4e31731
--- /dev/null
+++ b/test/unit/legacy/test-client.js
@@ -0,0 +1,95 @@
+var common = require('./common');
+var Parser = require(common.dir.lib + '/parser');
+var constants = require(common.dir.lib + '/constants');
+var Client = require(common.dir.lib + '/client');
+
+function test(test) {
+ client = new Client();
+ gently = new Gently();
+ test();
+ gently.verify(test.name);
+};
+
+test(function write() {
+ var PACKET = {buffer: []},
+ CONNECTION = client._socket = {};
+
+ gently.expect(CONNECTION, 'write', function(buffer) {
+ assert.strictEqual(buffer, PACKET.buffer);
+ });
+
+ client.write(PACKET);
+});
+
+test(function format() {
+ var sql = client.format('? + ? = ?', [1, 2, 'great']);
+ assert.equal(sql, '1 + 2 = \'great\'');
+
+ assert.throws(function() {
+ var sql = client.format('? + ? = ?', [1, 2]);
+ });
+
+ assert.throws(function() {
+ var sql = client.format('? + ? = ?', [1, 2, 3, 4]);
+ });
+});
+
+test(function escape() {
+ assert.equal(client.escape(undefined), 'NULL');
+ assert.equal(client.escape(null), 'NULL');
+ assert.equal(client.escape(false), 'false');
+ assert.equal(client.escape(true), 'true');
+ assert.equal(client.escape(5), '5');
+ assert.equal(client.escape({foo:'bar'}), "'[object Object]'");
+ assert.equal(client.escape([1,2,3]), "'1','2','3'");
+ assert.equal(client.escape(new Date(Date.UTC(2011,6,6,6,6,6,6))), "'2011-07-06T06:06:06.006Z'");
+
+ assert.equal(client.escape('Super'), "'Super'");
+ assert.equal(client.escape('Sup\0er'), "'Sup\\0er'");
+ assert.equal(client.escape('Sup\ber'), "'Sup\\ber'");
+ assert.equal(client.escape('Sup\ner'), "'Sup\\ner'");
+ assert.equal(client.escape('Sup\rer'), "'Sup\\rer'");
+ assert.equal(client.escape('Sup\ter'), "'Sup\\ter'");
+ assert.equal(client.escape('Sup\\er'), "'Sup\\\\er'");
+ assert.equal(client.escape('Sup\u001aer'), "'Sup\\Zer'");
+ assert.equal(client.escape('Sup\'er'), "'Sup\\'er'");
+ assert.equal(client.escape('Sup"er'), "'Sup\\\"er'");
+});
+
+test(function _packetToUserObject() {
+ (function testOkPacket() {
+ var PACKET = {
+ type: Parser.OK_PACKET,
+ length: 65,
+ received: 65,
+ number: 92,
+ foo: 'bar'
+ };
+
+ var ok = Client._packetToUserObject(PACKET);
+
+ assert.notStrictEqual(PACKET, ok);
+ assert.ok(!(ok instanceof Error));
+ assert.equal(ok.foo, PACKET.foo);
+ assert.equal(ok.type, undefined);
+ assert.equal(ok.length, undefined);
+ assert.equal(ok.received, undefined);
+ })();
+
+ (function testErrorPacket() {
+ var PACKET = {
+ type: Parser.ERROR_PACKET,
+ foo: 'bar',
+ errorMessage: 'oh no',
+ errorNumber: 1007
+ };
+
+ var err = Client._packetToUserObject(PACKET);
+
+ assert.ok(err instanceof Error);
+ assert.equal(err.message, 'oh no');
+ assert.equal(err.errorMessage, undefined);
+ assert.equal(err.number, 1007);
+ assert.equal(err.errorNumber, undefined);
+ })();
+});
diff --git a/test/unit/legacy/test-outgoing-packet.js b/test/unit/legacy/test-outgoing-packet.js
new file mode 100644
index 0000000..60cd4d7
--- /dev/null
+++ b/test/unit/legacy/test-outgoing-packet.js
@@ -0,0 +1,134 @@
+var common = require('./common');
+var OutgoingPacket = require(common.dir.lib + '/outgoing_packet'),
+ Buffer = require('buffer').Buffer;
+
+function test(test) {
+ gently = new Gently();
+ test();
+ gently.verify(test.name);
+}
+
+test(function constructor() {
+ var packet = new OutgoingPacket(10, 5);
+ assert.equal(packet.buffer.length, 14);
+ assert.deepEqual(
+ packet.buffer.slice(0, 3),
+ new Buffer([10, 0, 0])
+ );
+ assert.equal(packet.buffer[3], 5);
+ assert.equal(packet.index, 4);
+});
+
+test(function writeNumber() {
+ var packet = new OutgoingPacket(4);
+ packet.writeNumber(4, 257);
+ assert.deepEqual(
+ packet.buffer.slice(4, 8),
+ new Buffer([1, 1, 0, 0])
+ );
+});
+
+test(function writeFiller() {
+ var packet = new OutgoingPacket(5);
+ packet.writeFiller(5);
+ assert.equal(packet.index, 9);
+ assert.deepEqual(
+ packet.buffer.slice(4, 9),
+ new Buffer([0, 0, 0, 0, 0])
+ );
+});
+
+test(function write() {
+ (function testBuffer() {
+ var packet = new OutgoingPacket(3),
+ BUFFER = new Buffer([1, 2, 3]);
+
+ packet.write(BUFFER);
+ assert.equal(packet.index, 7);
+ assert.deepEqual(packet.buffer.slice(4, 7), BUFFER);
+ })();
+
+ (function testString() {
+ var packet = new OutgoingPacket(3),
+ STRING = 'abc';
+
+ packet.write(STRING);
+ assert.equal(packet.index, 7);
+ assert.equal(packet.buffer.slice(4, 7).toString(), STRING);
+ })();
+});
+
+test(function writeNullTerminated() {
+ var packet = new OutgoingPacket(4),
+ BUFFER = new Buffer([17, 23, 42]);
+
+ packet.buffer[7] = 100; // set last byte to non-0
+
+ gently.expect(packet, 'write', function(buffer) {
+ assert.strictEqual(buffer, BUFFER);
+ this.index += buffer.length;
+ });
+
+ packet.writeNullTerminated(BUFFER);
+ assert.equal(packet.buffer[7], 0);
+ assert.equal(packet.index, 8);
+});
+
+test(function writeLengthCoded() {
+ (function test1ByteNumber() {
+ var packet = new OutgoingPacket(1);
+ packet.writeLengthCoded(250);
+ assert.equal(packet.buffer[4], 250);
+ assert.equal(packet.index, 5);
+ })();
+
+ (function test2ByteNumber() {
+ var packet = new OutgoingPacket(6);
+ packet.writeLengthCoded(251);
+ assert.equal(packet.buffer[4], 252);
+ assert.equal(packet.buffer[5], 251);
+ assert.equal(packet.buffer[6], 0);
+ assert.equal(packet.index, 7);
+
+ packet.writeLengthCoded(257);
+ assert.equal(packet.buffer[7], 252);
+ assert.equal(packet.buffer[8], 1);
+ assert.equal(packet.buffer[9], 1);
+ assert.equal(packet.index, 10);
+ })();
+
+ (function test3ByteNumber() {
+ var packet = new OutgoingPacket(4);
+ packet.writeLengthCoded(Math.pow(256, 0) * 5 + Math.pow(256, 1) * 6 + Math.pow(256, 2) * 7);
+ assert.equal(packet.buffer[4], 253);
+ assert.equal(packet.buffer[5], 5);
+ assert.equal(packet.buffer[6], 6);
+ assert.equal(packet.buffer[7], 7);
+ assert.equal(packet.index, 8);
+ })();
+
+ (function testNull() {
+ var packet = new OutgoingPacket(1);
+ packet.writeLengthCoded(null);
+ assert.equal(packet.buffer[4], 251);
+ assert.equal(packet.index, 5);
+ })();
+
+ (function testBuffer() {
+ var packet = new OutgoingPacket(4),
+ BUFFER = new Buffer([17, 23, 42]);
+
+ packet.writeLengthCoded(BUFFER);
+ assert.equal(packet.buffer[4], 3);
+ assert.deepEqual(packet.buffer.slice(5, 8), BUFFER);
+ })();
+
+ (function testString() {
+ var packet = new OutgoingPacket(6),
+ STRING = 'über';
+
+ packet.writeLengthCoded(STRING);
+ assert.equal(packet.buffer[4], 5);
+ assert.equal(packet.buffer.slice(5, 10).toString(), STRING);
+ })();
+});
diff --git a/test/unit/legacy/test-parser.js b/test/unit/legacy/test-parser.js
new file mode 100644
index 0000000..6850ecb
--- /dev/null
+++ b/test/unit/legacy/test-parser.js
@@ -0,0 +1,387 @@
+var common = require('./common');
+var EventEmitter = require('events').EventEmitter,
+ Parser = require(common.dir.lib + '/parser'),
+ parser,
+ gently;
+
+function test(test) {
+ parser = new Parser();
+ gently = new Gently();
+ test();
+ gently.verify(test.name);
+}
+
+test(function constructor() {
+ assert.strictEqual(parser.state, Parser.PACKET_LENGTH);
+ assert.strictEqual(parser.packet, null);
+ assert.strictEqual(parser.greeted, false);
+ assert.strictEqual(parser.authenticated, false);
+ assert.strictEqual(parser.receivingFieldPackets, false);
+ assert.strictEqual(parser.receivingRowPackets, false);
+ assert.strictEqual(parser._lengthCodedLength, null);
+ assert.strictEqual(parser._lengthCodedStringLength, null);
+ assert.ok(parser instanceof EventEmitter);
+});
+
+test(function write() {
+ var packet;
+ (function testPacketLength() {
+ var LENGTH = 56;
+ parser.write(new Buffer([LENGTH]));
+
+ assert.equal(parser.state, Parser.PACKET_LENGTH);
+
+ packet = parser.packet;
+
+ assert.ok(packet instanceof EventEmitter);
+ assert.strictEqual(packet.number, 0);
+ assert.strictEqual(packet.length, LENGTH);
+
+ parser.write(new Buffer([0]));
+ parser.write(new Buffer([0]));
+ assert.strictEqual(
+ packet.length,
+ Math.pow(256, 0) * LENGTH + Math.pow(256, 1) * 0 + Math.pow(256, 2) * 0
+ );
+ })();
+
+ (function testPacketNumber() {
+ parser.write(new Buffer([42]));
+ assert.strictEqual(packet.number, 42);
+ assert.equal(parser.state, Parser.GREETING_PROTOCOL_VERSION);
+ })();
+
+ (function testGreetingErrorPacket() {
+ parser.write(new Buffer([0xff]));
+ assert.equal(packet.type, Parser.ERROR_PACKET);
+ assert.equal(parser.state, Parser.ERROR_NUMBER);
+
+ parser.write(new Buffer([5, 2]));
+ assert.equal(packet.errorNumber, Math.pow(256, 0) * 5 + Math.pow(256, 1) * 2);
+
+ parser.write(new Buffer('Hello World'));
+ assert.equal(packet.errorMessage, 'Hello World');
+
+ // Reset back to previous state
+ packet.type = Parser.GREETING_PACKET;
+ packet.received = 0;
+ parser.state = Parser.GREETING_PROTOCOL_VERSION;
+ })();
+
+ (function testGreetingPacket() {
+ parser.write(new Buffer([15]));
+ assert.equal(packet.type, Parser.GREETING_PACKET);
+ assert.equal(packet.protocolVersion, 15);
+ assert.equal(parser.state, Parser.GREETING_SERVER_VERSION);
+
+ var VERSION = 'MySql 5.1';
+ parser.write(new Buffer(VERSION+'\0', 'binary'));
+ assert.equal(packet.serverVersion, VERSION);
+ assert.equal(parser.state, Parser.GREETING_THREAD_ID);
+
+ parser.write(new Buffer([0, 0, 0, 1]));
+ assert.equal(packet.threadId, Math.pow(256, 3));
+
+ parser.write(new Buffer([1]));
+ assert.equal(packet.scrambleBuffer[0], 1);
+ assert.equal(packet.scrambleBuffer.length, 8 + 12);
+ assert.equal(parser.state, Parser.GREETING_SCRAMBLE_BUFF_1);
+
+ parser.write(new Buffer([2, 3, 4, 5, 6, 7, 8]));
+ assert.deepEqual(
+ packet.scrambleBuffer.slice(0, 8),
+ new Buffer([1, 2, 3, 4, 5, 6, 7, 8])
+ );
+ assert.equal(parser.state, Parser.GREETING_FILLER_1);
+
+ parser.write(new Buffer([0]));
+ assert.equal(parser.state, Parser.GREETING_SERVER_CAPABILITIES);
+
+ parser.write(new Buffer([0, 1]));
+ assert.equal(packet.serverCapabilities, Math.pow(256, 1));
+
+ parser.write(new Buffer([17]));
+ assert.equal(packet.serverLanguage, 17);
+ assert.equal(parser.state, Parser.GREETING_SERVER_STATUS);
+
+ parser.write(new Buffer([0, 1]));
+ assert.equal(packet.serverStatus, Math.pow(256, 1));
+
+ parser.write(new Buffer([0]));
+ assert.equal(parser.state, Parser.GREETING_FILLER_2);
+ parser.write(new Buffer(12));
+ assert.equal(parser.state, Parser.GREETING_SCRAMBLE_BUFF_2);
+
+ parser.write(new Buffer([9]));
+ assert.equal(packet.scrambleBuffer[8], 9);
+ assert.equal(parser.state, Parser.GREETING_SCRAMBLE_BUFF_2);
+
+ gently.expect(parser, 'emit', function(event, val) {
+ assert.equal(event, 'packet');
+ assert.ok(!('index' in val));
+ assert.strictEqual(val, packet);
+ assert.equal(parser.state, Parser.PACKET_LENGTH);
+ assert.equal(parser.greeted, true);
+ });
+
+ parser.write(new Buffer([10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 0]));
+ assert.deepEqual(
+ packet.scrambleBuffer.slice(9, 20),
+ new Buffer([10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20])
+ );
+ assert.strictEqual(parser.packet, null);
+ })();
+
+ (function testUseOldPasswordProtocolPacket() {
+ parser.write(new Buffer([1, 0, 0, 1]));
+
+ gently.expect(parser, 'emit', function(event, val) {
+ assert.equal(event, 'packet');
+ assert.equal(val.type, Parser.USE_OLD_PASSWORD_PROTOCOL_PACKET);
+ });
+
+ parser.write(new Buffer([254]));
+ })();
+
+
+ (function testErrorPacket() {
+ parser.write(new Buffer([12, 0, 0, 1]));
+ assert.equal(parser.state, Parser.FIELD_COUNT);
+ var packet = parser.packet;
+
+ parser.write(new Buffer([0xff]));
+ assert.equal(packet.type, Parser.ERROR_PACKET);
+ assert.equal(parser.state, Parser.ERROR_NUMBER);
+
+ parser.write(new Buffer([5, 2]));
+ assert.equal(packet.errorNumber, Math.pow(256, 0) * 5 + Math.pow(256, 1) * 2);
+
+ parser.write(new Buffer('#'));
+ assert.equal(packet.sqlStateMarker, '#');
+ assert.equal(parser.state, Parser.ERROR_SQL_STATE);
+
+ parser.write(new Buffer('abcde'));
+ assert.equal(packet.sqlState, 'abcde');
+
+ parser.write(new Buffer('er'));
+ assert.equal(parser.state, Parser.ERROR_MESSAGE);
+ assert.equal(packet.errorMessage, 'er');
+
+ gently.expect(parser, 'emit', function(event, val) {
+ assert.equal(event, 'packet');
+ assert.equal(packet.errorMessage, 'err');
+ });
+
+ parser.write(new Buffer('r'));
+ })();
+
+ (function testOkPacket() {
+ parser.write(new Buffer([15, 0, 0, 1]));
+ var packet = parser.packet;
+
+ parser.write(new Buffer([0x00]));
+ assert.equal(packet.type, Parser.OK_PACKET);
+ assert.equal(parser.authenticated, true);
+ assert.equal(parser.state, Parser.AFFECTED_ROWS);
+
+ parser.write(new Buffer([252, 17, 23]));
+ assert.equal(packet.affectedRows, Math.pow(256, 0) * 17 + Math.pow(256, 1) * 23);
+
+ parser.write(new Buffer([240]));
+ assert.equal(packet.insertId, 240);
+
+ parser.write(new Buffer([42, 113]));
+ assert.equal(packet.serverStatus, Math.pow(256, 0) * 42 + Math.pow(256, 1) * 113);
+
+ parser.write(new Buffer([32, 153]));
+ assert.equal(packet.warningCount, Math.pow(256, 0) * 32 + Math.pow(256, 1) * 153);
+
+ assert.strictEqual(packet.message, '');
+
+ gently.expect(parser, 'emit', function(event, val) {
+ assert.equal(event, 'packet');
+ assert.equal(packet.message, 'abcdef');
+ });
+
+ parser.write(new Buffer('abcdef'));
+ })();
+
+ (function testResultHeaderPacket() {
+ parser.write(new Buffer([1, 0, 0, 1]));
+ var packet = parser.packet;
+
+ gently.expect(parser, 'emit', function(event, val) {
+ assert.equal(event, 'packet');
+ assert.equal(val.type, Parser.RESULT_SET_HEADER_PACKET);
+ assert.equal(val.fieldCount, 5);
+ });
+
+ parser.write(new Buffer([5]));
+ })();
+
+ (function testResultHeaderPacketWithExtra() {
+ parser.receivingFieldPackets = false;
+
+ parser.write(new Buffer([5, 0, 0, 1]));
+ var packet = parser.packet;
+
+ parser.write(new Buffer([23]));
+ assert.equal(parser.state, Parser.EXTRA_LENGTH);
+ assert.equal(packet.fieldCount, 23);
+
+ parser.write(new Buffer([3]));
+
+ gently.expect(parser, 'emit', function(event, val) {
+ assert.equal(event, 'packet');
+ assert.equal(val.type, Parser.RESULT_SET_HEADER_PACKET);
+ assert.equal(val.extra, 'abc');
+ });
+
+ parser.write(new Buffer('abc'));
+ })();
+
+ (function testFieldPacket() {
+ parser.write(new Buffer([43, 0, 0, 1]));
+ var packet = parser.packet;
+
+ assert.equal(parser.state, Parser.FIELD_CATALOG_LENGTH);
+ parser.write(new Buffer([3]));
+ assert.equal(packet.type, Parser.FIELD_PACKET);
+ parser.write(new Buffer('abc'));
+ assert.equal(packet.catalog, 'abc');
+
+ assert.equal(parser.state, Parser.FIELD_DB_LENGTH);
+ parser.write(new Buffer([5]));
+ parser.write(new Buffer('hello'));
+ assert.equal(packet.db, 'hello');
+
+ assert.equal(parser.state, Parser.FIELD_TABLE_LENGTH);
+ parser.write(new Buffer([2]));
+ parser.write(new Buffer('ab'));
+ assert.equal(packet.table, 'ab');
+
+ assert.equal(parser.state, Parser.FIELD_ORIGINAL_TABLE_LENGTH);
+ parser.write(new Buffer([4]));
+ parser.write(new Buffer('1234'));
+ assert.equal(packet.originalTable, '1234');
+
+ assert.equal(parser.state, Parser.FIELD_NAME_LENGTH);
+ parser.write(new Buffer([1]));
+ parser.write(new Buffer('o'));
+ assert.equal(packet.name, 'o');
+
+ assert.equal(parser.state, Parser.FIELD_ORIGINAL_NAME_LENGTH);
+ parser.write(new Buffer([9]));
+ parser.write(new Buffer('wonderful'));
+ assert.equal(packet.originalName, 'wonderful');
+
+ assert.equal(parser.state, Parser.FIELD_FILLER_1);
+ parser.write(new Buffer([0]));
+
+ assert.equal(parser.state, Parser.FIELD_CHARSET_NR);
+ parser.write(new Buffer([42, 113]));
+ assert.equal(packet.charsetNumber, Math.pow(256, 0) * 42 + Math.pow(256, 1) * 113);
+
+ assert.equal(parser.state, Parser.FIELD_LENGTH);
+ parser.write(new Buffer([42, 113, 50, 30]));
+ assert.equal(packet.fieldLength, 42 + (256 * 113) + (256 * 256) * 50 + (256 * 256 * 256 * 30));
+
+ assert.equal(parser.state, Parser.FIELD_TYPE);
+ parser.write(new Buffer([58]));
+ assert.equal(packet.fieldType, 58);
+
+ assert.equal(parser.state, Parser.FIELD_FLAGS);
+ parser.write(new Buffer([42, 113]));
+ assert.equal(packet.flags, Math.pow(256, 0) * 42 + Math.pow(256, 1) * 113);
+
+ assert.equal(parser.state, Parser.FIELD_DECIMALS);
+ parser.write(new Buffer([58]));
+ assert.equal(packet.decimals, 58);
+
+ gently.expect(parser, 'emit', function(event, val) {
+ assert.equal(event, 'packet');
+ });
+
+ assert.equal(parser.state, Parser.FIELD_FILLER_2);
+ parser.write(new Buffer([0, 0]));
+ })();
+
+ (function testEofPacket() {
+ parser.write(new Buffer([5, 0, 0, 1]));
+ var packet = parser.packet;
+
+ parser.write(new Buffer([0xfe]));
+ assert.equal(packet.type, Parser.EOF_PACKET);
+
+ assert.equal(parser.state, Parser.EOF_WARNING_COUNT);
+ parser.write(new Buffer([42, 113]));
+ assert.equal(packet.warningCount, Math.pow(256, 0) * 42 + Math.pow(256, 1) * 113);
+
+ gently.expect(parser, 'emit', function(event, val) {
+ assert.equal(event, 'packet');
+ assert.equal(parser.receivingFieldPackets, false);
+ assert.equal(parser.receivingRowPackets, true);
+ });
+
+ assert.equal(parser.state, Parser.EOF_SERVER_STATUS);
+ parser.write(new Buffer([42, 113]));
+ assert.equal(packet.serverStatus, Math.pow(256, 0) * 42 + Math.pow(256, 1) * 113);
+ })();
+
+ (function testRowPacket() {
+ parser.write(new Buffer([23, 0, 0, 1]));
+ var packet = parser.packet;
+
+ gently.expect(parser, 'emit', function(event, val) {
+ assert.equal(event, 'packet');
+ });
+
+ parser.write(new Buffer([16]));
+ assert.equal(parser.state, Parser.COLUMN_VALUE_STRING);
+ assert.equal(packet.type, Parser.ROW_DATA_PACKET);
+ assert.equal(packet.columnLength, 16);
+
+ gently.expect(packet, 'emit', function(event, val, remaining) {
+ assert.equal(event, 'data');
+ assert.equal(val.toString(), 'hi, ');
+ assert.equal(remaining, 12);
+ });
+
+ parser.write(new Buffer('hi, '));
+
+ gently.expect(packet, 'emit', function(event, val, remaining) {
+ assert.equal(event, 'data');
+ assert.equal(val.toString(), 'how');
+ assert.equal(remaining, 9);
+ });
+
+ parser.write(new Buffer('how'));
+
+ gently.expect(packet, 'emit', function(event, val, remaining) {
+ assert.equal(event, 'data');
+ assert.equal(val.toString(), ' are you?');
+ assert.equal(remaining, 0);
+ });
+
+ gently.expect(packet, 'emit', function(event, val, remaining) {
+ assert.equal(event, 'data');
+ assert.equal(val.toString(), 'Fine!');
+ assert.equal(remaining, 0);
+ assert.equal(packet.index, 0);
+ });
+
+ parser.write(new Buffer(' are you?\u0005Fine!'));
+
+ assert.equal(parser.packet, null);
+ assert.equal(parser.state, Parser.PACKET_LENGTH);
+ })();
+
+ (function testEofPacketAfterRowPacket() {
+ parser.write(new Buffer([5, 0, 0, 1]));
+ var packet = parser.packet;
+
+ parser.write(new Buffer([0xfe]));
+ assert.equal(packet.type, Parser.EOF_PACKET);
+ assert.equal(parser.receivingRowPackets, false);
+ })();
+});
diff --git a/test/unit/legacy/test-query.js b/test/unit/legacy/test-query.js
new file mode 100644
index 0000000..9559eef
--- /dev/null
+++ b/test/unit/legacy/test-query.js
@@ -0,0 +1,68 @@
+var common = require('./common');
+var Query = require(common.dir.lib + '/query');
+var EventEmitter = require('events').EventEmitter;
+var Parser = require(common.dir.lib + '/parser');
+var query;
+var gently;
+
+function test(test) {
+ query = new Query();
+ gently = new Gently();
+ test();
+ gently.verify(test.name);
+}
+
+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');
+});
+
+test(function _handlePacket() {
+ function typeCast(type, strValue) {
+ query._fields = [{name: 'my_field', fieldType: type}];
+
+ var PACKET = new EventEmitter(), r;
+ PACKET.type = Parser.ROW_DATA_PACKET;
+
+ gently.expect(PACKET, 'on', function (event, fn) {
+ assert.equal(event, 'data');
+
+ gently.expect(query, 'emit', function (event, row) {
+ assert.equal(event, 'row');
+ r = row.my_field;
+ });
+
+ var val = (strValue === null)
+ ? null
+ : new Buffer(strValue);
+
+ fn(val, 0);
+ });
+
+ query._handlePacket(PACKET);
+ return r;
+ }
+
+ assert.deepEqual(typeCast(Query.FIELD_TYPE_TIMESTAMP, '2010-10-05 06:23:42 UTC'), new Date('2010-10-05 06:23:42Z'));
+
+ assert.deepEqual(typeCast(Query.FIELD_TYPE_TIMESTAMP, '2010-10-05 UTC'), new Date('2010-10-05Z'));
+ assert.deepEqual(typeCast(Query.FIELD_TYPE_DATE, '2010-10-05 UTC'), new Date('2010-10-05Z'));
+ assert.deepEqual(typeCast(Query.FIELD_TYPE_DATETIME, '2010-10-05 UTC'), new Date('2010-10-05Z'));
+ assert.deepEqual(typeCast(Query.FIELD_TYPE_NEWDATE, '2010-10-05 UTC'), new Date('2010-10-05Z'));
+
+ assert.strictEqual(typeCast(Query.FIELD_TYPE_TINY, '08'), 8);
+ assert.strictEqual(typeCast(Query.FIELD_TYPE_SHORT, '08'), 8);
+ assert.strictEqual(typeCast(Query.FIELD_TYPE_LONG, '08'), 8);
+ assert.strictEqual(typeCast(Query.FIELD_TYPE_LONGLONG, '08'), 8);
+ assert.strictEqual(typeCast(Query.FIELD_TYPE_INT24, '08'), 8);
+ assert.strictEqual(typeCast(Query.FIELD_TYPE_YEAR, '08'), 8);
+
+ assert.strictEqual(typeCast(Query.FIELD_TYPE_DECIMAL, '2.8'), '2.8');
+ assert.strictEqual(typeCast(Query.FIELD_TYPE_FLOAT, '2.8'), 2.8);
+ assert.strictEqual(typeCast(Query.FIELD_TYPE_DOUBLE, '2.8'), 2.8);
+ assert.strictEqual(typeCast(Query.FIELD_TYPE_NEWDECIMAL, '2.8'), '2.8');
+
+ assert.strictEqual(typeCast(Query.FIELD_TYPE_DATE, null), null);
+});
diff --git a/test/unit/test-client.js b/test/unit/test-client.js
new file mode 100644
index 0000000..a324b0f
--- /dev/null
+++ b/test/unit/test-client.js
@@ -0,0 +1,33 @@
+var common = require('../common');
+var assert = require('assert');
+var test = require('utest');
+var Client = require(common.dir.lib + '/client');
+
+var client;
+test('Client', {
+ before: function() {
+ client = new Client();
+ },
+
+ '#format() does not manipulate params parameter': function() {
+ var sql = '?';
+ var params = [1];
+
+ client.format(sql, params);
+ assert.equal(params.length, 1);
+ },
+
+ '#format() does not quote floats': function() {
+ var params = [1.23];
+
+ var sql = client.format('?', params);
+ assert.strictEqual(sql, '1.23');
+ },
+
+ 'Timeout reconnect works with empty queue': function() {
+ // A non-error packet
+ var packet = {};
+ // This must not throw an error
+ client._handlePacket(packet);
+ },
+});
diff --git a/test/unit/test-mysql.js b/test/unit/test-mysql.js
new file mode 100644
index 0000000..a77409b
--- /dev/null
+++ b/test/unit/test-mysql.js
@@ -0,0 +1,10 @@
+var common = require('../common');
+var assert = require('assert');
+var test = require('utest');
+var mysql = require(common.dir.root);
+
+test('mysql module', {
+ 'Package JSON is exported': function() {
+ assert.strictEqual(mysql.PACKAGE.name, 'mysql');
+ },
+});
diff --git a/tool/pcap-mysql.js b/tool/pcap-mysql.js
new file mode 100755
index 0000000..7810b0e
--- /dev/null
+++ b/tool/pcap-mysql.js
@@ -0,0 +1,48 @@
+#!/usr/bin/env node
+
+var sys = require("sys"),
+ pcap = require("pcap"),
+ mysqlPort = parseInt(process.argv[3]) || 3306,
+ pcap_session = pcap.createSession(process.argv[2] || '', 'tcp port '+mysqlPort);
+
+sys.puts('This tool allows to reverse engineer the mysql procotocol using node-pcap.');
+sys.puts('');
+sys.puts('Available devices (active one is denoted by *):');
+
+// Print all devices, currently listening device prefixed with an asterisk
+pcap_session.findalldevs().forEach(function (dev) {
+ sys.print(' ');
+ if (pcap_session.device_name === dev.name) {
+ sys.print("* ");
+ }
+ sys.print(dev.name + " ");
+ if (dev.addresses.length > 0) {
+ dev.addresses.forEach(function (address) {
+ sys.print(address.addr + "/" + address.netmask);
+ });
+ sys.print("\n");
+ } else {
+ sys.print("no address\n");
+ }
+});
+
+sys.puts('');
+sys.puts('Execute `./pcap-mysql.js <device> <mysql-port>` to listen on another device.');
+sys.puts('');
+
+// Listen for packets, decode them, and feed the simple printer. No tricks.
+pcap_session.on('packet', function (raw_packet) {
+ var packet = pcap.decode.packet(raw_packet);
+ //sys.puts(pcap.print.packet(packet));
+ var tcp = packet.link.ip.tcp;
+ if (!tcp.data) {
+ return;
+ }
+
+ if (tcp.sport == mysqlPort) {
+ sys.puts('<- '+tcp.data.inspect());
+ } else {
+ sys.puts('-> '+tcp.data.inspect());
+ }
+});
+
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/node-mysql.git
More information about the Pkg-javascript-commits
mailing list