Skip to content

Commit

Permalink
Initial commit.
Browse files Browse the repository at this point in the history
  • Loading branch information
mscdex committed Apr 29, 2010
0 parents commit d1f7608
Show file tree
Hide file tree
Showing 6 changed files with 266 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
@@ -0,0 +1 @@

3 changes: 3 additions & 0 deletions AUTHORS
@@ -0,0 +1,3 @@
# Authors ordered by first contribution.

Brian White <mscdex@gmail.com>
7 changes: 7 additions & 0 deletions LICENSE
@@ -0,0 +1,7 @@
Copyright (C) 2010 Brian White <mscdex@gmail.com>. All rights reserved.

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.
92 changes: 92 additions & 0 deletions README.md
@@ -0,0 +1,92 @@
# node-poormansmysql

node-poormansmysql is a MySQL driver for [node.js](http://nodejs.org/) that executes queries using the mysql command-line client.


# Requirements

* [node.js](http://nodejs.org/) -- tested with [0.1.92](http://github.com/ry/node/commit/caa828a242f39b6158084ef4376355161c14fe34)
* [libxmljs](http://github.com/polotek/libxmljs) -- tested with [7903666cd29001b19fb0](http://github.com/polotek/libxmljs/commit/7903666cd29001b19fb0821f62bcf50f6b5576b3)


# Documentation

MysqlConnection Methods:

* (constructor) **MysqlConnection**(_config_)
* **Description**: Performs initialization and sets the connection configuration. All properties supplied in _config_ overwrite the defaults listed below.
* **Parameters**:
* _config_: (Object) An object containing the necessary connection information to connect to the MySQL server and perform queries. At least the user and password should be supplied. The default configuration is:

user: null,
password: null,
db: null,
host: 'localhost',
port: 3306,
connect_timeout: 0, // in seconds
query_timeout: 0 // in seconds (TODO)
* **Return Value**: (MysqlConnection) An instance of MysqlConnection.
* **setConfig**(_config_)
* **Description**: Sets the connection configuration. This is only needed if you are reusing the instance and need to change the connection information. As with the constructor, all properties supplied in _config_ are merged with the defaults.
* **Parameters**:
* _config_: (Object) An object containing the necessary connection information to connect to the MySQL server and perform queries. At least the user and password should be supplied. See above for the default configuration.
* **Return Value**: None
* **query**(_sql_)
* **Description**: Enqueues an SQL statement.
* **Parameters**:
* _sql_: (String) A valid SQL statement. Do not include a trailing semicolon at the end of the statement.
* **Return Value**: None
* **execute**()
* **Description**: Begins executing the enqueued SQL statements all at once.
* **Parameters**: None
* **Return Value**: (Boolean) _true_ if there were statements to be executed and the 'user' and 'password' connection details have been supplied, _false_ otherwise.
* (getter) **affectedRows**
* **Description**: Returns the number of rows updated, inserted, or deleted by the last SQL statement.
* **Return Value**: (Integer) The number of rows affected by the last SQL statement.
* (getter) **lastInsertId**
* **Description**: Returns the first automatically generated value that was set for an AUTO_INCREMENT column by the most recent INSERT statement. See [here](http://dev.mysql.com/doc/refman/5.0/en/information-functions.html#function_last-insert-id) for more details.
* **Return Value**: (Integer) The value of the AUTO_INCREMENT column for the last INSERTed row.

MysqlConnection Events:

* **error**
* **Description**: Fired when an error occurs. This can be either a MySQL error or a child process spawning error.
* **Parameters**:
* _err_: (String) The error details.
* **row**
* **Description**: Fired when a row has been generated for the given query.
* **Parameters**:
* _rowdata_: (Object) A hash containing a single row from the results of a query.
* _sql_: (String) The original SQL (SELECT) statement that generated the row.
* **queryDone**
* **Description**: Fired when the given query has completed and any/all resulting rows for the given query have been emitted.
* **Parameters**:
* _sql_: (String) The original SQL (SELECT) statement that generated the row.
* **done**
* **Description**: Fired when <u>**all**</u> queries have completed and any/all resulting rows for each query have been emitted.
* **Parameters**: None

# Example

var pmm = require('./node-poormansmysql'), sys = require('sys');

var conn = new pmm.MysqlConnection({user: 'foo', password: 'bar', db: 'baz'});
conn.addListener('error', function(err) {
sys.puts('Uh oh, ' + err);
});
conn.addListener('row', function(rowdata, sql) {
sys.puts("'" + sql + "' generated a row: " + sys.inspect(row));
});
conn.addListener('queryDone', function(sql) {
sys.puts("Done with query: " + sql);
});
conn.addListener('done', function() {
sys.puts('Done executing all SQL statements!');
});
conn.query("SELECT * FROM table");
conn.execute();


# License

See LICENSE file.
17 changes: 17 additions & 0 deletions example.js
@@ -0,0 +1,17 @@
var pmm = require('./node-poormansmysql'), sys = require('sys');

var conn = new pmm.MysqlConnection({user: 'foo', password: 'bar', db: 'baz'});
conn.addListener('error', function(err) {
sys.puts('Uh oh, ' + err);
});
conn.addListener('row', function(rowdata, sql) {
sys.puts("'" + sql + "' generated a row: " + sys.inspect(row));
});
conn.addListener('queryDone', function(sql) {
sys.puts("Done with query: " + sql);
});
conn.addListener('done', function() {
sys.puts('Done executing all SQL statements!');
});
conn.query("SELECT * FROM table");
conn.execute();
146 changes: 146 additions & 0 deletions node-poormansmysql.js
@@ -0,0 +1,146 @@
var spawn = require('child_process').spawn;
var libxml = require('./libxmljs');
var inherits = require('sys').inherits;
var EventEmitter = require('events').EventEmitter;

function MysqlConnection(newconfig) {
var default_config = {
user: null,
password: null,
db: null,
host: 'localhost',
port: 3306,
connect_timeout: 0, // in seconds
query_timeout: 0 // in seconds (TODO)
};
var config;

var queries = [];

var _affectedRows = 0;
var _lastInsertId = 0; // 0 means no row(s) inserted
var isInserting = false;

/* Bookkeeping */
var curQuery = null;
var curRow = null;
var curField = null;
var queryTotal = 0;
var queryCount = 0;
var lastIndex = 0;

var lastError = null;

var proc = null;
var parser;

var self = this;

/* Public Methods */

this.setConfig = function(newconfig) {
config = {};
for (var option in default_config)
config[option] = (typeof newconfig[option] != "undefined" ? newconfig[option] : default_config[option]);
}

this.query = function(sql) {
sql = sql.replace(/^\s*/, "").replace(/\s*$/, "").replace('"', '\"');
queries.push(sql);
if (sql.substr(0, 6).toUpperCase() == "INSERT")
isInserting = true;
}

this.execute = function() {
if (queries.length > 0 && config.user != null && config.password != null) {
queries.push("SELECT ROW_COUNT() AS _node_poormansmysql_affectedRows");
if (isInserting) {
queries.push("SELECT LAST_INSERT_ID() AS _node_poormansmysql_lastInsertId");
lastIndex = queries.length-3;
} else
lastIndex = queries.length-2;

proc = spawn('/bin/sh', ['-c', 'mysql --xml --quick --disable-auto-rehash --connect_timeout=' + config.connect_timeout + ' --host=' + config.host + ' --port=' + config.port + ' --user=' + config.user + ' --password=' + config.password + (config.db != null ? ' --database=' + config.db : '') + ' --execute="' + queries.join('; ') + '"']);

proc.stdout.setEncoding('utf8');
proc.stderr.setEncoding('utf8');

proc.stdout.addListener('data', function(data) {
// UGLY HACK: There is one XML declaration for each MySQL query, so remove the extra ones where applicable, otherwise libxml throws a fit.
var header = "<?xml version=\"1.0\"?>\n";
if (queryCount == 0 && data.substr(0, header.length) == header)
data = header + "<results>\n" + data.substr(header.length);
data = data.replace("\n" + header, "");
parser.push(data);
});
proc.stderr.addListener('data', function(data) {
if (/^execvp\(\)/.test(data))
lastError = 'Spawn Error: Failed to start child process.';
else
lastError = 'MySQL Error: ' + data;

self.emit('error', lastError);
});
proc.addListener('exit', function(code) {
if (code == 0) {
parser.push("</results>");
queryCount = 0;
}
});

queryTotal = queries.length;
queries = [];
isInserting = false;
return true;
} else
return false;
}

this.__defineGetter__('affectedRows', function () { return _affectedRows; });
this.__defineGetter__('lastInsertId', function () { return _lastInsertId; });

/* Initialization */

this.setConfig(newconfig);
parser = new libxml.SaxPushParser(function(cb) {
cb.onStartElementNS(function(elem, attrs, prefix, uri, namespaces) {
if (elem == "resultset")
curQuery = attrs[0][3];
else if (elem == "row")
curRow = {};
else if (elem == "field") {
curField = attrs[0][3];
curRow[curField] = null;
}
});
cb.onEndElementNS(function(elem, prefix, uri) {
if (elem == "resultset") {
if (queryCount <= lastIndex)
self.emit('queryDone', curQuery);
curQuery = null;
if (++queryCount == queryTotal)
self.emit('done');
} else if (elem == "row") {
if (typeof curRow['_node_poormansmysql_lastInsertId'] != 'undefined')
_lastInsertId = curRow['_node_poormansmysql_lastInsertId'];
else if (typeof curRow['_node_poormansmysql_affectedRows'] != 'undefined')
_affectedRows = curRow['_node_poormansmysql_affectedRows'];
else
self.emit('row', curRow, curQuery);
curRow = null;
} else if (elem == "field")
curField = null;
});
cb.onCharacters(function(chars) {
if (curField != null) {
if (curRow[curField] == null)
curRow[curField] = chars;
else
curRow[curField] += chars;
}
});
});
};
inherits(MysqlConnection, EventEmitter);

exports.MysqlConnection = MysqlConnection;

0 comments on commit d1f7608

Please sign in to comment.