[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