Skip to content
Browse files

fix #16

  • Loading branch information...
1 parent 0998cf5 commit 9d903de603c4f8803e1123a0b799bc91d2ac07de @hgourvest committed Nov 4, 2012
Showing with 637 additions and 401 deletions.
  1. +91 −47 README.md
  2. +400 −255 lib/index.js
  3. +1 −1 package.json
  4. +145 −98 test/test.js
View
138 README.md
@@ -1,6 +1,8 @@
# node-firebird
-Pure javascript and asynchronous Firebird client for Node.js
+Pure javascript and asynchronous Firebird client for Node.js.
+
+If you are new to Firebird you will find useful documentation [here] [1].
## Install
@@ -11,70 +13,112 @@ Pure javascript and asynchronous Firebird client for Node.js
### Connecting
fb = require("node-firebird");
- fb.attach('127.0.0.1', 3050, 'database.fdb', 'SYSDBA', 'masterkey', 'role',
- function(db){
- database = db;
- console.log("connected");
- },
- function(error){
- console.log("can't connect");
+ fb.attach(
+ {
+ host: '127.0.0.1',
+ database: 'database.fdb',
+ user: 'SYSDBA',
+ password: 'masterkey'
+ },
+ function(err, db){
+ if (err) {
+ console.log(err.message);
+ } else {
+ database = db;
+ console.log("connected");
+ }
}
);
### Querying
#### Simple query
- database.execute("select cast(? as integer) from rdb$database", [123],
- function (result) {
- console.log(result.data)
+ database.query("select cast(? as integer) from rdb$database", 123,
+ function (err, result) {
+ console.log(result)
}
);
-The transaction automatically started and commited/rollbacked.
+The transaction automatically started, commited or rollbacked.
- query is a non optional string.
- params is optional, can be a single value or an array.
-- callback & error are optional.
+- callback is optional.
### Using transaction
- var tr;
-
- function fail(err) {
- tr.rollback();
- console.log(err.message);
- }
-
- database.startTransaction(function(transaction) {
- tr = transaction;
- tr.execute("select cast(? as integer) from rdb$database", 123, function(result1) {
- tr.execute("select cast(? as integer) from rdb$database", 456, function(result2) {
- tr.commit(function(ret) {
- console.log(result1.data[0]);
- console.log(result2.data[0]);
- }, fail)
- }, fail);
- }, fail);
- })
+ function checkError(err) {
+ if (err) {
+ throw new Error(err.message)
+ }
+ }
+ function check(tr, callback){
+ return function(err, param) {
+ if (!err) {
+ callback(err, param);
+ } else {
+ tr.rollback();
+ throw new Error(err.message)
+ }
+ }
+ }
+
+ database.startTransaction(
+ function(err, transaction) {
+ checkError(err);
+ transaction.query("select cast(? as integer) from rdb$database", 123,
+ check(transaction, function(err, result1) {
+ transaction.query("select cast(? as integer) from rdb$database", 456,
+ check(transaction, function(err, result2) {
+ transaction.commit(
+ function(err) {
+ checkError(err);
+ console.log(result1[0]);
+ console.log(result2[0]);
+ }
+ )
+ })
+ );
+ })
+ );
+ }
+ )
+
+### Arrays or Objects ?
+
+The common usage is to fetch records as objects
+
+ database.query(...)
+
+You can also fetch records as arrays
+
+ database.execute(...)
+
+In this case you can retrieve fields name in the callback
+
+ function(err, rows, fields){...}
+
+You can do the same on transactions.
### Errors handling
-Most async methods can trigger a callback and an error event, they are optionnals. If an error occur the error event will be called, if no error event is provided, the error will be sent to the callback event and you will have to check if the result is an error. An error object have a status property.
+This is a typical error object:
- function CheckResult(obj) {
- if (obj.status) {
- throw new Error(obj.message)
- }
- }
-
- database.startTransaction(function(transaction) {
- transaction.execute("select cast(? as integer) from rdb$database", 123, function(result) {
- transaction.commit(function(ret) { // commit in all situations for a single query
- CheckResult(result); // error executing query ?
- CheckResult(ret); // error commiting ?
- console.log(result.data);
- })
- });
- })
+ {
+ status: [
+ {gdscode: 335544569}, // Dynamic SQL Error
+ {gdscode: 335544436, params: [-104]}, // SQL error code = -104
+ {gdscode: 335544634, params: [1,31]}, // Token unknown - line 1, column 31
+ {gdscode: 335544382, params: ["m"]} // m
+ ],
+ sqlcode: -104,
+ message: "Dynamic SQL Error, SQL error code = -104, Token unknown - line 1, column 31, m"
+ }
+
+- The first gdscode value is the most significant error.
+- The sqlcode value is extracted from status vector.
+- The message string is built using firebrd.msg file.
+
+ [1]: http://www.firebirdsql.org/en/documentation/
View
655 lib/index.js
@@ -68,26 +68,26 @@ const
// DSQL operations
- op_allocate_statement = 62, // allocate a statment handle
+ op_allocate_statement = 62, // allocate a statment handle
op_execute = 63, // execute a prepared statement
op_exec_immediate = 64, // execute a statement
op_fetch = 65, // fetch a record
op_fetch_response = 66, // response for record fetch
op_free_statement = 67, // free a statement
- op_prepare_statement = 68, // prepare a statement
+ op_prepare_statement = 68, // prepare a statement
op_set_cursor = 69, // set a cursor name
op_info_sql = 70,
op_dummy = 71, // dummy packet to detect loss of client
- op_response_piggyback = 72, // response block for piggybacked messages
- op_start_and_receive = 73,
- op_start_send_and_receive = 74,
+ op_response_piggyback = 72, // response block for piggybacked messages
+ op_start_and_receive = 73,
+ op_start_send_and_receive = 74,
op_exec_immediate2 = 75, // execute an immediate statement with msgs
op_execute2 = 76, // execute a statement with msgs
op_insert = 77,
op_sql_response = 78, // response from execute, exec immed, insert
op_transact = 79,
- op_transact_response = 80,
+ op_transact_response = 80,
op_drop_database = 81,
op_service_attach = 82,
op_service_detach = 83,
@@ -428,6 +428,13 @@ const
ISOLATION_SERIALIZABLE = [isc_tpb_version3, isc_tpb_write, isc_tpb_wait, isc_tpb_consistency],
ISOLATION_READ_COMMITED_READ_ONLY = [isc_tpb_version3, isc_tpb_read, isc_tpb_wait, isc_tpb_read_committed, isc_tpb_no_rec_version];
+const
+ DEFAULT_HOST = '127.0.0.1',
+ DEFAULT_PORT = 3050,
+ DEFAULT_USER = 'SYSDBA',
+ DEFAULT_PASSWORD = 'masterkey',
+ DEFAULT_PAGE_SIZE = 4096;
+
exports.ISOLATION_READ_UNCOMMITTED = ISOLATION_READ_UNCOMMITTED;
exports.ISOLATION_READ_COMMITED = ISOLATION_READ_COMMITED;
exports.ISOLATION_REPEATABLE_READ = ISOLATION_REPEATABLE_READ;
@@ -462,10 +469,11 @@ const
function SQLVarText() {}
SQLVarText.prototype.decode = function(data) {
+ var ret;
if (this.subType > 1) {
- var ret = data.readText(this.length, DEFAULT_ENCODING);
+ ret = data.readText(this.length, DEFAULT_ENCODING);
} else {
- var ret = data.readBuffer(this.length);
+ ret = data.readBuffer(this.length);
}
if (!data.readInt()) {
@@ -490,10 +498,11 @@ SQLVarNull.prototype.constructor = SQLVarNull;
function SQLVarString() {}
SQLVarString.prototype.decode = function(data) {
+ var ret;
if (this.subType > 1) {
- var ret = data.readString(DEFAULT_ENCODING)
+ ret = data.readString(DEFAULT_ENCODING)
} else {
- var ret = data.readBuffer()
+ ret = data.readBuffer()
}
if (!data.readInt()) {
return ret;
@@ -636,7 +645,7 @@ SQLVarDate.prototype.decode = function(data) {
var ret = data.readInt();
if (!data.readInt()) {
var d = new Date(0);
- d.setMilliseconds((ret - DateOffset) * TimeCoeff + d.getTimezoneOffset() * MsPerMinute)
+ d.setMilliseconds((ret - DateOffset) * TimeCoeff + d.getTimezoneOffset() * MsPerMinute);
return d;
}
return null;
@@ -654,7 +663,7 @@ SQLVarTime.prototype.decode = function(data) {
var ret = data.readUInt();
if (!data.readInt()) {
var d = new Date(0);
- d.setMilliseconds(Math.floor(ret / 10) + d.getTimezoneOffset() * MsPerMinute)
+ d.setMilliseconds(Math.floor(ret / 10) + d.getTimezoneOffset() * MsPerMinute);
return d;
}
return null;
@@ -734,10 +743,10 @@ SQLParamInt64.prototype.calcBlr = function(blr) {
SQLParamInt64.prototype.encode = function(data) {
if (this.value != null) {
- data.addLong(this.value);
+ data.addInt64(this.value);
data.addInt(0);
} else {
- data.addLong(0);
+ data.addInt64(0);
data.addInt(1);
}
};
@@ -864,11 +873,19 @@ function isError(obj) {
return (obj instanceof Object && obj.status)
}
-function doCallback(obj, callback, error) {
- if (isError(obj) && error) {
- error(obj)
- } else if (callback) {
- callback(obj)
+function doCallback(obj, callback) {
+ if (callback) {
+ if (isError(obj)) {
+ callback(obj)
+ } else {
+ callback(undefined, obj)
+ }
+ }
+}
+
+function doError(obj, callback) {
+ if (callback) {
+ callback(obj)
}
}
@@ -882,29 +899,28 @@ function Statement(connection) {
this.connection = connection;
}
-Statement.prototype.close = function(callback, error) {
- this.connection.closeStatement(this, callback, error);
+Statement.prototype.close = function(callback) {
+ this.connection.closeStatement(this, callback);
};
-Statement.prototype.drop = function(callback, error) {
- this.connection.dropStatement(this, callback, error);
+Statement.prototype.drop = function(callback) {
+ this.connection.dropStatement(this, callback);
};
-Statement.prototype.execute = function(transaction, params, callback, error){
+Statement.prototype.execute = function(transaction, params, callback){
if (params instanceof Function) {
- error = callback;
callback = params;
params = null;
}
- this.connection.executeStatement(transaction, this, params, callback, error);
+ this.connection.executeStatement(transaction, this, params, callback);
};
-Statement.prototype.fetch = function(transaction, count, callback, error) {
- this.connection.fetch(this, transaction, count, callback, error);
+Statement.prototype.fetch = function(transaction, count, callback) {
+ this.connection.fetch(this, transaction, count, callback);
};
-Statement.prototype.fetchAll = function(transaction, callback, error) {
- this.connection.fetchAll(this, transaction, callback, error);
+Statement.prototype.fetchAll = function(transaction, callback) {
+ this.connection.fetchAll(this, transaction, callback);
};
@@ -918,76 +934,90 @@ function Transaction(connection) {
this.connection = connection;
}
-Transaction.prototype.newStatement = function(query, callback, error) {
+Transaction.prototype.newStatement = function(query, callback) {
var cnx = this.connection;
var self = this;
- cnx.allocateStatement(function(statement) {
- cnx.prepareStatement(self, statement, query, false, callback, error);
- }, error)
+ cnx.allocateStatement(
+ function(err, statement) {
+ if (err) {doError(err, callback); return}
+ cnx.prepareStatement(self, statement, query, false, callback);
+ }
+ )
};
-Transaction.prototype.execute = function(query, params, callback, error) {
+Transaction.prototype.execute = function(query, params, callback) {
if (params instanceof Function) {
- error = callback;
callback = params;
params = null;
}
var self = this;
this.newStatement(query,
- function(statement) {
- function dropError(ret) {
+ function(err, statement) {
+ if (err) {doError(err, callback); return}
+ function dropError(err) {
statement.drop();
- doCallback(ret, callback, error);
+ doCallback(err, callback);
}
- statement.execute(self, params, function() {
- switch (statement.type) {
- case isc_info_sql_stmt_select:
- statement.fetchAll(self, function(ret) {
+ statement.execute(self, params,
+ function(err) {
+ if (err) {dropError(err); return}
+ switch (statement.type) {
+ case isc_info_sql_stmt_select:
+ statement.fetchAll(self,
+ function(err, ret) {
+ if (err) {dropError(err); return}
+ statement.drop();
+ if (callback) {
+ callback(undefined, ret, statement.output, true);
+ }
+ }
+ );
+ break;
+ case isc_info_sql_stmt_exec_procedure:
+ if (statement.output.length) {
+ statement.fetch(self, 1,
+ function(err, ret) {
+ if (err) {dropError(err); return}
+ statement.drop();
+ if (callback) {
+ callback(undefined, ret.data[0], statement.output, false);
+ }
+ }
+ );
+ break;
+ }
+ // Fall through is normal
+ default:
statement.drop();
if (callback) {
- callback({meta: statement.output, data: ret})
+ callback()
}
- }, dropError);
- break;
- case isc_info_sql_stmt_exec_procedure:
- if (statement.output.length) {
- statement.fetch(self, 1, function(ret) {
- statement.drop();
- if (callback) {
- callback({meta: statement.output, data: ret.data[0]})
- }
- }, dropError);
- break;
- }
- default:
- statement.drop();
- if (callback) {
- callback()
- }
+ }
}
- }, dropError)
- },
- function(err) {
- // do not try to use invalid statement
- doCallback(err, callback, error);
- })
+ )
+ }
+ )
};
-Transaction.prototype.commit = function(callback, error) {
- this.connection.commit(this, callback, error)
+Transaction.prototype.query = function(query, params, callback) {
+ this.execute(query, params, mapFieldNames(callback));
};
-Transaction.prototype.rollback = function(callback, error) {
- this.connection.rollback(this, callback, error)
+Transaction.prototype.commit = function(callback) {
+ this.connection.commit(this, callback)
};
-Transaction.prototype.commitRetaining = function(callback, error) {
- this.connection.commitRetaining(this, callback, error)
+Transaction.prototype.rollback = function(callback) {
+ this.connection.rollback(this, callback)
};
-Transaction.prototype.rollbackRetaining = function(callback, error) {
- this.connection.rollbackRetaining(this, callback, error)
+Transaction.prototype.commitRetaining = function(callback) {
+ this.connection.commitRetaining(this, callback)
+};
+
+Transaction.prototype.rollbackRetaining = function(callback) {
+ this.connection.rollbackRetaining(this, callback)
};
/***************************************
@@ -1000,87 +1030,175 @@ function Database(connection) {
this.connection = connection;
}
-Database.prototype.detach = function(callback, error) {
+Database.prototype.detach = function(callback) {
var self = this;
this.connection.detach(
- function(obj) {
+ function(err, obj) {
self.connection.disconnect();
- doCallback(obj, callback, error)
+ if (callback) {
+ callback(err, obj);
+ }
}
);
-
};
-Database.prototype.startTransaction = function(isolation, callback, error) {
- this.connection.startTransaction(isolation, callback, error);
+Database.prototype.startTransaction = function(isolation, callback) {
+ this.connection.startTransaction(isolation, callback);
};
-Database.prototype.newStatement = function (query, callback, error) {
- this.startTransaction(function(transaction) {
- transaction.newStatement(query, function(statement) {
- transaction.commit(function() {
- callback(statement);
- }, error);
- }, error);
- }, error)
+Database.prototype.newStatement = function (query, callback) {
+ this.startTransaction(
+ function(err, transaction) {
+ if (err) {callback(err); return}
+ transaction.newStatement(query,
+ function(err, statement) {
+ if (err) {callback(err); return}
+ transaction.commit(
+ function(err) {
+ callback(err, statement);
+ }
+ );
+ }
+ );
+ }
+ )
+};
+
+Database.prototype.execute = function(query, params, callback) {
+ this.connection.startTransaction(
+ function(err, transaction) {
+ if (err) {doError(err, callback); return}
+ transaction.execute(query, params,
+ function(err, result, meta, isSelect) {
+ if (err) {
+ transaction.rollback(
+ function() {
+ doError(err, callback);
+ }
+ )
+ } else {
+ transaction.commit(
+ function(err) {
+ if (callback) {
+ callback(err, result, meta, isSelect);
+ }
+ }
+ );
+ }
+ }
+ )
+ }
+ )
};
-Database.prototype.execute = function(query, params, callback, error) {
- this.connection.startTransaction(function(transaction) {
- transaction.execute(query, params,
- function(result) {
- transaction.commit(function() {
- if (callback) {
- callback(result)
+function mapFieldNames(callback) {
+ if (callback) {
+ return function(err, result, meta, isSelect) {
+ if (err) {
+ doError(err, callback);
+ } else {
+ if (result) {
+ var lower = new Array(meta.length);
+ for (var k = 0; k < meta.length; k++) {
+ lower[k] = meta[k].alias.toLowerCase();
+ }
+ function convert(value) {
+ var obj = {};
+ for (var j = 0; j < lower.length; j++) {
+ obj[lower[j]] = value[j];
+ }
+ return obj;
}
- }, error);
- },
- function(result) {
- transaction.rollback(function() {
- if (error) {
- error(result)
- } else
- if (callback) {
- callback(result);
+ if (isSelect) {
+ var item;
+ for (var i = 0; i < result.length; i++) {
+ result[i] = convert(result[i]);
+ }
+ callback(undefined, result, meta, isSelect);
+ } else {
+ callback(undefined, convert(result), meta, isSelect);
}
- }, error)
- })
- }, error)
+ } else {
+ callback();
+ }
+ }
+ }
+ } else {
+ return undefined;
+ }
+}
+
+Database.prototype.query = function(query, params, callback) {
+ this.execute(query, params, mapFieldNames(callback));
};
-exports.attach = function(host, port, database, user, password, callback, error){
- var cnx = this.connection = new Connection(host, port);
- cnx.connect(database, function(ret){
- if (!ret.status) {
- cnx.attach(database, user, password, callback, error);
- } else {
- doCallback(ret, callback, error)
+exports.attach = function(options, callback){
+ var host = options.host || DEFAULT_HOST;
+ var port = options.port || DEFAULT_PORT;
+ var cnx = this.connection = new Connection(host, port,
+ function(err) {
+ if (!err) {
+ cnx.connect(options.database,
+ function(err){
+ if (!err) {
+ cnx.attach(options, callback);
+ } else {
+ doError(err, callback)
+ }
+ }
+ );
+ } else {
+ doError(err, callback)
+ }
}
- }, error);
+ )
};
-exports.create = function(host, port, database, user, password, pageSize, callback, error) {
+exports.create = function(options, callback) {
+ var host = options.host || DEFAULT_HOST;
+ var port = options.port || DEFAULT_PORT;
var cnx = this.connection = new Connection(host, port);
- cnx.connect(database, function(ret){
- if (!ret.status) {
- cnx.createDatabase(filename, user, password, pageSize, callback, error);
- } else {
- doCallback(ret, callback, error)
+ cnx.connect(options.database,
+ function(err){
+ if (!err) {
+ cnx.createDatabase(options, callback);
+ } else {
+ doError(err, callback)
+ }
}
- }, error);
+ );
};
-exports.attachOrCreate = function(host, port, database, user, password, pageSize, role, callback, error) {
- var cnx = this.connection = new Connection(host, port);
- cnx.connect(database, function(ret){
- if (!ret.status) {
- cnx.attach(database, user, password, role, callback, function() {
- cnx.createDatabase(database, user, password, pageSize, role, callback, error);
- });
- } else {
- doCallback(ret, callback, error)
+exports.attachOrCreate = function(options, callback) {
+ var host = options.host || DEFAULT_HOST;
+ var port = options.port || DEFAULT_PORT;
+ var cnx = this.connection = new Connection(host, port,
+ function(err) {
+ if (err) {
+ callback({error: err, message: "Connect error"});
+ return;
+ }
+ cnx.connect(options.database,
+ function(err){
+ if (!err) {
+ cnx.attach(options,
+ function(err, ret) {
+ if (!err) {
+ doCallback(ret, callback);
+ } else {
+ cnx.createDatabase(options, callback);
+ }
+ }
+ );
+ } else {
+ doError(err, callback)
+ }
+ }
+ );
}
- }, error);
+ );
+
+
};
/***************************************
@@ -1089,13 +1207,15 @@ exports.attachOrCreate = function(host, port, database, user, password, pageSize
*
***************************************/
-var Connection = exports.Connection = function (host, port){
+var Connection = exports.Connection = function (host, port, callback){
this._msg = new XdrWriter(32);
this._blr = new BlrWriter(32);
- this._queue = new Array();
+ this._queue = [];
this._socket = net.createConnection(port, host);
var self = this;
- this._socket.on('data', function(data) {
+ this._socket.on('error', callback);
+ this._socket.on('connect', callback);
+ this._socket.on('data', function(data) {
var obj, cb, pos, xdr, buf;
if (!self._xdr) {
xdr = new XdrReader(data)
@@ -1112,7 +1232,7 @@ var Connection = exports.Connection = function (host, port){
pos = xdr.pos;
try {
cb = self._queue[0];
- obj = decodeResponse(xdr, cb.callback);
+ obj = decodeResponse(xdr, cb);
} catch(err) {
buf = new Buffer(xdr.buffer.length - pos);
xdr.buffer.copy(buf, 0, pos);
@@ -1126,11 +1246,11 @@ var Connection = exports.Connection = function (host, port){
messages.lookupMessages(obj.status,
function(message){
obj.message = message;
- doCallback(obj, cb.callback, cb.error);
+ doCallback(obj, cb);
}
)
} else {
- doCallback(obj, cb.callback, cb.error);
+ doCallback(obj, cb);
}
}
});
@@ -1235,13 +1355,12 @@ function decodeResponse(data, callback){
}
}
-Connection.prototype._queueEvent = function(callback, error){
- this._queue.push({callback: callback, error: error});
+Connection.prototype._queueEvent = function(callback){
+ this._queue.push(callback);
this._socket.write(this._msg.getData());
};
-
-Connection.prototype.connect = function (filename, callback, error) {
+Connection.prototype.connect = function (database, callback) {
var msg = this._msg;
var blr = this._blr;
msg.pos = 0;
@@ -1251,7 +1370,7 @@ Connection.prototype.connect = function (filename, callback, error) {
msg.addInt(op_attach);
msg.addInt(CONNECT_VERSION2);
msg.addInt(ARCHITECTURE_GENERIC);
- msg.addString(filename || '', DEFAULT_ENCODING);
+ msg.addString(database || '', DEFAULT_ENCODING);
msg.addInt(1); // Protocol version understood count.
blr.addString(1, process.env['USER'] || process.env['USERNAME'] || "Unknown", DEFAULT_ENCODING);
@@ -1266,11 +1385,16 @@ Connection.prototype.connect = function (filename, callback, error) {
msg.addInt(ptype_batch_send); // Max type
msg.addInt(2); // Preference weight
- this._queueEvent(callback, error);
+ this._queueEvent(callback);
};
-Connection.prototype.attach = function (filename, user, password, role, callback, error) {
+Connection.prototype.attach = function (options, callback) {
+ var database = options.database;
+ var user = options.user || DEFAULT_USER;
+ var password = options.password || DEFAULT_PASSWORD;
+ var role = options.role;
+
var msg = this._msg;
var blr = this._blr;
msg.pos = 0;
@@ -1286,31 +1410,44 @@ Connection.prototype.attach = function (filename, user, password, role, callback
msg.addInt(op_attach);
msg.addInt(0); // Database Object ID
- msg.addString(filename, DEFAULT_ENCODING);
+ msg.addString(database, DEFAULT_ENCODING);
msg.addBlr(this._blr);
var self = this;
- function cb(ret) {
+ function cb(err, ret) {
+ if (err) {doError(err, callback); return}
self.dbhandle = ret.handle;
- doCallback(ret, callback, error);
+ if (callback) {
+ callback(undefined, ret);
+ }
}
cb.response = new Database(this);
- this._queueEvent(cb, error);
+ this._queueEvent(cb);
};
-Connection.prototype.detach = function (callback, error) {
+Connection.prototype.detach = function (callback) {
var msg = this._msg;
msg.pos = 0;
msg.addInt(op_detach);
msg.addInt(0); // Database Object ID
var self = this;
- this._queueEvent(function (ret) {
- delete(self.dbhandle);
- doCallback(ret, callback, error);
- }, error);
+ this._queueEvent(
+ function (err, ret) {
+ delete(self.dbhandle);
+ if (callback) {
+ callback(err, ret);
+ }
+ }
+ );
};
-Connection.prototype.createDatabase = function (filename, user, password, pageSize, role, callback, error) {
+Connection.prototype.createDatabase = function (options, callback) {
+ var database = options.database;
+ var user = options.user || DEFAULT_USER;
+ var password = options.password || DEFAULT_PASSWORD;
+ var pageSize = options.pageSize || DEFAULT_PAGE_SIZE;
+ var role = options.role;
+
var blr = this._blr;
blr.pos = 0;
blr.addByte(1);
@@ -1330,25 +1467,29 @@ Connection.prototype.createDatabase = function (filename, user, password, pageSi
msg.pos = 0;
msg.addInt(op_create); // op_create
msg.addInt(0); // Database Object ID
- msg.addString(filename, DEFAULT_ENCODING);
+ msg.addString(database, DEFAULT_ENCODING);
msg.addBlr(blr);
var self = this;
- function cb(ret) {
- self.dbhandle = ret.handle;
- doCallback(ret, callback, error);
+ function cb(err, ret) {
+ if (ret) {
+ self.dbhandle = ret.handle;
+ }
+
+ if (callback) {
+ callback(err, ret);
+ }
}
cb.response = new Database(this);
- this._queueEvent(cb, error);
+ this._queueEvent(cb);
};
-Connection.prototype.startTransaction = function (isolation, callback, error) {
+Connection.prototype.startTransaction = function (isolation, callback) {
var blr = this._blr;
var msg = this._msg;
blr.pos = 0;
msg.pos = 0;
if (isolation instanceof Function) {
- error = callback;
callback = isolation;
isolation = null;
}
@@ -1358,66 +1499,66 @@ Connection.prototype.startTransaction = function (isolation, callback, error) {
msg.addInt(this.dbhandle);
msg.addBlr(blr);
callback.response = new Transaction(this);
- this._queueEvent(callback, error);
+ this._queueEvent(callback);
};
-Connection.prototype.commit = function (transaction, callback, error) {
+Connection.prototype.commit = function (transaction, callback) {
var msg = this._msg;
msg.pos = 0;
msg.addInt(op_commit);
msg.addInt(transaction.handle);
- this._queueEvent(callback, error);
+ this._queueEvent(callback);
};
-Connection.prototype.rollback = function (transaction, callback, error) {
+Connection.prototype.rollback = function (transaction, callback) {
var msg = this._msg;
msg.pos = 0;
msg.addInt(op_rollback);
msg.addInt(transaction.handle);
- this._queueEvent(callback, error);
+ this._queueEvent(callback);
};
-Connection.prototype.commitRetaining = function (transaction, callback, error) {
+Connection.prototype.commitRetaining = function (transaction, callback) {
var msg = this._msg;
msg.pos = 0;
msg.addInt(op_commit_retaining);
msg.addInt(transaction.handle);
- this._queueEvent(callback, error);
+ this._queueEvent(callback);
};
-Connection.prototype.rollbackRetaining = function (transaction, callback, error) {
+Connection.prototype.rollbackRetaining = function (transaction, callback) {
var msg = this._msg;
msg.pos = 0;
msg.addInt(op_rollback_retaining);
msg.addInt(transaction.handle);
- this._queueEvent(callback, error);
+ this._queueEvent(callback);
};
-Connection.prototype.allocateStatement = function (callback, error) {
+Connection.prototype.allocateStatement = function (callback) {
var msg = this._msg;
msg.pos = 0;
msg.addInt(op_allocate_statement);
msg.addInt(this.dbhandle);
callback.response = new Statement(this);
- this._queueEvent(callback, error);
+ this._queueEvent(callback);
};
-Connection.prototype.dropStatement = function (statement, callback, error) {
+Connection.prototype.dropStatement = function (statement, callback) {
var msg = this._msg;
msg.pos = 0;
msg.addInt(op_free_statement);
msg.addInt(statement.handle);
msg.addInt(DSQL_drop);
- this._queueEvent(callback, error);
+ this._queueEvent(callback);
};
-Connection.prototype.closeStatement = function (statement, callback, error) {
+Connection.prototype.closeStatement = function (statement, callback) {
var msg = this._msg;
msg.pos = 0;
msg.addInt(op_free_statement);
msg.addInt(statement.handle);
msg.addInt(DSQL_close);
- this._queueEvent(callback, error);
+ this._queueEvent(callback);
};
function describe(ret, statement){
@@ -1518,13 +1659,12 @@ function describe(ret, statement){
}
}
-Connection.prototype.prepareStatement = function (transaction, statement, query, plan, callback, error) {
+Connection.prototype.prepareStatement = function (transaction, statement, query, plan, callback) {
var msg = this._msg;
var blr = this._blr;
msg.pos = 0;
blr.pos = 0;
if (plan instanceof Function) {
- error = callback;
callback = plan;
plan = false;
}
@@ -1539,14 +1679,18 @@ Connection.prototype.prepareStatement = function (transaction, statement, query,
msg.addBlr(blr);
msg.addInt(65535); // buffer_length
- this._queueEvent(function (ret) {
- if (!ret.status){
- describe(ret, statement);
- statement.query = query;
- ret = statement;
+ this._queueEvent(
+ function(err, ret) {
+ if (!err){
+ describe(ret, statement);
+ statement.query = query;
+ ret = statement;
+ }
+ if (callback) {
+ callback(err, ret);
+ }
}
- doCallback(ret, callback, error);
- }, error);
+ );
};
function CalcBlr(blr, xsqlda) {
@@ -1631,13 +1775,12 @@ function PrepareParams(params, input) {
return ret;
}
-Connection.prototype.executeStatement = function(transaction, statement, params, callback, error){
+Connection.prototype.executeStatement = function(transaction, statement, params, callback){
var msg = this._msg;
var blr = this._blr;
msg.pos = 0;
blr.pos = 0;
if (params instanceof Function) {
- error = callback;
callback = params;
params = null;
}
@@ -1670,10 +1813,10 @@ Connection.prototype.executeStatement = function(transaction, statement, params,
msg.addInt(0); // message number
msg.addInt(0); // param count
}
- this._queueEvent(callback, error);
+ this._queueEvent(callback);
};
-function fetchBlobs(statement, transaction, rows, callback, error) {
+function fetchBlobs(statement, transaction, rows, callback) {
if (rows.data && rows.data.length) {
var indexes = [];
for (var i = 0; i < statement.output.length; i++) {
@@ -1682,38 +1825,44 @@ function fetchBlobs(statement, transaction, rows, callback, error) {
}
}
if (indexes.length) {
- function fetch(row, col, callback, error) {
+ function fetch(row, col, callback) {
var blobid = rows.data[row][col];
if (blobid) {
- statement.connection.openBlob(blobid, transaction, function(blob) {
- var buffer;
- function read() {
- statement.connection.getSegment(blob, function(ret) {
- var blr = new BlrReader(ret.buffer);
- var data = blr.readSegment();
- if (buffer) {
- var tmp = buffer;
- buffer = new Buffer(tmp.length + data.length);
- tmp.copy(buffer);
- data.copy(buffer, tmp.length);
- } else {
- buffer = data;
- }
- if (ret.handle == 2) { // ???
- if (statement.output[col].subType == isc_blob_text) {
- rows.data[row][col] = buffer.toString(DEFAULT_ENCODING);
- } else {
- rows.data[row][col] = buffer
+ statement.connection.openBlob(blobid, transaction,
+ function(err, blob) {
+ if (err) {callback(err); return}
+ var buffer;
+ function read() {
+ statement.connection.getSegment(blob,
+ function(err, ret) {
+ if (err) {callback(err); return}
+ var blr = new BlrReader(ret.buffer);
+ var data = blr.readSegment();
+ if (buffer) {
+ var tmp = buffer;
+ buffer = new Buffer(tmp.length + data.length);
+ tmp.copy(buffer);
+ data.copy(buffer, tmp.length);
+ } else {
+ buffer = data;
+ }
+ if (ret.handle == 2) { // ???
+ if (statement.output[col].subType == isc_blob_text) {
+ rows.data[row][col] = buffer.toString(DEFAULT_ENCODING);
+ } else {
+ rows.data[row][col] = buffer
+ }
+ callback();
+ statement.connection.closeBlob(blob);
+ } else {
+ read();
+ }
}
- callback();
- statement.connection.closeBlob(blob);
- } else {
- read();
- }
- }, error);
+ );
+ }
+ read()
}
- read()
- }, error)
+ )
} else {
callback()
}
@@ -1723,42 +1872,33 @@ function fetchBlobs(statement, transaction, rows, callback, error) {
for (var r = 0; r < rows.data.length; r++) {
for (var c = 0; c < indexes.length; c++) {
fetch(r, indexes[c],
- function() {
- count--;
- if (count == 0) {
- if (callback) {
- callback(rows)
+ function(err) {
+ if (!err) {
+ count--;
+ if (count == 0) {
+ callback(undefined, rows)
}
+ } else {
+ callback(err);
}
- },
- function(ret) {
- if (error) {
- error(ret);
- } else if (callback) {
- callback(ret);
- }
- });
+ }
+ );
}
}
} else {
- if (callback) {
- callback(rows)
- }
+ callback(undefined, rows)
}
} else {
- if (callback) {
- callback(rows)
- }
+ callback(undefined, rows)
}
}
-Connection.prototype.fetch = function(statement, transaction, count, callback, error) {
+Connection.prototype.fetch = function(statement, transaction, count, callback) {
var msg = this._msg;
var blr = this._blr;
msg.pos = 0;
blr.pos = 0;
if (count instanceof Function) {
- error = callback;
callback = count;
count = DEFAULT_FETCHSIZE;
}
@@ -1771,22 +1911,27 @@ Connection.prototype.fetch = function(statement, transaction, count, callback, e
if (transaction) {
- var cb = function(ret) {
- fetchBlobs(statement, transaction, ret, callback, error)
+ var cb = function(err, ret) {
+ if (!err) {
+ fetchBlobs(statement, transaction, ret, callback);
+ } else {
+ callback(err, ret);
+ }
};
cb.statement = statement;
- this._queueEvent(cb, error);
+ this._queueEvent(cb);
} else {
callback.statement = statement;
- this._queueEvent(callback, error);
+ this._queueEvent(callback);
}
};
-Connection.prototype.fetchAll = function(statement, transaction, callback, error) {
+Connection.prototype.fetchAll = function(statement, transaction, callback) {
var self = this;
var data;
- var loop = function(ret){
+ var loop = function(err, ret){
+ if (err) {callback(err); return};
if (!data) {
data = ret.data;
} else {
@@ -1795,37 +1940,37 @@ Connection.prototype.fetchAll = function(statement, transaction, callback, error
}
}
if (ret.fetched) {
- callback(data)
+ callback(undefined, data)
} else {
- self.fetch(statement, transaction, DEFAULT_FETCHSIZE, loop, error)
+ self.fetch(statement, transaction, DEFAULT_FETCHSIZE, loop)
}
};
- this.fetch(statement, transaction, DEFAULT_FETCHSIZE, loop, error);
+ this.fetch(statement, transaction, DEFAULT_FETCHSIZE, loop);
};
-Connection.prototype.openBlob = function(blob, transaction, callback, error) {
+Connection.prototype.openBlob = function(blob, transaction, callback) {
var msg = this._msg;
msg.pos = 0;
msg.addInt(op_open_blob);
msg.addInt(transaction.handle);
msg.addQuad(blob);
- this._queueEvent(callback, error);
+ this._queueEvent(callback);
};
-Connection.prototype.closeBlob = function(blob, callback, error) {
+Connection.prototype.closeBlob = function(blob, callback) {
var msg = this._msg;
msg.pos = 0;
msg.addInt(op_close_blob);
msg.addInt(blob.handle);
- this._queueEvent(callback, error);
+ this._queueEvent(callback);
};
-Connection.prototype.getSegment = function(blob, callback, error) {
+Connection.prototype.getSegment = function(blob, callback) {
var msg = this._msg;
msg.pos = 0;
msg.addInt(op_get_segment);
msg.addInt(blob.handle);
msg.addInt(1024); // buffer length
msg.addInt(0); // ???
- this._queueEvent(callback, error);
+ this._queueEvent(callback);
};
View
2 package.json
@@ -1,6 +1,6 @@
{
"name": "node-firebird",
- "version": "0.0.8",
+ "version": "0.1.0",
"description": "Firebird client - pure javascript",
"keywords": [
"firebird",
View
243 test/test.js
@@ -5,26 +5,31 @@ macdb = '/fbdata/test.fdb';
windb = 'C:\\dev\\bases\\test.fdb';
db = windb;
-host = '127.0.0.1';
-port = 3050;
-user = 'SYSDBA';
-password = 'masterkey';
-role = null;
-pagesize = 4096;
+config = {
+ database: db,
+ host: '127.0.0.1', // default
+ port: 3050, // default
+ user: 'SYSDBA', // default
+ password: 'masterkey', // default
+ role: null, // default
+ pageSize: 4096 // default when creating database
+}
quit = function() {
- database.detach(function(){
- console.log('database detached');
- });
+ database.detach(
+ function(){
+ console.log('database detached');
+ }
+ );
};
-function logerror(err) {
+function logError(err) {
console.log(err.message);
}
-function CheckResult(obj) {
- if (obj.status) {
- throw new Error('oups')
+function checkError(err) {
+ if (err) {
+ throw new Error(err.message)
}
}
@@ -33,83 +38,108 @@ function CheckResult(obj) {
// - params is optional, can be a single value or an array
// - callback is optional
test1 = function(){
- database.execute("select cast(? as integer) from rdb$database", 123,
- // success
- function (result) {
- console.log(result.data[0][0]);
- },
- // error
- logerror);
+ database.query("select cast(? as integer) from rdb$database", 123,
+ function (err, result) {
+ if (err) {logError(err); return}
+ console.log(result[0].cast);
+ }
+ );
};
-// simple usage of a transaction without providing error event
+// simple usage of a transaction
test2 = function() {
- database.startTransaction(function(transaction) {
- transaction.execute("select cast(? as integer) from rdb$database", 123, function(result) {
- transaction.commit(function(ret) { // commit in all situations for a single query
- CheckResult(result); // error executing query ?
- CheckResult(ret); // error commiting ?
- console.log(result.data);
- })
- });
- })
+ database.startTransaction(
+ function(err0, transaction) {
+ checkError(err0);
+ transaction.query("select cast(? as integer) from rdb$database", 123,
+ function(err1, result) {
+ transaction.commit(
+ function(err2) { // commit in all situations for a single query
+ checkError(err1); // error executing query ?
+ checkError(err2); // error commiting ?
+ console.log(result);
+ }
+ )
+ }
+ );
+ }
+ )
};
// multiple queries in a transaction
test3 = function() {
- var tr;
- function fail(err) {
- tr.rollback(function() {
- console.log(err.status);
- })
+ function check(tr, callback){
+ return function(err, param) {
+ if (!err) {
+ callback(err, param);
+ } else {
+ tr.rollback();
+ console.log(err.message);
+ }
+ }
}
- database.startTransaction(function(transaction) {
- tr = transaction;
- tr.execute("select cast(? as integer) from rdb$database", 123, function(result1) {
- tr.execute("select cast(? as integer) from rdb$database", 456, function(result2) {
- tr.commit(function() {
- console.log(result1.data[0]);
- console.log(result2.data[0]);
- }, fail)
- }, fail);
- }, fail);
- })
+ database.startTransaction(
+ function(err, transaction) {
+ checkError(err);
+ transaction.query("select cast(? as integer) from rdb$database", 123,
+ check(transaction, function(err, result1) {
+ transaction.query("select cast(? as integer) from rdb$database", 456,
+ check(transaction, function(err, result2) {
+ transaction.commit(
+ function(err) {
+ checkError(err);
+ console.log(result1[0]);
+ console.log(result2[0]);
+ }
+ )
+ })
+ );
+ })
+ );
+ }
+ )
};
// concurrency
function createPool(count, callback) {
var pool = [];
for(var i = 0; i < count; i++) {
- fb.attach(host, port, db, user, password, role, function(db) {
- pool[--count] = db;
- if (count == 0) {
- callback(pool);
- }
- }, function(err) {
- callback(err);
- callback = null;
- })
+ fb.attach(config,
+ function(err, db) {
+ if (err) {
+ logError(err);
+ return;
+ }
+ pool[--count] = db;
+ if (count == 0) {
+ callback(pool);
+ }
+ }
+ )
}
}
test4 = function(count, poolsize) {
-
- createPool(poolsize, function(pool) {
- var n = Date.now();
- var max = count;
- for (var i = 0; i < max; i++) {
- pool[i % poolsize].execute("select * from rdb$relations", function(){
- if (--count == 0) {
- console.log(max + " queries");
- console.log((Date.now() - n)/max + 'ms / query');
- for (var db in pool) {pool[db].detach()}
- }
- });
+ createPool(poolsize,
+ function(pool) {
+ var n = Date.now();
+ var max = count;
+ for (var i = 0; i < max; i++) {
+ pool[i % poolsize].execute("select * from rdb$relations",
+ function(){
+ if (--count == 0) {
+ console.log(max + " queries");
+ console.log((Date.now() - n)/max + 'ms / query');
+ for (var db in pool) {pool[db].detach()}
+ }
+ }
+ );
+ }
}
- });
+ );
};
// more complex sample
@@ -119,42 +149,59 @@ test5 = function() {
function error(err) {
if (tr) tr.rollback();
if (st) st.drop();
- console.log(err);
+ logError(err);
}
function fetch(callback) {
- st.fetch(tr, function(ret) {
- console.log(ret.data);
- callback(ret.fetched);
- }, error)
+ st.fetch(tr,
+ function(err, ret) {
+ if (err) {
+ error(err);
+ } else {
+ console.log(ret.data);
+ callback(ret.fetched);
+ }
+ }
+ )
}
- database.startTransaction(function(transaction) {
- tr = transaction;
- tr.newStatement("select * from rdb$relations", function(statement) {
- st = statement;
- st.execute(tr, function() {
-
- var cb = function(fetched) {
- if (fetched) {
- st.drop();
- tr.commit();
- } else {
- fetch(cb);
- }
- };
- fetch(cb);
- }, error)
- }, error);
- }, error)
+ database.startTransaction(
+ function(err, transaction) {
+ if (err) {error(err); return};
+ tr = transaction;
+ tr.newStatement("select * from rdb$relations",
+ function(err, statement) {
+ if (err) {error(err); return};
+ st = statement;
+ st.execute(tr,
+ function(err) {
+ if (err) {error(err); return};
+ var cb = function(fetched) {
+ if (fetched) {
+ st.drop();
+ tr.commit();
+ } else {
+ fetch(cb);
+ }
+ };
+ fetch(cb);
+ }
+ )
+ }
+ );
+ }
+ )
};
-
repl.start("");
-fb.attachOrCreate(host, port, db, user, password, pagesize, role,
- function (db) {
- database = db;
- test1();
- }, logerror
-);
+fb.attachOrCreate(config,
+ function (err, db) {
+ if (err) {
+ console.log(err.message);
+ } else {
+ database = db;
+ test1();
+ }
+ }
+);

0 comments on commit 9d903de

Please sign in to comment.
Something went wrong with that request. Please try again.