Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Initial implementation.

  • Loading branch information...
commit 733b5742108cfe50b7dd201458a762bbd4273d50 1 parent 9abb16b
Roel van Uden authored
Showing with 7,374 additions and 0 deletions.
  1. +1 −0  index.js
  2. +206 −0 lib/pool.js
  3. +5 −0 node_modules/mysql/.npmignore
  4. +5 −0 node_modules/mysql/.travis.yml
  5. +193 −0 node_modules/mysql/Changes.md
  6. +19 −0 node_modules/mysql/License
  7. +4 −0 node_modules/mysql/Makefile
  8. +759 −0 node_modules/mysql/Readme.md
  9. +96 −0 node_modules/mysql/benchmark/analyze.js
  10. +112 −0 node_modules/mysql/benchmark/parse-100k-blog-rows.js
  11. +42 −0 node_modules/mysql/benchmark/select-100k-blog-rows.js
  12. +12 −0 node_modules/mysql/index.js
  13. +155 −0 node_modules/mysql/lib/Connection.js
  14. +111 −0 node_modules/mysql/lib/ConnectionConfig.js
  15. +165 −0 node_modules/mysql/lib/protocol/Auth.js
  16. +5 −0 node_modules/mysql/lib/protocol/PacketHeader.js
  17. +197 −0 node_modules/mysql/lib/protocol/PacketWriter.js
  18. +366 −0 node_modules/mysql/lib/protocol/Parser.js
  19. +292 −0 node_modules/mysql/lib/protocol/Protocol.js
  20. +7 −0 node_modules/mysql/lib/protocol/ResultSet.js
  21. +136 −0 node_modules/mysql/lib/protocol/SqlString.js
  22. +128 −0 node_modules/mysql/lib/protocol/constants/charsets.js
  23. +26 −0 node_modules/mysql/lib/protocol/constants/client.js
  24. +725 −0 node_modules/mysql/lib/protocol/constants/errors.js
  25. +18 −0 node_modules/mysql/lib/protocol/constants/field_flags.js
  26. +39 −0 node_modules/mysql/lib/protocol/constants/server_status.js
  27. +29 −0 node_modules/mysql/lib/protocol/constants/types.js
  28. +32 −0 node_modules/mysql/lib/protocol/packets/ClientAuthenticationPacket.js
  29. +25 −0 node_modules/mysql/lib/protocol/packets/ComChangeUserPacket.js
  30. +12 −0 node_modules/mysql/lib/protocol/packets/ComPingPacket.js
  31. +15 −0 node_modules/mysql/lib/protocol/packets/ComQueryPacket.js
  32. +7 −0 node_modules/mysql/lib/protocol/packets/ComQuitPacket.js
  33. +12 −0 node_modules/mysql/lib/protocol/packets/ComStatisticsPacket.js
  34. +6 −0 node_modules/mysql/lib/protocol/packets/EmptyPacket.js
  35. +20 −0 node_modules/mysql/lib/protocol/packets/EofPacket.js
  36. +35 −0 node_modules/mysql/lib/protocol/packets/ErrorPacket.js
  37. +31 −0 node_modules/mysql/lib/protocol/packets/Field.js
  38. +65 −0 node_modules/mysql/lib/protocol/packets/FieldPacket.js
  39. +81 −0 node_modules/mysql/lib/protocol/packets/HandshakeInitializationPacket.js
  40. +8 −0 node_modules/mysql/lib/protocol/packets/LocalDataFilePacket.js
  41. +34 −0 node_modules/mysql/lib/protocol/packets/OkPacket.js
  42. +15 −0 node_modules/mysql/lib/protocol/packets/OldPasswordPacket.js
  43. +25 −0 node_modules/mysql/lib/protocol/packets/ResultSetHeaderPacket.js
  44. +89 −0 node_modules/mysql/lib/protocol/packets/RowDataPacket.js
  45. +20 −0 node_modules/mysql/lib/protocol/packets/StatisticsPacket.js
  46. +14 −0 node_modules/mysql/lib/protocol/packets/UseOldPasswordPacket.js
  47. +4 −0 node_modules/mysql/lib/protocol/packets/index.js
  48. +41 −0 node_modules/mysql/lib/protocol/sequences/ChangeUser.js
  49. +65 −0 node_modules/mysql/lib/protocol/sequences/Handshake.js
  50. +14 −0 node_modules/mysql/lib/protocol/sequences/Ping.js
  51. +162 −0 node_modules/mysql/lib/protocol/sequences/Query.js
  52. +13 −0 node_modules/mysql/lib/protocol/sequences/Quit.js
  53. +83 −0 node_modules/mysql/lib/protocol/sequences/Sequence.js
  54. +23 −0 node_modules/mysql/lib/protocol/sequences/Statistics.js
  55. +4 −0 node_modules/mysql/lib/protocol/sequences/index.js
  56. BIN  node_modules/mysql/node_modules/require-all/.Readme.md.un~
  57. BIN  node_modules/mysql/node_modules/require-all/.index.js.un~
  58. +16 −0 node_modules/mysql/node_modules/require-all/Readme.md
  59. +25 −0 node_modules/mysql/node_modules/require-all/index.js
  60. +22 −0 node_modules/mysql/node_modules/require-all/package.json
  61. BIN  node_modules/mysql/node_modules/require-all/test/.test.js.un~
  62. +4 −0 node_modules/mysql/node_modules/require-all/test/controllers/main-Controller.js
  63. +1 −0  node_modules/mysql/node_modules/require-all/test/controllers/notthis.js
  64. +2 −0  node_modules/mysql/node_modules/require-all/test/controllers/other-Controller.js
  65. +1 −0  node_modules/mysql/node_modules/require-all/test/mydir/foo.js
  66. +2 −0  node_modules/mysql/node_modules/require-all/test/mydir/hello.js
  67. BIN  node_modules/mysql/node_modules/require-all/test/mydir/sub/.config.json.un~
  68. +4 −0 node_modules/mysql/node_modules/require-all/test/mydir/sub/config.json
  69. +1 −0  node_modules/mysql/node_modules/require-all/test/mydir/sub/no.2js
  70. +1 −0  node_modules/mysql/node_modules/require-all/test/mydir/sub/yes.js
  71. +38 −0 node_modules/mysql/node_modules/require-all/test/test.js
  72. +32 −0 node_modules/mysql/package.json
  73. +147 −0 node_modules/mysql/test/FakeServer.js
  74. +55 −0 node_modules/mysql/test/common.js
  75. +3 −0  node_modules/mysql/test/fixtures/data.csv
  76. +27 −0 node_modules/mysql/test/integration/connection/test-bad-credentials.js
  77. +20 −0 node_modules/mysql/test/integration/connection/test-callback-errors-are-not-caught.js
  78. +24 −0 node_modules/mysql/test/integration/connection/test-change-user-fatal-error.js
  79. +36 −0 node_modules/mysql/test/integration/connection/test-change-user.js
  80. +39 −0 node_modules/mysql/test/integration/connection/test-connection-config-flags-affected-rows.js
  81. +53 −0 node_modules/mysql/test/integration/connection/test-connection-config-flags.js
  82. +9 −0 node_modules/mysql/test/integration/connection/test-connection-destroy.js
  83. +18 −0 node_modules/mysql/test/integration/connection/test-custom-query-format.js
  84. +46 −0 node_modules/mysql/test/integration/connection/test-custom-typecast.js
  85. +45 −0 node_modules/mysql/test/integration/connection/test-destroy-while-streaming-rows.js
  86. +24 −0 node_modules/mysql/test/integration/connection/test-double-connect.js
  87. +20 −0 node_modules/mysql/test/integration/connection/test-drain-event.js
  88. +27 −0 node_modules/mysql/test/integration/connection/test-end-callback.js
  89. +7 −0 node_modules/mysql/test/integration/connection/test-escape-id.js
  90. +66 −0 node_modules/mysql/test/integration/connection/test-exception-safety.js
  91. +25 −0 node_modules/mysql/test/integration/connection/test-fatal-auth-error-going-to-all-callbacks.js
  92. +20 −0 node_modules/mysql/test/integration/connection/test-fatal-auth-error-without-handlers.js
  93. +21 −0 node_modules/mysql/test/integration/connection/test-fatal-network-error-going-to-all-callbacks.js
  94. +17 −0 node_modules/mysql/test/integration/connection/test-fatal-network-error-without-handlers.js
  95. +19 −0 node_modules/mysql/test/integration/connection/test-fatal-query-error-without-callback.js
  96. +26 −0 node_modules/mysql/test/integration/connection/test-host-denied-error.js
  97. +16 −0 node_modules/mysql/test/integration/connection/test-implicit-connect.js
  98. +26 −0 node_modules/mysql/test/integration/connection/test-insert-results.js
  99. +55 −0 node_modules/mysql/test/integration/connection/test-load-data-infile.js
  100. +12 −0 node_modules/mysql/test/integration/connection/test-long-stack-traces-for-connection-errors.js
  101. +16 −0 node_modules/mysql/test/integration/connection/test-long-stack-traces.js
  102. +55 −0 node_modules/mysql/test/integration/connection/test-multiple-statements-streaming.js
  103. +36 −0 node_modules/mysql/test/integration/connection/test-multiple-statements-with-error.js
  104. +31 −0 node_modules/mysql/test/integration/connection/test-multiple-statements.js
  105. +48 −0 node_modules/mysql/test/integration/connection/test-nested-tables-query.js
  106. +19 −0 node_modules/mysql/test/integration/connection/test-normal-error-without-handler.js
  107. +19 −0 node_modules/mysql/test/integration/connection/test-normal-query-error-without-callback.js
  108. +35 −0 node_modules/mysql/test/integration/connection/test-old-password.js
  109. +15 −0 node_modules/mysql/test/integration/connection/test-ping.js
  110. +40 −0 node_modules/mysql/test/integration/connection/test-procedure-with-multiple-selects.js
  111. +33 −0 node_modules/mysql/test/integration/connection/test-procedure-with-single-select.js
  112. +17 −0 node_modules/mysql/test/integration/connection/test-query-after-destroy.js
  113. +23 −0 node_modules/mysql/test/integration/connection/test-query-after-end-without-callback.js
  114. +23 −0 node_modules/mysql/test/integration/connection/test-query-after-end.js
  115. +36 −0 node_modules/mysql/test/integration/connection/test-query-escaping.js
  116. +21 −0 node_modules/mysql/test/integration/connection/test-select-1.js
  117. +16 −0 node_modules/mysql/test/integration/connection/test-select-empty-string.js
  118. +81 −0 node_modules/mysql/test/integration/connection/test-send-and-receive-large-packets.js
  119. +24 −0 node_modules/mysql/test/integration/connection/test-server-timeout-disconnect.js
  120. +36 −0 node_modules/mysql/test/integration/connection/test-server-unexpected-disconnect.js
  121. +26 −0 node_modules/mysql/test/integration/connection/test-statistics.js
  122. +46 −0 node_modules/mysql/test/integration/connection/test-streaming-rows-quick-pause-resume.js
  123. +65 −0 node_modules/mysql/test/integration/connection/test-streaming-rows.js
  124. +67 −0 node_modules/mysql/test/integration/connection/test-timezones.js
  125. +41 −0 node_modules/mysql/test/integration/connection/test-transaction-commit.js
  126. +41 −0 node_modules/mysql/test/integration/connection/test-transaction-rollback.js
  127. +34 −0 node_modules/mysql/test/integration/connection/test-type-cast-null-fields.js
  128. +37 −0 node_modules/mysql/test/integration/connection/test-type-cast-query.js
  129. +118 −0 node_modules/mysql/test/integration/connection/test-type-casting.js
  130. +32 −0 node_modules/mysql/test/integration/connection/test-unix-domain-socket.js
  131. +39 −0 node_modules/mysql/test/integration/connection/test-zerofill-results.js
  132. +7 −0 node_modules/mysql/test/run.js
  133. +125 −0 node_modules/mysql/test/unit/protocol/test-Parser.js
  134. +132 −0 node_modules/mysql/test/unit/protocol/test-SqlString.js
  135. +29 −0 node_modules/mysql/test/unit/test-ConnectionConfig.js
  136. +28 −0 node_modules/mysql/tool/generate-error-constants.js
  137. +23 −0 package.json
1  index.js
View
@@ -0,0 +1 @@
+module.exports = require('./lib/pool.js');
206 lib/pool.js
View
@@ -0,0 +1,206 @@
+var mysql = require('mysql');
+
+/**
+ * Represents the class managing a MySQL connection pool for node-mysql. The
+ * connection pool accepts an options object which is passed to the node-mysql
+ * createConnection function to establish a connection. A maximum number of
+ * connections can be configured.
+ *
+ * @param maximumNumberOfConnections The maximum number of connections.
+ * @param options The options with which a connection is created.
+ */
+function Pool(maximumNumberOfConnections, options) {
+ // Initialize public properties.
+ if (true) {
+ // The maximum number of connections.
+ this.maximumNumberOfConnections = maximumNumberOfConnections ? maximumNumberOfConnections : 100;
+ // The options with which a connection is created.
+ this.options = options ? options : {};
+ }
+ // Initialize private properties.
+ if (true) {
+ // The current number of connections being established.
+ this._currentNumberOfConnectionsEstablishing = 0;
+ // The current number of _connections.
+ this._currentNumberOfConnections = 0;
+ // The established _connections.
+ this._connections = [];
+ // Indicates whether the pool has been disposed of.
+ this._disposed = false;
+ // The _pending operations.
+ this._pending = [];
+ }
+}
+
+/**
+ * Claim a managed connection. A claimed connection is not managed by the pool until
+ * the connection is rebound. Once the caller has finished using the connection, rebound
+ * it using the end function on the connection. This function makes it possible for a
+ * transaction to function as intended.
+ *
+ * @param fn The callback function.
+ */
+Pool.prototype.claim = function(fn) {
+ // Check if the pool has not been disposed of.
+ if (!this._disposed) {
+ // Check if the connection pool has exhausted each connection.
+ if (this._connections.length === 0) {
+ // Push the claim to the pending operations.
+ this._pending.push({claiming: true, fn: fn});
+ // Update the connection pool
+ this._update();
+ }
+ // Otherwise a connection is available.
+ else {
+ // Retrieve a connection.
+ var connection = Pool._connections.pop();
+ // Send the connection to the callback function.
+ fn(null, connection);
+ }
+ }
+};
+
+/**
+ * Dispose of the connection pool. Further queries are ignored, but all pending
+ * operations are handled. Once the pending operations have been finished, the
+ * connections are removed.
+ */
+Pool.prototype.dispose = function() {
+ // Check if the pool has not been disposed of.
+ if (!this._disposed) {
+ // Set the status indicating whether the pool has been disposed of.
+ this._disposed = true;
+ }
+};
+
+/**
+ * Execute a query. This will add the operation to the pending operations and instructs
+ * the connection pool to find an available connection. When a connection is not available,
+ * a connection is established. If the maximum number of connections has been reached, the
+ * operation will be pending until a connection is returned.
+ *
+ * @param query The query to execute.
+ * @param options The options
+ * @param fn The callback function.
+ */
+Pool.prototype.query = function(query, options, fn) {
+ // Check if the pool has not been disposed of.
+ if (!this._disposed) {
+ // Check if the options variable is a function.
+ if (typeof options == 'function') {
+ // Set the callback to the options.
+ fn = options;
+ // Initialize the options.
+ options = {};
+ }
+ // Push the query to the pending operations.
+ this._pending.push({claiming: false, query: query, options: options, fn: fn});
+ // Update the connection pool and pending queries.
+ this._update();
+ }
+};
+
+/**
+ * Create a managed connection. A managed connection has an event handler to detect
+ * connection errors and changes the termination behaviour. Once the managed connection
+ * has been established, it is added to the connection pool.
+ *
+ * @return Indicates whether a connection is being established.
+ */
+Pool.prototype._create = function() {
+ // Check if a connection may be established.
+ if (this._currentNumberOfConnections + this._currentNumberOfConnectionsEstablishing < this.maximumNumberOfConnections) {
+ // Create a connection.
+ var connection = mysql.createConnection(this.options);
+ // Retrieve the pool instance.
+ var pool = this;
+ // Increment the current number of connections being established.
+ this._currentNumberOfConnectionsEstablishing++;
+ // Connect to the database.
+ connection.connect(function(err) {
+ // Decrement the current number of connections being established.
+ pool._currentNumberOfConnectionsEstablishing--;
+ // Check if the connection has been established
+ if (!err) {
+ // Increment the current number of connections.
+ pool._currentNumberOfConnections++;
+ // Attach a handler to the error event.
+ connection.on('error', function(err) {
+ // Check if the connection has been lost.
+ if (err.fatal && err.code !== 'PROTOCOL_CONNECTION_LOST') {
+ // Decrement the current number of _connections.
+ pool._currentNumberOfConnections--;
+ }
+ });
+ // Save the terminate function in case we want to dispose.
+ connection._end = connection.end;
+ // Change the behaviour of the termination of the connection.
+ connection.end = function() {
+ // Add the connection to the established _connections.
+ pool._connections.push(this);
+ // Update the connection pool and _pending queries.
+ pool._update();
+ };
+ // Rebound a managed connection.
+ connection.end();
+ }
+ // Otherwise the connection failed.
+ else {
+ // Update the connection pool.
+ pool._update();
+ }
+ });
+ // Return true.
+ return true;
+ }
+ // Otherwise return false.
+ else return false;
+};
+
+/**
+ * Update the connection pool. This method is called whenever a change in the
+ * connection pool has occured, handles pending operations and establishes
+ * connections.
+ */
+Pool.prototype._update = function() {
+ // Check if a _pending query is available.
+ if (this._pending.length !== 0) {
+ // Check if a connection is available.
+ if (this._connections.length !== 0) {
+ // Retrieve a connection.
+ var connection = this._connections.pop();
+ // Retrieve a _pending query.
+ var pending = this._pending.pop();
+ // Execute the query using this handler to rebound the connection.
+ connection.query(pending.query, pending.options, function(err, results) {
+ // Rebound a managed connection.
+ connection.end();
+ // Send the error and results to the callback function.
+ pending.fn(err, results);
+ });
+ }
+ // Otherwise a connection may have to be established.
+ else {
+ // Iterate until sufficient managed _connections are establishing.
+ while (this._currentNumberOfConnectionsEstablishing < this._pending.length) {
+ // Create a managed connection.
+ if (!this._create()) {
+ break;
+ }
+ }
+ }
+ }
+ // Otherwise check if the pool has been disposed of.
+ else if (this._disposed) {
+ // Iterate through each connection.
+ for (var i = 0; i < this._connections.length; i++) {
+ // Terminate the connection.
+ this._connections[i]._end();
+ }
+ // Clear connections.
+ this._connections.length = 0;
+ }
+};
+
+// Export the Pool class.
+module.exports = Pool;
5 node_modules/mysql/.npmignore
View
@@ -0,0 +1,5 @@
+*.un~
+
+/node_modules
+
+*.sublime-*
5 node_modules/mysql/.travis.yml
View
@@ -0,0 +1,5 @@
+language: node_js
+node_js:
+ - 0.4
+ - 0.6
+ - 0.8
193 node_modules/mysql/Changes.md
View
@@ -0,0 +1,193 @@
+# Changes
+
+This file is a manually maintained list of changes for each release. Feel free
+to add your changes here when sending pull requests. Also send corrections if
+you spot any mistakes.
+
+## v2.0.0-alpha5 (2012-12-03)
+
+* Add mysql.escapeId to escape identifiers (closes #342)
+* Allow custom escaping mode (config.queryFormat)
+* Convert DATE columns to configured timezone instead of UTC (#332)
+* Convert LONGLONG and NEWDECIMAL to numbers (#333)
+* Fix Connection.escape() (fixes #330)
+* Changed Readme ambiguity about custom type cast fallback
+* Change typeCast to receive Connection instead of Connection.config.timezone
+* Fix drain event having useless err parameter
+* Add Connection.statistics() back from v0.9
+* Add Connection.ping() back from v0.9
+
+## v2.0.0-alpha4 (2012-10-03)
+
+* Fix some OOB errors on resume()
+* Fix quick pause() / resume() usage
+* Properly parse host denied / similar errors
+* Add Connection.ChangeUser functionality
+* Make sure changeUser errors are fatal
+* Enable formatting nested arrays for bulk inserts
+* Add Connection.escape functionality
+* Renamed 'close' to 'end' event
+* Return parsed object instead of Buffer for GEOMETRY types
+* Allow nestTables inline (using a string instead of a boolean)
+* Check for ZEROFILL_FLAG and format number accordingly
+* Add timezone support (default: local)
+* Add custom typeCast functionality
+* Export mysql column types
+* Add connection flags functionality (#237)
+* Exports drain event when queue finishes processing (#272, #271, #306)
+
+## v2.0.0-alpha3 (2012-06-12)
+
+* Implement support for `LOAD DATA LOCAL INFILE` queries (#182).
+* Support OLD\_PASSWORD() accounts like 0.9.x did. You should still upgrade any
+ user accounts in your your MySQL user table that has short (16 byte) Password
+ values. Connecting to those accounts is not secure. (#204)
+* Ignore function values when escaping objects, allows to use RowDataPacket
+ objects as query arguments. (Alex Gorbatchev, #213)
+* Handle initial error packets from server such as `ER_HOST_NOT_PRIVILEGED`.
+* Treat `utf8\_bin` as a String, not Buffer. (#214)
+* Handle empty strings in first row column value. (#222)
+* Honor Connection#nestTables setting for queries. (#221)
+* Remove `CLIENT_INTERACTIVE` flag from config. Improves #225.
+* Improve docs for connections settings.
+* Implement url string support for Connection configs.
+
+## v2.0.0-alpha2 (2012-05-31)
+
+* Specify escaping before for NaN / Infinity (they are as unquoted constants).
+* Support for unix domain socket connections (use: {socketPath: '...'}).
+* Fix type casting for NULL values for Date/Number fields
+* Add `fields` argument to `query()` as well as `'fields'` event. This is
+ similar to what was available in 0.9.x.
+* Support connecting to the sphinx searchd daemon as well as MariaDB (#199).
+* Implement long stack trace support, will be removed / disabled if the node
+ core ever supports it natively.
+* Implement `nestTables` option for queries, allows fetching JOIN result sets
+ with overlapping column names.
+* Fix ? placeholder mechanism for values containing '?' characters (#205).
+* Detect when `connect()` is called more than once on a connection and provide
+ the user with a good error message for it (#204).
+* Switch to `UTF8_GENERAL_CI` (previously `UTF8_UNICODE_CI`) as the default
+ charset for all connections to avoid strange MySQL performance issues (#200),
+ and also make the charset user configurable.
+* Fix BLOB type casting for `TINY_BLOG`, `MEDIUM_BLOB` and `LONG_BLOB`.
+* Add support for sending and receiving large (> 16 MB) packets.
+
+## v2.0.0-alpha (2012-05-15)
+
+This release is a rewrite. You should carefully test your application after
+upgrading to avoid problems. This release features many improvements, most
+importantly:
+
+* ~5x faster than v0.9.x for parsing query results
+* Support for pause() / resume() (for streaming rows)
+* Support for multiple statement queries
+* Support for stored procedures
+* Support for transactions
+* Support for binary columns (as blobs)
+* Consistent & well documented error handling
+* A new Connection class that has well defined semantics (unlike the old Client class).
+* Convenient escaping of objects / arrays that allows for simpler query construction
+* A significantly simpler code base
+* Many bug fixes & other small improvements (Closed 62 out of 66 GitHub issues)
+
+Below are a few notes on the upgrade process itself:
+
+The first thing you will run into is that the old `Client` class is gone and
+has been replaced with a less ambitious `Connection` class. So instead of
+`mysql.createClient()`, you now have to:
+
+```js
+var mysql = require('mysql');
+var connection = mysql.createConnection({
+ host : 'localhost',
+ user : 'me',
+ password : 'secret',
+});
+
+connection.query('SELECT 1', function(err, rows) {
+ if (err) throw err;
+
+ console.log('Query result: ', rows);
+});
+
+connection.end();
+```
+
+The new `Connection` class does not try to handle re-connects, please study the
+`Server disconnects` section in the new Readme.
+
+Other than that, the interface has stayed very similar. Here are a few things
+to check out so:
+
+* BIGINT's are now cast into strings
+* Binary data is now cast to buffers
+* The `'row'` event on the `Query` object is now called `'result'` and will
+ also be emitted for queries that produce an OK/Error response.
+* Error handling is consistently defined now, check the Readme
+* Escaping has become more powerful which may break your code if you are
+ currently using objects to fill query placeholders.
+* Connections can now be established explicitly again, so you may wish to do so
+ if you want to handle connection errors specifically.
+
+That should be most of it, if you run into anything else, please send a patch
+or open an issue to improve this document.
+
+## v0.9.6 (2012-03-12)
+
+* Escape array values so they produce sql arrays (Roger Castells, Colin Smith)
+* docs: mention mysql transaction stop gap solution (Blake Miner)
+* docs: Mention affectedRows in FAQ (Michael Baldwin)
+
+## v0.9.5 (2011-11-26)
+
+* 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 (2011-08-31)
+
+* Expose package.json as `mysql.PACKAGE` (#104)
+
+## v0.9.3 (2011-08-22)
+
+* 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 (2011-08-07)
+
+* The underlaying socket connection is now managed implicitly rather than explicitly.
+* Check the [upgrading guide][] for a full list of changes.
+
+## v0.9.1 (2011-02-20)
+
+* 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 maintaining this file:
+
+* [v0.9.0](https://github.com/felixge/node-mysql/compare/v0.8.0...v0.9.0)
+ (2011-01-04)
+* [v0.8.0](https://github.com/felixge/node-mysql/compare/v0.7.0...v0.8.0)
+ (2010-10-30)
+* [v0.7.0](https://github.com/felixge/node-mysql/compare/v0.6.0...v0.7.0)
+ (2010-10-14)
+* [v0.6.0](https://github.com/felixge/node-mysql/compare/v0.5.0...v0.6.0)
+ (2010-09-28)
+* [v0.5.0](https://github.com/felixge/node-mysql/compare/v0.4.0...v0.5.0)
+ (2010-09-17)
+* [v0.4.0](https://github.com/felixge/node-mysql/compare/v0.3.0...v0.4.0)
+ (2010-09-02)
+* [v0.3.0](https://github.com/felixge/node-mysql/compare/v0.2.0...v0.3.0)
+ (2010-08-25)
+* [v0.2.0](https://github.com/felixge/node-mysql/compare/v0.1.0...v0.2.0)
+ (2010-08-22)
+* [v0.1.0](https://github.com/felixge/node-mysql/commits/v0.1.0)
+ (2010-08-22)
19 node_modules/mysql/License
View
@@ -0,0 +1,19 @@
+Copyright (c) 2012 Felix Geisendörfer (felix@debuggable.com) and contributors
+
+ 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.
4 node_modules/mysql/Makefile
View
@@ -0,0 +1,4 @@
+test:
+ node test/run.js
+
+.PHONY: test
759 node_modules/mysql/Readme.md
View
@@ -0,0 +1,759 @@
+# node-mysql
+
+[![Build Status](https://secure.travis-ci.org/felixge/node-mysql.png)](http://travis-ci.org/felixge/node-mysql)
+
+## Install
+
+```bash
+npm install mysql@2.0.0-alpha5
+```
+
+Despite the alpha tag, this is the recommended version for new applications.
+For information about the previous 0.9.x releases, visit the [v0.9 branch][].
+
+Sometimes I may also ask you to install the latest version from Github to check
+if a bugfix is working. In this case, please do:
+
+```
+npm install git://github.com/felixge/node-mysql.git
+```
+
+[v0.9 branch]: https://github.com/felixge/node-mysql/tree/v0.9
+
+## Introduction
+
+This is a node.js driver for mysql. It is written in JavaScript, does not
+require compiling, and is 100% MIT licensed.
+
+Here is an example on how to use it:
+
+```js
+var mysql = require('mysql');
+var connection = mysql.createConnection({
+ host : 'localhost',
+ user : 'me',
+ password : 'secret',
+});
+
+connection.connect();
+
+connection.query('SELECT 1 + 1 AS solution', function(err, rows, fields) {
+ if (err) throw err;
+
+ console.log('The solution is: ', rows[0].solution);
+});
+
+connection.end();
+```
+
+From this example, you can learn the following:
+
+* Every method you invoke on a connection is queued and executed in sequence.
+* Closing the connection is done using `end()` which makes sure all remaining
+ queries are executed before sending a quit packet to the mysql server.
+
+## Contributors
+
+Thanks goes to the people who have contributed code to this module, see the
+[GitHub Contributors page][].
+
+[GitHub Contributors page]: https://github.com/felixge/node-mysql/graphs/contributors
+
+Additionally I'd like to thank the following people:
+
+* [Andrey Hristov][] (Oracle) - for helping me with protocol questions.
+* [Ulf Wendel][] (Oracle) - for helping me with protocol questions.
+
+[Ulf Wendel]: http://blog.ulf-wendel.de/
+[Andrey Hristov]: http://andrey.hristov.com/
+
+## Sponsors
+
+The following companies have supported this project financially, allowing me to
+spend more time on it (ordered by time of contribution):
+
+* [Transloadit](http://transloadit.com) (my startup, we do file uploading &
+ video encoding as a service, check it out)
+* [Joyent](http://www.joyent.com/)
+* [pinkbike.com](http://pinkbike.com/)
+* [Holiday Extras](http://www.holidayextras.co.uk/) (they are [hiring](http://join.holidayextras.co.uk/vacancy/senior-web-technologist/))
+* [Newscope](http://newscope.com/) (they are [hiring](http://www.newscope.com/stellenangebote))
+
+If you are interested in sponsoring a day or more of my time, please
+[get in touch][].
+
+[get in touch]: http://felixge.de/consulting
+
+## Community
+
+If you'd like to discuss this module, or ask questions about it, please use one
+of the following:
+
+* **Mailing list**: https://groups.google.com/forum/#!forum/node-mysql
+* **IRC Channel**: #node.js (on freenode.net, I pay attention to any message
+ including the term `mysql`)
+
+## Establishing connections
+
+The recommended way to establish a connection is this:
+
+```js
+var mysql = require('mysql');
+var connection = mysql.createConnection({
+ host : 'example.org',
+ user : 'bob',
+ password : 'secret',
+});
+
+connection.connect(function(err) {
+ // connected! (unless `err` is set)
+});
+```
+
+However, a connection can also be implicitly established by invoking a query:
+
+```js
+var mysql = require('mysql');
+var connection = mysql.createConnection(...);
+
+connection.query('SELECT 1', function(err, rows) {
+ // connected! (unless `err` is set)
+});
+```
+
+Depending on how you like to handle your errors, either method may be
+appropriate. Any type of connection error (handshake or network) is considered
+a fatal error, see the [Error Handling](#error-handling) section for more
+information.
+
+## Connection options
+
+When establishing a connection, you can set the following options:
+
+* `host`: The hostname of the database you are connecting to. (Default:
+ `localhost`)
+* `port`: The port number to connect to. (Default: `3306`)
+* `socketPath`: The path to a unix domain socket to connect to. When used `host`
+ and `port` are ignored.
+* `user`: The MySQL user to authenticate as.
+* `password`: The password of that MySQL user.
+* `database`: Name of the database to use for this connection (Optional).
+* `charset`: The charset for the connection. (Default: `'UTF8_GENERAL_CI'`)
+* `timezone`: The timezone used to store local dates. (Default: `'local'`)
+* `insecureAuth`: Allow connecting to MySQL instances that ask for the old
+ (insecure) authentication method. (Default: `false`)
+* `typeCast`: Determines if column values should be converted to native
+ JavaScript types. (Default: `true`)
+* `queryFormat`: A custom query format function. See [Custom format](#custom-format).
+* `debug`: Prints protocol details to stdout. (Default: `false`)
+* `multipleStatements`: Allow multiple mysql statements per query. Be careful
+ with this, it exposes you to SQL injection attacks. (Default: `false)
+* `flags`: List of connection flags to use other than the default ones. It is
+ also possible to blacklist default ones. For more information, check [Connection Flags](#connection-flags).
+
+In addition to passing these options as an object, you can also use a url
+string. For example:
+
+```js
+var connection = mysql.createConnection('mysql://user:pass@host/db?debug=true&charset=BIG5_CHINESE_CI&timezone=-0700');
+```
+
+Note: The query values are first attempted to be parsed as JSON, and if that
+fails assumed to be plaintext strings.
+
+## Terminating connections
+
+There are two ways to end a connection. Terminating a connection gracefully is
+done by calling the `end()` method:
+
+```js
+connection.end(function(err) {
+ // The connection is terminated now
+});
+```
+
+This will make sure all previously enqueued queries are still before sending a
+`COM_QUIT` packet to the MySQL server. If a fatal error occurs before the
+`COM_QUIT` packet can be sent, an `err` argument will be provided to the
+callback, but the connection will be terminated regardless of that.
+
+An alternative way to end the connection is to call the `destroy()` method.
+This will cause an immediate termination of the underlying socket.
+Additionally `destroy()` guarantees that no more events or callbacks will be
+triggered for the connection.
+
+```js
+connection.destroy();
+```
+
+Unlike `end()` the `destroy()` method does not take a callback argument.
+
+## Switching users / altering connection state
+
+MySQL offers a changeUser command that allows you to alter the current user and
+other aspects of the connection without shutting down the underlying socket:
+
+```js
+connection.changeUser({user : 'john'}, function(err) {
+ if (err) throw err;
+});
+```
+
+The available options for this feature are:
+
+* `user`: The name of the new user (defaults to the previous one).
+* `password`: The password of the new user (defaults to the previous one).
+* `charset`: The new charset (defaults to the previous one).
+* `database`: The new database (defaults to the previous one).
+
+A sometimes useful side effect of this functionality is that this function also
+resets any connection state (variables, transactions, etc.).
+
+Errors encountered during this operation are treated as fatal connection errors
+by this module.
+
+## Server disconnects
+
+You may lose the connection to a MySQL server due to network problems, the
+server timing you out, or the server crashing. All of these events are
+considered fatal errors, and will have the `err.code =
+'PROTOCOL_CONNECTION_LOST'`. See the [Error Handling](#error-handling) section
+for more information.
+
+The best way to handle such unexpected disconnects is shown below:
+
+```js
+function handleDisconnect(connection) {
+ connection.on('error', function(err) {
+ if (!err.fatal) {
+ return;
+ }
+
+ if (err.code !== 'PROTOCOL_CONNECTION_LOST') {
+ throw err;
+ }
+
+ console.log('Re-connecting lost connection: ' + err.stack);
+
+ connection = mysql.createConnection(connection.config);
+ handleDisconnect(connection);
+ connection.connect();
+ });
+}
+
+handleDisconnect(connection);
+```
+
+As you can see in the example above, re-connecting a connection is done by
+establishing a new connection. Once terminated, an existing connection object
+cannot be re-connected by design.
+
+This logic will also be part of connection pool support once I add that to this
+library.
+
+## Escaping query values
+
+In order to avoid SQL Injection attacks, you should always escape any user
+provided data before using it inside a SQL query. You can do so using the
+`connection.escape()` method:
+
+```js
+var userId = 'some user provided value';
+var sql = 'SELECT * FROM users WHERE id = ' + connection.escape(userId);
+connection.query(sql, function(err, results) {
+ // ...
+});
+```
+
+Alternatively, you can use `?` characters as placeholders for values you would
+like to have escaped like this:
+
+```js
+connection.query('SELECT * FROM users WHERE id = ?', [userId], function(err, results) {
+ // ...
+});
+```
+
+This looks similar to prepared statements in MySQL, however it really just uses
+the same `connection.escape()` method internally.
+
+Different value types are escaped differently, here is how:
+
+* Numbers are left untouched
+* Booleans are converted to `true` / `false` strings
+* Date objects are converted to `'YYYY-mm-dd HH:ii:ss'` strings
+* Buffers are converted to hex strings, e.g. `X'0fa5'`
+* Strings are safely escaped
+* Arrays are turned into list, e.g. `['a', 'b']` turns into `'a', 'b'`
+* Nested arrays are turned into grouped lists (for bulk inserts), e.g. `[['a',
+ 'b'], ['c', 'd']]` turns into `('a', 'b'), ('c', 'd')`
+* Objects are turned into `key = 'val'` pairs. Nested objects are cast to
+ strings.
+* `undefined` / `null` are converted to `NULL`
+* `NaN` / `Infinity` are left as-is. MySQL does not support these, and trying
+ to insert them as values will trigger MySQL errors until they implement
+ support.
+
+If you paid attention, you may have noticed that this escaping allows you
+to do neat things like this:
+
+```js
+var post = {id: 1, title: 'Hello MySQL'};
+var query = connection.query('INSERT INTO posts SET ?', post, function(err, result) {
+ // Neat!
+});
+console.log(query.sql); // INSERT INTO posts SET `id` = 1, `title` = 'Hello MySQL'
+
+```
+
+If you feel the need to escape queries by yourself, you can also use the escaping
+function directly:
+
+```js
+var query = "SELECT * FROM posts WHERE title=" + mysql.escape("Hello MySQL");
+
+console.log(query); // SELECT * FROM posts WHERE title='Hello MySQL'
+```
+
+## Escaping query identifiers
+
+If you can't trust an SQL identifier (database / table / column name) because it is
+provided by a user, you should escape it with `mysql.escapeId(identifier)` like this:
+
+```js
+var sorter = 'date';
+var query = 'SELECT * FROM posts ORDER BY ' + mysql.escapeId(sorter);
+
+console.log(query); // SELECT * FROM posts ORDER BY `date`
+```
+
+It also supports adding qualified identifiers. It will escape both parts.
+
+```js
+var sorter = 'date';
+var query = 'SELECT * FROM posts ORDER BY ' + mysql.escapeId('posts.' + sorter);
+
+console.log(query); // SELECT * FROM posts ORDER BY `posts`.`date`
+```
+
+When you pass an Object to `.escape()` or `.query()`, `.escapeId()` is used to avoid SQL
+injection in object keys.
+
+### Custom format
+
+If you prefer to have another type of query escape format, there's a connection configuration option you can use to define a custom format function. You can access the connection object if you want to use the built-in `.escape()` or any other connection function.
+
+Here's an example of how to implement another format:
+
+```js
+connection.config.queryFormat = function (query, values) {
+ if (!values) return query;
+ return query.replace(/\:(\w+)/g, function (txt, key) {
+ if (values.hasOwnProperty(key)) {
+ return this.escape(values[key]);
+ }
+ return txt;
+ }.bind(this));
+};
+
+connection.query("UPDATE posts SET title = :title", { title: "Hello MySQL" });
+```
+
+## Getting the id of an inserted row
+
+If you are inserting a row into a table with an auto increment primary key, you
+can retrieve the insert id like this:
+
+```js
+connection.query('INSERT INTO posts SET ?', {title: 'test'}, function(err, result) {
+ if (err) throw err;
+
+ console.log(result.insertId);
+});
+```
+
+## Executing queries in parallel
+
+The MySQL protocol is sequential, this means that you need multiple connections
+to execute queries in parallel. Future version of this module may ship with a
+connection pool implementation, but for now you have to figure out how to
+manage multiple connections yourself if you want to execute queries in
+parallel.
+
+One simple approach is to create one connection per incoming http request.
+
+## Streaming query rows
+
+Sometimes you may want to select large quantities of rows and process each of
+them as they are received. This can be done like this:
+
+```js
+var query = connection.query('SELECT * FROM posts');
+query
+ .on('error', function(err) {
+ // Handle error, an 'end' event will be emitted after this as well
+ })
+ .on('fields', function(fields) {
+ // the field packets for the rows to follow
+ })
+ .on('result', function(row) {
+ // Pausing the connnection is useful if your processing involves I/O
+ connection.pause();
+
+ processRow(row, function() {
+ connection.resume();
+ });
+ })
+ .on('end', function() {
+ // all rows have been received
+ });
+```
+
+Please note a few things about the example above:
+
+* Usually you will want to receive a certain amount of rows before starting to
+ throttle the connection using `pause()`. This number will depend on the
+ amount and size of your rows.
+* `pause()` / `resume()` operate on the underlying socket and parser. You are
+ guaranteed that no more `'result'` events will fire after calling `pause()`.
+* You MUST NOT provide a callback to the `query()` method when streaming rows.
+* The `'result'` event will fire for both rows as well as OK packets
+ confirming the success of a INSERT/UPDATE query.
+
+Additionally you may be interested to know that it is currently not possible to
+stream individual row columns, they will always be buffered up entirely. If you
+have a good use case for streaming large fields to and from MySQL, I'd love to
+get your thoughts and contributions on this.
+
+## Multiple statement queries
+
+Support for multiple statements is disabled for security reasons (it allows for
+SQL injection attacks if values are not properly escaped). To use this feature
+you have to enable it for your connection:
+
+```js
+var connection = mysql.createConnection({multipleStatements: true});
+```
+
+Once enabled, you can execute multiple statement queries like any other query:
+
+```js
+connection.query('SELECT 1; SELECT 2', function(err, results) {
+ if (err) throw err;
+
+ // `results` is an array with one element for every statement in the query:
+ console.log(results[0]); // [{1: 1}]
+ console.log(results[1]); // [{2: 2}]
+});
+```
+
+Additionally you can also stream the results of multiple statement queries:
+
+```js
+var query = connection.query('SELECT 1; SELECT 2');
+
+query
+ .on('fields', function(fields, index) {
+ // the fields for the result rows that follow
+ })
+ .on('result', function(row, index) {
+ // index refers to the statement this result belongs to (starts at 0)
+ });
+```
+
+If one of the statements in your query causes an error, the resulting Error
+object contains a `err.index` property which tells you which statement caused
+it. MySQL will also stop executing any remaining statements when an error
+occurs.
+
+Please note that the interface for streaming multiple statement queries is
+experimental and I am looking forward to feedback on it.
+
+## Stored procedures
+
+You can call stored procedures from your queries as with any other mysql driver.
+If the stored procedure produces several result sets, they are exposed to you
+the same way as the results for multiple statement queries.
+
+## Joins with overlapping column names
+
+When executing joins, you are likely to get result sets with overlapping column
+names.
+
+By default, node-mysql will overwrite colliding column names in the
+order the columns are received from MySQL, causing some of the received values
+to be unavailable.
+
+However, you can also specify that you want your columns to be nested below
+the table name like this:
+
+```js
+var options = {sql: '...', nestTables: true};
+connection.query(options, function(err, results) {
+ /* results will be an array like this now:
+ [{
+ table1: {
+ fieldA: '...',
+ fieldB: '...',
+ },
+ table2: {
+ fieldA: '...',
+ fieldB: '...',
+ },
+ }, ...]
+ */
+});
+```
+
+Or use a string separator to have your results merged.
+
+```js
+var options = {sql: '...', nestTables: '_'};
+connection.query(options, function(err, results) {
+ /* results will be an array like this now:
+ [{
+ table1_fieldA: '...',
+ table1_fieldB: '...',
+ table2_fieldA: '...',
+ table2_fieldB: '...'
+ }, ...]
+ */
+});
+```
+
+## Error handling
+
+This module comes with a consistent approach to error handling that you should
+review carefully in order to write solid applications.
+
+All errors created by this module are instances of the JavaScript [Error][]
+object. Additionally they come with two properties:
+
+* `err.code`: Either a [MySQL server error][] (e.g.
+ `'ER_ACCESS_DENIED_ERROR'`), a node.js error (e.g. `'ECONNREFUSED'`) or an
+ internal error (e.g. `'PROTOCOL_CONNECTION_LOST'`).
+* `err.fatal`: Boolean, indicating if this error is terminal to the connection
+ object.
+
+[Error]: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error
+[MySQL server error]: http://dev.mysql.com/doc/refman/5.5/en/error-messages-server.html
+
+Fatal errors are propagated to *all* pending callbacks. In the example below, a
+fatal error is triggered by trying to connect to an invalid port. Therefore the
+error object is propagated to both pending callbacks:
+
+```js
+var connection = require('mysql').createConnection({
+ port: 84943, // WRONG PORT
+});
+
+connection.connect(function(err) {
+ console.log(err.code); // 'ECONNREFUSED'
+ console.log(err.fatal); // true
+});
+
+connection.query('SELECT 1', function(err) {
+ console.log(err.code); // 'ECONNREFUSED'
+ console.log(err.fatal); // true
+});
+```
+
+Normal errors however are only delegated to the callback they belong to. So in
+the example below, only the first callback receives an error, the second query
+works as expected:
+
+```js
+connection.query('USE name_of_db_that_does_not_exist', function(err, rows) {
+ console.log(err.code); // 'ER_BAD_DB_ERROR'
+});
+
+connection.query('SELECT 1', function(err, rows) {
+ console.log(err); // null
+ console.log(rows.length); // 1
+});
+```
+
+Last but not least: If a fatal errors occurs and there are no pending
+callbacks, or a normal error occurs which has no callback belonging to it, the
+error is emitted as an `'error'` event on the connection object. This is
+demonstrated in the example below:
+
+```js
+connection.on('error', function(err) {
+ console.log(err.code); // 'ER_BAD_DB_ERROR'
+});
+
+connection.query('USE name_of_db_that_does_not_exist');
+```
+
+Note: `'error'` are special in node. If they occur without an attached
+listener, a stack trace is printed and your process is killed.
+
+**tl;dr:** This module does not want you to deal with silent failures. You
+should always provide callbacks to your method calls. If you want to ignore
+this advice and suppress unhandled errors, you can do this:
+
+```js
+// I am Chuck Norris:
+connection.on('error', function() {});
+```
+
+## Exception Safety
+
+This module is exception safe. That means you can continue to use it, even if
+one of your callback functions throws an error which you're catching using
+'uncaughtException' or a domain.
+
+## Type casting
+
+For your convenience, this driver will cast mysql types into native JavaScript
+types by default. The following mappings exist:
+
+### Number
+
+* TINYINT
+* SMALLINT
+* INT
+* MEDIUMINT
+* YEAR
+* FLOAT
+* DOUBLE
+
+### Date
+
+* TIMESTAMP
+* DATE
+* DATETIME
+
+### Buffer
+
+* TINYBLOB
+* MEDIUMBLOB
+* LONGBLOB
+* BLOB
+* BINARY
+* VARBINARY
+* BIT (last byte will be filled with 0 bits as necessary)
+
+### String
+
+* CHAR
+* VARCHAR
+* TINYTEXT
+* MEDIUMTEXT
+* LONGTEXT
+* TEXT
+* ENUM
+* SET
+* DECIMAL (may exceed float precision)
+* BIGINT (may exceed float precision)
+* TIME (could be mapped to Date, but what date would be set?)
+* GEOMETRY (never used those, get in touch if you do)
+
+It is not recommended (and may go away / change in the future) to disable type
+casting, but you can currently do so on either the connection:
+
+```js
+var connection = require('mysql').createConnection({typeCast: false});
+```
+
+Or on the query level:
+
+```js
+var options = {sql: '...', typeCast: false};
+var query = connection.query(options, function(err, results) {
+
+}):
+```
+
+You can also pass a function and handle type casting yourself. You're given some
+column information like database, table and name and also type and length. If you
+just want to apply a custom type casting to a specific type you can do it and then
+fallback to the default. Here's an example of converting `TINYINT(1)` to boolean:
+
+```js
+connection.query({
+ sql: '...',
+ typeCast: function (field, next) {
+ if (field.type == 'TINY' && field.length == 1) {
+ return (field.string() == '1'); // 1 = true, 0 = false
+ }
+ return next();
+ }
+})
+```
+
+If you need a buffer there's also a `.buffer()` function and also a `.geometry()` one
+both used by the default type cast that you can use.
+
+## Connection Flags
+
+If, for any reason, you would like to change the default connection flags, you
+can use the connection option `flags`. Pass a string with a comma separated list
+of items to add to the default flags. If you don't want a default flag to be used
+prepend the flag with a minus sign. To add a flag that is not in the default list, don't prepend it with a plus sign, just write the flag name (case insensitive).
+
+**Please note that some available flags that are not default are still not supported
+(e.g.: SSL, Compression). Use at your own risk.**
+
+### Example
+
+The next example blacklists FOUND_ROWS flag from default connection flags.
+
+```js
+var connection = mysql.createConnection("mysql://localhost/test?flags=-FOUND_ROWS")
+```
+
+### Default Flags
+
+- LONG_PASSWORD
+- FOUND_ROWS
+- LONG_FLAG
+- CONNECT_WITH_DB
+- ODBC
+- LOCAL_FILES
+- IGNORE_SPACE
+- PROTOCOL_41
+- IGNORE_SIGPIPE
+- TRANSACTIONS
+- RESERVED
+- SECURE_CONNECTION
+- MULTI_RESULTS
+- MULTI_STATEMENTS (used if `multipleStatements` option is activated)
+
+### Other Available Flags
+
+- NO_SCHEMA
+- COMPRESS
+- INTERACTIVE
+- SSL
+- PS_MULTI_RESULTS
+- PLUGIN_AUTH
+- SSL_VERIFY_SERVER_CERT
+- REMEMBER_OPTIONS
+
+## Debugging and reporting problems
+
+If you are running into problems, one thing that may help is enabling the
+`debug` mode for the connection:
+
+```js
+var connection = mysql.createConnection({debug: true});
+```
+
+This will print all incoming and outgoing packets on stdout.
+
+If that does not help, feel free to open a GitHub issue. A good GitHub issue
+will have:
+
+* The minimal amount of code required to reproduce the problem (if possible)
+* As much debugging output and information about your environment (mysql
+ version, node version, os, etc.) as you can gather.
+
+## Todo
+
+* Prepared statements
+* setTimeout() for Connection / Query
+* connection pooling
+* Support for encodings other than UTF-8 / ASCII
+* API support for transactions, similar to [php](http://www.php.net/manual/en/mysqli.quickstart.transactions.php)
96 node_modules/mysql/benchmark/analyze.js
View
@@ -0,0 +1,96 @@
+var script = process.cwd() + '/' + process.argv[2];
+var spawn = require('child_process').spawn;
+
+var numbers = [];
+var boringResults = 0;
+var scriptRuns = 0;
+
+function runScript() {
+ scriptRuns++;
+
+ var child = spawn(process.execPath, [script]);
+
+ var buffer = '';
+ child.stdout.on('data', function(chunk) {
+ buffer += chunk;
+
+ var offset;
+ while ((offset = buffer.indexOf('\n')) > -1) {
+ var number = parseInt(buffer.substr(0, offset), 10);
+ buffer = buffer.substr(offset + 1);
+
+ var maxBefore = max();
+ var minBefore = min();
+
+ numbers.push(number);
+
+ if (maxBefore === max() && minBefore === min()) {
+ boringResults++;
+ }
+
+ if (boringResults > 10) {
+ boringResults = 0;
+ child.kill();
+ runScript();
+ }
+ }
+ });
+}
+
+function report() {
+ console.log(
+ 'max: %s | median: %s | sdev: %s | last: %s | min: %s | runs: %s | results: %s',
+ max(),
+ median(),
+ sdev(),
+ numbers[numbers.length - 1],
+ min(),
+ scriptRuns,
+ numbers.length
+ );
+}
+
+function min() {
+ if (!numbers.length) return undefined;
+
+ return numbers.reduce(function(min, number) {
+ return (number < min)
+ ? number
+ : min;
+ });
+}
+
+function max() {
+ if (!numbers.length) return undefined;
+
+ return numbers.reduce(function(max, number) {
+ return (number > max)
+ ? number
+ : max;
+ });
+}
+
+function median() {
+ return numbers[Math.floor(numbers.length / 2)];
+}
+
+function sdev() {
+ if (!numbers.length) return undefined;
+
+ return Math.round(Math.sqrt(variance()));
+}
+
+function variance() {
+ var t = 0, squares = 0, len = numbers.length;
+
+ for (var i=0; i<len; i++) {
+ var obs = numbers[i];
+ t += obs;
+ squares += Math.pow(obs, 2);
+ }
+ return (squares/len) - Math.pow(t/len, 2);
+}
+
+setInterval(report, 1000);
+
+runScript();
112 node_modules/mysql/benchmark/parse-100k-blog-rows.js
View
@@ -0,0 +1,112 @@
+var lib = __dirname + '/../lib';
+var Protocol = require(lib + '/protocol/protocol');
+var Packets = require(lib + '/protocol/packets');
+var PacketWriter = require(lib + '/protocol/PacketWriter');
+var Parser = require(lib + '/protocol/Parser');
+
+var options = {
+ rows : 100000,
+ bufferSize : 64 * 1024,
+};
+
+console.error('Config:', options);
+
+function createBuffers() {
+ var parser = new Parser();
+
+ process.stderr.write('Creating row buffers ... ');
+
+ var number = 1;
+ var id = 0;
+ var start = Date.now();
+
+ var buffers = [
+ createPacketBuffer(parser, new Packets.ResultSetHeaderPacket({fieldCount: 2})),
+ createPacketBuffer(parser, new Packets.FieldPacket({catalog: 'foo', name: 'id'})),
+ createPacketBuffer(parser, new Packets.FieldPacket({catalog: 'foo', name: 'text'})),
+ createPacketBuffer(parser, new Packets.EofPacket()),
+ ];
+
+ for (var i = 0; i < options.rows; i++) {
+ buffers.push(createRowDataPacketBuffer(parser, number++));
+ }
+
+ buffers.push(createPacketBuffer(parser, new Packets.EofPacket));
+
+ buffers = mergeBuffers(buffers);
+
+ var bytes = buffers.reduce(function(bytes, buffer) {
+ return bytes + buffer.length;
+ }, 0);
+
+ var mb = (bytes / 1024 / 1024).toFixed(2)
+
+ console.error('%s buffers (%s mb) in %s ms', buffers.length, mb, (Date.now() - start));
+
+ return buffers;
+}
+
+function createPacketBuffer(parser, packet) {
+ var writer = new PacketWriter();
+ packet.write(writer);
+ return writer.toBuffer(parser);
+}
+
+function createRowDataPacketBuffer(parser, number) {
+ var writer = new PacketWriter();
+
+ writer.writeLengthCodedString(parser._nextPacketNumber);
+ writer.writeLengthCodedString('Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry\'s standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has sur');
+
+ return writer.toBuffer(parser);
+}
+
+function mergeBuffers(buffers) {
+ var mergeBuffer = new Buffer(options.bufferSize);
+ var mergeBuffers = [];
+ var offset = 0;
+
+ for (var i = 0; i < buffers.length; i++) {
+ var buffer = buffers[i];
+
+ var bytesRemaining = mergeBuffer.length - offset;
+ if (buffer.length < bytesRemaining) {
+ buffer.copy(mergeBuffer, offset);
+ offset += buffer.length;
+ } else {
+ buffer.copy(mergeBuffer, offset, 0, bytesRemaining);
+ mergeBuffers.push(mergeBuffer);
+
+ mergeBuffer = new Buffer(options.bufferSize);
+ buffer.copy(mergeBuffer, 0, bytesRemaining);
+ offset = buffer.length - bytesRemaining;
+ }
+ }
+
+ if (offset > 0) {
+ mergeBuffers.push(mergeBuffer.slice(0, offset));
+ }
+
+ return mergeBuffers;
+}
+
+function benchmark(buffers) {
+ var protocol = new Protocol();
+ protocol._handshakeInitializationPacket = true;
+ protocol.query({typeCast: false, sql: 'SELECT ...'});
+
+ var start = +new Date;
+
+ for (var i = 0; i < buffers.length; i++) {
+ protocol.write(buffers[i]);
+ }
+
+ var duration = Date.now() - start;
+ var hz = Math.round(options.rows / (duration / 1000));
+ console.log(hz);
+}
+
+var buffers = createBuffers();
+while (true) {
+ benchmark(buffers);
+}
42 node_modules/mysql/benchmark/select-100k-blog-rows.js
View
@@ -0,0 +1,42 @@
+var common = require('../test/common');
+var client = common.createConnection({typeCast: false});
+var rowsPerRun = 100000;
+
+client.connect(function(err) {
+ if (err) throw err;
+
+ client.query('USE node_mysql_test', function(err, results) {
+ if (err) throw err;
+
+ selectRows();
+ });
+});
+
+var firstSelect;
+var rowCount = 0;
+
+console.error('Benchmarking rows per second in hz:');
+
+function selectRows() {
+ firstSelect = firstSelect || Date.now();
+
+ client.query('SELECT * FROM posts', function(err, rows) {
+ if (err) throw err;
+
+ rowCount += rows.length;
+ if (rowCount < rowsPerRun) {
+ selectRows();
+ return;
+ }
+
+ var duration = (Date.now() - firstSelect) / 1000;
+ var hz = Math.round(rowCount / duration);
+
+ console.log(hz);
+
+ rowCount = 0;
+ firstSelect = null;
+
+ selectRows();
+ });
+};
12 node_modules/mysql/index.js
View
@@ -0,0 +1,12 @@
+var Connection = require('./lib/Connection');
+var ConnectionConfig = require('./lib/ConnectionConfig');
+var Types = require('./lib/protocol/constants/types');
+var SqlString = require('./lib/protocol/SqlString');
+
+exports.createConnection = function(config) {
+ return new Connection({config: new ConnectionConfig(config)});
+};
+
+exports.Types = Types;
+exports.escape = SqlString.escape;
+exports.escapeId = SqlString.escapeId;
155 node_modules/mysql/lib/Connection.js
View
@@ -0,0 +1,155 @@
+var Net = require('net');
+var ConnectionConfig = require('./ConnectionConfig');
+var Protocol = require('./protocol/Protocol');
+var SqlString = require('./protocol/SqlString');
+var EventEmitter = require('events').EventEmitter;
+var Util = require('util');
+
+module.exports = Connection;
+Util.inherits(Connection, EventEmitter);
+function Connection(options) {
+ EventEmitter.call(this);
+
+ this.config = options.config;
+
+ this._socket = options.socket;
+ this._protocol = new Protocol({config: this.config, connection: this});
+ this._connectCalled = false;
+}
+
+Connection.prototype.connect = function(cb) {
+ if (!this._connectCalled) {
+ this._connectCalled = true;
+
+ this._socket = (this.config.socketPath)
+ ? Net.createConnection(this.config.socketPath)
+ : Net.createConnection(this.config.port, this.config.host);
+
+ this._socket.pipe(this._protocol);
+ this._protocol.pipe(this._socket);
+
+ this._socket.on('error', this._handleNetworkError.bind(this));
+ this._protocol.on('unhandledError', this._handleProtocolError.bind(this));
+ this._protocol.on('drain', this._handleProtocolDrain.bind(this));
+ this._protocol.on('end', this._handleProtocolEnd.bind(this));
+ }
+
+ this._protocol.handshake(cb);
+};
+
+Connection.prototype.changeUser = function(options, cb){
+ this._implyConnect();
+
+ if (typeof options === 'function') {
+ cb = options;
+ options = {};
+ }
+
+ var charsetNumber = (options.charset)
+ ? Config.getCharsetNumber(options.charset)
+ : this.config.charsetNumber;
+
+ return this._protocol.changeUser({
+ user : options.user || this.config.user,
+ password : options.password || this.config.password,
+ database : options.database || this.config.database,
+ charsetNumber : charsetNumber,
+ currentConfig : this.config
+ }, cb);
+};
+
+Connection.prototype.query = function(sql, values, cb) {
+ this._implyConnect();
+
+ var options = {};
+
+ if (typeof sql === 'object') {
+ // query(options, cb)
+ options = sql;
+ cb = values;
+ values = options.values;
+
+ delete options.values;
+ } else if (typeof values === 'function') {
+ // query(sql, cb)
+ cb = values;
+ options.sql = sql;
+ values = undefined;
+ } else {
+ // query(sql, values, cb)
+ options.sql = sql;
+ options.values = values;
+ }
+
+ options.sql = this.format(options.sql, values || []);
+
+ if (!('typeCast' in options)) {
+ options.typeCast = this.config.typeCast;
+ }
+
+ return this._protocol.query(options, cb);
+};
+
+Connection.prototype.ping = function(cb) {
+ this._implyConnect();
+ this._protocol.ping(cb);
+};
+
+Connection.prototype.statistics = function(cb) {
+ this._implyConnect();
+ this._protocol.stats(cb);
+};
+
+Connection.prototype.end = function(cb) {
+ this._implyConnect();
+ this._protocol.quit(cb);
+};
+
+Connection.prototype.destroy = function() {
+ this._implyConnect();
+ this._socket.destroy();
+ this._protocol.destroy();
+};
+
+Connection.prototype.pause = function() {
+ this._socket.pause();
+ this._protocol.pause();
+};
+
+Connection.prototype.resume = function() {
+ this._socket.resume();
+ this._protocol.resume();
+};
+
+Connection.prototype.escape = function(value) {
+ return SqlString.escape(value, false, this.config.timezone);
+};
+
+Connection.prototype.format = function(sql, values) {
+ if (typeof this.config.queryFormat == "function") {
+ return this.config.queryFormat.call(this, sql, values, this.config.timezone);
+ }
+ return SqlString.format(sql, values, this.config.timezone);
+};
+
+Connection.prototype._handleNetworkError = function(err) {
+ this._protocol.handleNetworkError(err);
+};
+
+Connection.prototype._handleProtocolError = function(err) {
+ this.emit('error', err);
+};
+
+Connection.prototype._handleProtocolDrain = function() {
+ this.emit('drain');
+};
+
+Connection.prototype._handleProtocolEnd = function(err) {
+ this.emit('end', err);
+};
+
+Connection.prototype._implyConnect = function() {
+ if (!this._connectCalled) {
+ this.connect();
+ }
+};
111 node_modules/mysql/lib/ConnectionConfig.js
View
@@ -0,0 +1,111 @@
+var urlParse = require('url').parse;
+var ClientConstants = require('./protocol/constants/client');
+var Charsets = require('./protocol/constants/charsets');
+
+module.exports = ConnectionConfig;
+function ConnectionConfig(options) {
+ if (typeof options === 'string') {
+ options = ConnectionConfig.parseUrl(options);
+ }
+
+ this.host = options.host || 'localhost';
+ this.port = options.port || 3306;
+ this.socketPath = options.socketPath;
+ this.user = options.user || undefined;
+ this.password = options.password || undefined;
+ this.database = options.database;
+ this.insecureAuth = options.insecureAuth || false;
+ this.debug = options.debug;
+ this.timezone = options.timezone || 'local';
+ this.flags = options.flags || '';
+ this.queryFormat = options.queryFormat;
+ this.typeCast = (options.typeCast === undefined)
+ ? true
+ : options.typeCast;
+
+ if (this.timezone[0] == " ") {
+ // "+" is a url encoded char for space so it
+ // gets translated to space when giving a
+ // connection string..
+ this.timezone = "+" + this.timezone.substr(1);
+ }
+
+ this.maxPacketSize = 0;
+ this.charsetNumber = (options.charset)
+ ? ConnectionConfig.getCharsetNumber(options.charset)
+ : Charsets.UTF8_GENERAL_CI;
+
+ this.clientFlags = ConnectionConfig.mergeFlags(ConnectionConfig.getDefaultFlags(options),
+ options.flags || '');
+}
+
+ConnectionConfig.mergeFlags = function(default_flags, user_flags) {
+ var flags = 0x0, i;
+
+ user_flags = (user_flags || '').toUpperCase().split(/\s*,+\s*/);
+
+ // add default flags unless "blacklisted"
+ for (i in default_flags) {
+ if (user_flags.indexOf("-" + default_flags[i]) >= 0) continue;
+
+ flags |= ClientConstants["CLIENT_" + default_flags[i]] || 0x0;
+ }
+ // add user flags unless already already added
+ for (i in user_flags) {
+ if (user_flags[i][0] == "-") continue;
+ if (default_flags.indexOf(user_flags[i]) >= 0) continue;
+
+ flags |= ClientConstants["CLIENT_" + user_flags[i]] || 0x0;
+ }
+
+ return flags;
+};
+
+ConnectionConfig.getDefaultFlags = function(options) {
+ var defaultFlags = [ "LONG_PASSWORD", "FOUND_ROWS", "LONG_FLAG",
+ "CONNECT_WITH_DB", "ODBC", "LOCAL_FILES",
+ "IGNORE_SPACE", "PROTOCOL_41", "IGNORE_SIGPIPE",
+ "TRANSACTIONS", "RESERVED", "SECURE_CONNECTION",
+ "MULTI_RESULTS" ];
+ if (options && options.multipleStatements) {
+ defaultFlags.push("MULTI_STATEMENTS");
+ }
+
+ return defaultFlags;
+};
+
+ConnectionConfig.getCharsetNumber = function(charset) {
+ return Charsets[charset];
+};
+
+ConnectionConfig.parseUrl = function(url) {
+ url = urlParse(url, true);
+
+ var options = {
+ host : url.hostname,
+ port : url.port,
+ database : url.pathname.substr(1),
+ };
+
+ if (url.auth) {
+ var auth = url.auth.split(':');
+ options.user = auth[0];
+ options.password = auth[1];
+ }
+
+ if (url.query) {
+ for (var key in url.query) {
+ var value = url.query[key];
+
+ try {
+ // Try to parse this as a JSON expression first
+ options[key] = JSON.parse(value);
+ } catch (err) {
+ // Otherwise assume it is a plain string
+ options[key] = value;
+ }
+ }
+ }
+
+ return options;
+};
165 node_modules/mysql/lib/protocol/Auth.js
View
@@ -0,0 +1,165 @@
+var Buffer = require('buffer').Buffer;
+var Crypto = require('crypto');
+var Auth = exports;
+
+function sha1(msg) {
+ var hash = Crypto.createHash('sha1');
+ hash.update(msg);
+ // hash.digest() does not output buffers yet
+ return hash.digest('binary');
+};
+Auth.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;
+};
+Auth.xor = xor;
+
+Auth.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.
+Auth.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;
+};
+
+Auth.randomInit = function(seed1, seed2) {
+ return {
+ max_value: 0x3FFFFFFF,
+ max_value_dbl: 0x3FFFFFFF,
+ seed1: seed1 % 0x3FFFFFFF,
+ seed2: seed2 % 0x3FFFFFFF
+ };
+};
+
+Auth.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;
+};
+
+Auth.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;
+};
+
+Auth.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;
+};
+
+Auth.xor32 = function(a,b){
+ return [a[0] ^ b[0], a[1] ^ b[1]];
+};
+
+Auth.add32 = function(a,b){
+ var w1 = a[1] + b[1],
+ w2 = a[0] + b[0] + ((w1 & 0xFFFF0000) >> 16);
+
+ return [w2 & 0xFFFF, w1 & 0xFFFF];
+};
+
+Auth.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];
+};
+
+Auth.and32 = function(a,b){
+ return [a[0] & b[0], a[1] & b[1]];
+};
+
+Auth.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];
+};
+
+Auth.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;
+};
+
+Auth.int32Read = function(buffer, offset){
+ return (buffer[offset] << 24)
+ + (buffer[offset+1] << 16)
+ + (buffer[offset+2] << 8)
+ + (buffer[offset+3]);
+};
5 node_modules/mysql/lib/protocol/PacketHeader.js
View
@@ -0,0 +1,5 @@
+module.exports = PacketHeader;
+function PacketHeader(length, number) {
+ this.length = length;
+ this.number = number;
+}
197 node_modules/mysql/lib/protocol/PacketWriter.js
View
@@ -0,0 +1,197 @@
+var BIT_16 = Math.pow(2, 16);
+var BIT_24 = Math.pow(2, 24);
+// The maximum precision JS Numbers can hold precisely
+// Don't panic: Good enough to represent byte values up to 8192 TB
+var IEEE_754_BINARY_64_PRECISION = Math.pow(2, 53);
+var MAX_PACKET_LENGTH = Math.pow(2, 24) - 1;
+
+module.exports = PacketWriter;
+function PacketWriter() {
+ this._buffer = new Buffer(0);
+ this._offset = 0;
+}
+
+PacketWriter.prototype.toBuffer = function(parser) {
+ var packets = Math.floor(this._buffer.length / MAX_PACKET_LENGTH) + 1;
+ var buffer = this._buffer;
+ this._buffer = new Buffer(this._buffer.length + packets * 4);
+
+ for (var packet = 0; packet < packets; packet++) {
+ this._offset = packet * (MAX_PACKET_LENGTH + 4);
+
+ var isLast = (packet + 1 === packets);
+ var packetLength = (isLast)
+ ? buffer.length % MAX_PACKET_LENGTH
+ : MAX_PACKET_LENGTH;
+
+ var packetNumber = parser.incrementPacketNumber();
+
+ this.writeUnsignedNumber(3, packetLength);
+ this.writeUnsignedNumber(1, packetNumber);
+
+ var start = packet * MAX_PACKET_LENGTH;
+ var end = start + packetLength;
+
+ this.writeBuffer(buffer.slice(start, end));
+ }
+
+ return this._buffer;
+};
+
+PacketWriter.prototype.writeUnsignedNumber = function(bytes, value) {
+ this._allocate(bytes);
+
+ for (var i = 0; i < bytes; i++) {
+ this._buffer[this._offset++] = (value >> (i * 8)) & 0xff;
+ }
+};
+
+PacketWriter.prototype.writeFiller = function(bytes) {
+ this._allocate(bytes);
+
+ for (var i = 0; i < bytes; i++) {
+ this._buffer[this._offset++] = 0x00;
+ }
+};
+
+PacketWriter.prototype.writeNullTerminatedString = function(value, encoding) {
+ // Typecast undefined into '' and numbers into strings
+ value = value || '';
+ value = value + '';
+
+ var bytes = Buffer.byteLength(value, encoding || 'utf-8') + 1;
+ this._allocate(bytes);
+
+ this._buffer.write(value, this._offset, encoding);
+ this._buffer[this._offset + bytes - 1] = 0x00;
+
+ this._offset += bytes;
+};
+
+PacketWriter.prototype.writeString = function(value) {
+ // Typecast undefined into '' and numbers into strings
+ value = value || '';
+ value = value + '';
+
+ var bytes = Buffer.byteLength(value, 'utf-8');
+ this._allocate(bytes);
+
+ this._buffer.write(value, this._offset, 'utf-8');
+
+ this._offset += bytes;
+};
+
+PacketWriter.prototype.writeBuffer = function(value) {
+ var bytes = value.length;
+
+ this._allocate(bytes);
+ value.copy(this._buffer, this._offset);
+ this._offset += bytes;
+};
+
+PacketWriter.prototype.writeLengthCodedNumber = function(value) {
+ if (value === null) {
+ this._allocate(1);
+ this._buffer[this._offset++] = 251;
+ return;
+ }
+
+ if (value <= 250) {
+ this._allocate(1);
+ this._buffer[this._offset++] = value;
+ return;
+ }
+
+ if (value > IEEE_754_BINARY_64_PRECISION) {
+ throw new Error(
+ 'writeLengthCodedNumber: JS precision range exceeded, your ' +
+ 'number is > 53 bit: "' + value + '"'
+ );
+ }
+
+ if (value <= BIT_16) {
+ this._allocate(3)
+ this._buffer[this._offset++] = 252;
+ } else if (value <= BIT_24) {
+ this._allocate(4)
+ this._buffer[this._offset++] = 253;
+ } else {
+ this._allocate(9);
+ this._buffer[this._offset++] = 254;
+ }
+
+ // 16 Bit
+ this._buffer[this._offset++] = value & 0xff;
+ this._buffer[this._offset++] = (value >> 8) & 0xff;
+
+ if (value <= BIT_16) return;
+
+ // 24 Bit
+ this._buffer[this._offset++] = (value >> 16) & 0xff;
+
+ if (value <= BIT_24) return;
+
+ this._buffer[this._offset++] = (value >> 24) & 0xff;
+
+ // Hack: Get the most significant 32 bit (JS bitwise operators are 32 bit)
+ value = value.toString(2);
+ value = value.substr(0, value.length - 32);
+ value = parseInt(value, 2);
+
+ this._buffer[this._offset++] = value & 0xff;
+ this._buffer[this._offset++] = (value >> 8) & 0xff;
+ this._buffer[this._offset++] = (value >> 16) & 0xff;
+
+ // Set last byte to 0, as we can only support 53 bits in JS (see above)
+ this._buffer[this._offset++] = 0;
+};
+
+PacketWriter.prototype.writeLengthCodedBuffer = function(value) {
+ var bytes = value.length;
+ this.writeLengthCodedNumber(bytes);
+ this.writeBuffer(value);
+};
+
+PacketWriter.prototype.writeNullTerminatedBuffer = function(value) {
+ this.writeBuffer(value);
+ this.writeFiller(1); // 0x00 terminator
+};
+
+PacketWriter.prototype.writeLengthCodedString = function(value) {
+ if (value === null) {
+ this.writeLengthCodedNumber(null);
+ return;
+ }
+
+ value = (value === undefined)
+ ? ''
+ : String(value);
+
+ var bytes = Buffer.byteLength(value, 'utf-8');
+ this.writeLengthCodedNumber(bytes);
+
+ if (!bytes) {
+ return;
+ }
+
+ this._allocate(bytes);
+ this._buffer.write(value, this._offset, 'utf-8');
+ this._offset += bytes;
+};
+
+PacketWriter.prototype._allocate = function(bytes) {
+ if (!this._buffer) {
+ this._buffer = new Buffer(bytes);
+ return;
+ }
+
+ var bytesRemaining = this._buffer.length - this._offset;
+ if (bytesRemaining >= bytes) {
+ return;
+ }
+
+ var oldBuffer = this._buffer;
+
+ this._buffer = new Buffer(oldBuffer.length + bytes);
+ oldBuffer.copy(this._buffer);
+};
366 node_modules/mysql/lib/protocol/Parser.js
View
@@ -0,0 +1,366 @@
+var IEEE_754_BINARY_64_PRECISION = Math.pow(2, 53);
+var MAX_PACKET_LENGTH = Math.pow(2, 24) - 1;
+var PacketHeader = require('./PacketHeader');
+
+module.exports = Parser;
+function Parser(options) {
+ options = options || {};
+
+ this._buffer = new Buffer(0);
+ this._longPacketBuffers = [];
+ this._offset = 0;
+ this._packetEnd = null;
+ this._packetHeader = null;
+ this._onPacket = options.onPacket || function() {};
+ this._nextPacketNumber = 0;
+ this._encoding = 'utf-8';
+ this._paused = false;
+}
+
+Parser.prototype.write = function(buffer) {
+ this.append(buffer);
+
+ while (true) {
+ if (this._paused) {
+ return;
+ }
+
+ if (!this._packetHeader) {
+ if (this._bytesRemaining() < 4) {
+ break;
+ }
+
+ this._packetHeader = new PacketHeader(
+ this.parseUnsignedNumber(3),
+ this.parseUnsignedNumber(1)
+ );
+
+ this._trackAndVerifyPacketNumber(this._packetHeader.number);
+ }
+
+ if (this._bytesRemaining() < this._packetHeader.length) {
+ break;
+ }
+
+ this._packetEnd = this._offset + this._packetHeader.length;
+
+ if (this._packetHeader.length === MAX_PACKET_LENGTH) {
+ this._longPacketBuffers.push(this._buffer.slice(this._offset, this._packetEnd));
+
+ this._advanceToNextPacket();
+ continue;
+ }
+
+ this._combineLongPacketBuffers();
+
+ // Try...finally to ensure exception safety. Unfortunately this is costing
+ // us up to ~10% performance in some benchmarks.
+ var hadException = true;
+ try {
+ this._onPacket(this._packetHeader);
+ hadException = false;
+ } finally {
+ this._advanceToNextPacket();
+
+ // If we had an exception, the parser while loop will be broken out
+ // of after the finally block. So we need to make sure to re-enter it
+ // to continue parsing any bytes that may already have been received.
+ if (hadException) {
+ process.nextTick(this.write.bind(this));
+ }
+ }
+ }
+};
+
+Parser.prototype.append = function(newBuffer) {
+ // If resume() is called, we don't pass a buffer to write()
+ if (!newBuffer) {
+ return;
+ }
+
+ var oldBuffer = this._buffer;
+ var bytesRemaining = this._bytesRemaining();
+ var newLength = bytesRemaining + newBuffer.length;
+
+ var combinedBuffer = (this._offset > newLength)
+ ? oldBuffer.slice(0, newLength)
+ : new Buffer(newLength);
+
+ oldBuffer.copy(combinedBuffer, 0, this._offset);
+ newBuffer.copy(combinedBuffer, bytesRemaining);
+
+ this._buffer = combinedBuffer;
+ this._offset = 0;
+};
+
+Parser.prototype.pause = function() {
+ this._paused = true;
+};
+
+Parser.prototype.resume = function() {
+ this._paused = false;
+
+ // nextTick() to avoid entering write() multiple times within the same stack
+ // which would cause problems as write manipulates the state of the object.
+ process.nextTick(this.write.bind(this));
+};
+
+Parser.prototype.peak = function() {
+ return this._buffer[this._offset];
+};
+
+Parser.prototype.parseUnsignedNumber = function(bytes) {
+ var bytesRead = 0;
+ var value = 0;
+
+ while (bytesRead < bytes) {
+ var byte = this._buffer[this._offset++];
+
+ value += byte * Math.pow(256, bytesRead);
+
+ bytesRead++;
+ }
+
+ return value;
+};
+
+Parser.prototype.parseLengthCodedString = function() {
+ var length = this.parseLengthCodedNumber();
+
+ if (length === null) {
+ return null;
+ }
+
+ return this.parseString(length);
+};
+
+Parser.prototype.parseLengthCodedBuffer = function() {
+ var length = this.parseLengthCodedNumber();
+
+ if (length === null) {
+ return null;
+ }
+
+ return this.parseBuffer(length);
+};
+
+Parser.prototype.parseLengthCodedNumber = function() {
+ var byte = this._buffer[this._offset++];
+
+ if (byte <= 251) {
+ return (byte === 251)
+ ? null
+ : byte;
+ }
+
+ var length;
+ if (byte === 252) {
+ length = 2;
+ } else if (byte === 253) {
+ length = 3;
+ } else if (byte === 254) {
+ length = 8;
+ } else {
+ throw new Error('parseLengthCodedNumber: Unexpected first byte: ' + byte);
+ }
+
+ var value = 0;
+ for (var bytesRead = 0; bytesRead < length; bytesRead++) {
+ var byte = this._buffer[this._offset++];
+ value += Math.pow(256, bytesRead) * byte;
+ }
+
+ if (value >= IEEE_754_BINARY_64_PRECISION) {
+ throw new Error(
+ 'parseLengthCodedNumber: JS precision range exceeded, ' +
+ 'number is >= 53 bit: "' + value + '"'
+ );
+ }
+
+ return value;
+};
+
+Parser.prototype.parseFiller = function(length) {
+ return this.parseBuffer(length);
+};
+
+Parser.prototype.parseNullTerminatedBuffer = function() {
+ var end = this._nullByteOffset();
+ var value = this._buffer.slice(this._offset, end);
+ this._offset = end + 1;
+
+ return value;
+};
+
+Parser.prototype.parseNullTerminatedString = function() {
+ var end = this._nullByteOffset();
+ var value = this._buffer.toString(this._encoding, this._offset, end)
+ this._offset = end + 1;
+
+ return value;
+};
+
+Parser.prototype._nullByteOffset = function() {
+ var offset = this._offset;
+
+ while (this._buffer[offset] !== 0x00) {
+ offset++;
+
+ if (offset >= this._buffer.length) {
+ throw new Error('Offset of null terminated string not found.');
+ }
+ }
+
+ return offset;
+};
+
+Parser.prototype.parsePacketTerminatedString = function() {
+ var length = this._packetEnd - this._offset;
+ return this.parseString(length);
+};
+
+Parser.prototype.parseBuffer = function(length) {
+ var response = new Buffer(length);
+ this._buffer.copy(response, 0, this._offset, this._offset + length);
+
+ this._offset += length;
+ return response;
+};
+
+Parser.prototype.parseString = function(length) {
+ var offset = this._offset;
+ var end = offset + length;
+ var value = this._buffer.toString(this._encoding, offset, end);
+
+ this._offset = end;
+ return value;
+};
+
+Parser.prototype.parseGeometryValue = function() {
+ var buffer = this.parseLengthCodedBuffer();
+ var offset = 4;
+
+ if (buffer === null) {
+ return null;
+ }
+
+ function parseGeometry() {
+ var result = null;
+ var byteOrder = buffer.readUInt8(offset); offset += 1;
+ var wkbType = byteOrder? buffer.readUInt32LE(offset) : buffer.readUInt32BE(offset); offset += 4;
+ switch(wkbType) {