diff --git a/CUBRID version supported.txt b/CUBRID version supported.txt new file mode 100644 index 0000000..cd2f0a9 --- /dev/null +++ b/CUBRID version supported.txt @@ -0,0 +1 @@ +>=8.4.1.2030 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..9e9eef3 --- /dev/null +++ b/LICENSE @@ -0,0 +1,50 @@ +Copyright (c) 2008-2012, Search Solution Corporation. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +Redistributions in binary form must reproduce the above copyright notice, this list of conditions + and the following disclaimer in the documentation and/or other materials provided with the distribution. +Neither the name of the Search Solution Corporation nor the names of its contributors may be used to endorse + or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +Project notes +================================================================================================================================== +Part of this software uses code from the node-mysql project: +https://github.com/felixge/node-mysql +See below the license of the node-mysql project. + + +Additional licenses +================================================================================================================================== +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. + diff --git a/Readme.md b/Readme.md new file mode 100644 index 0000000..e86e8b6 --- /dev/null +++ b/Readme.md @@ -0,0 +1,82 @@ +node-cubrid +June-August, 2012 +http://www.cubrid.org + + +Introduction +======================================================================================================= +The CUBRID node.js driver is an open-source project with the goal of implementing a 100% native node.js driver for the CUBRID database engine (www.cubrid.org). + +The driver is currently under development and this (August 2012) is the first release (Milestone 1) of the driver code, which features: +- 2.000+ LOC +- Connect/Close, Query, Execute, Fetch, Close request etc. completed +- 15+ functional test cases +- 15+ unit tests +- E2E demos + +These are the main project deliverables we target for the project: +- The driver source code +- Test cases +- Code documentation +- Demos & Tutorials +- npm package; registered on http://search.npmjs.org/ + + +Installation +======================================================================================================= + +This first release does not feature yet an npm installer – it will be available in the upcoming beta release. +Therefore, if you want to use it now, please download the driver code on your machine. + + +Usage +======================================================================================================= +This first release contains many test cases and demos which will show you how to use the driver. +These examples are located in the following (sub)folders: + \demo + \src\test + + +TODOs +======================================================================================================= +In the next code release (Technology preview - September 2012), we are targeting: +- Transactions support +- Additional data types support +- Schema support +- Documentation release +- More functionality & more testing +- Additional demos and E2E scenarios +- Code improvements, optimizations and refactoring + + +Author and Contributors +======================================================================================================= +The main authors of this driver are the members of the CUBRID API team - http://www.cubrid.org/wiki_apis. + +We welcome any contributors and we hope you will enjoy coding with CUBRID! J + + +Special thanks +======================================================================================================= +We would like to thanks to the following people & projects for inspiration, +code we have reused and for doing such a great job for the open-source community! +- https://github.com/caolan/async +- https://github.com/felixge/node-mysql +- https://github.com/jeromeetienne/microcache.js + + +TODO +======================================================================================================= +This release is just the first milestone for this project. +We intend to release soon a beta version, followed by a stable release, with demos and tutorials. +Here are the scheduled releases: +- Milestone 1. Basic driver interfaces: connect, queries support +- Milestone 2. Technology preview release: ~80% functionality ready +- Milestone 3. Beta release +- Milestone 4. Stable release +- Milestone 5. Tutorials & Installer/Package completed; web awareness achieved. + +...Stay tuned! :) + + + diff --git a/demo/DemoServer.js b/demo/DemoServer.js new file mode 100644 index 0000000..d33243e --- /dev/null +++ b/demo/DemoServer.js @@ -0,0 +1,74 @@ +var http = require('http'), + Result2Array = require('../src/resultset/Result2Array'), + client = require('../index.js').createDefaultCUBRIDDemodbConnection(); + +//Note: Open http://localhost:8080 to test it + +function resultToHtmlTable(result) { + var ret = ''; + + ret += '

Table `code` content

'; + ret += ' '; + + var arr = Result2Array.GetResultsArray(result); + for (var i = 0; i < arr.length; i++) { + ret += ' '; + for (var j = 0; j < arr[i].length; j++) { + ret += ' '; + } + ret += ' '; + } + ret += '
'; + ret += ' ' + arr[i][j]; + ret += '
'; + + return ret; +} + +http.createServer(function (request, response) { + if (request.url == '/') { + client.connect(function (err) { + if (err) { + response.end(err.message); + } else { + client.query('select * from code', function (err, result, queryHandle) { + if (err) { + response.end(err.message); + } else { + var output = ''; + output += ''; + output += ' '; + output += ' CUBRID Node.js Driver test'; + output += ' '; + output += ' '; + output += ' '; + output += resultToHtmlTable(result); + output += ' '; + output += ''; + client.closeRequest(queryHandle, function (err) { + if (err) { + response.end(err.message); + } else { + client.close(function (err) { + if (err) { + response.end(err.message); + } + }); + response.writeHead(200, {'Content-Type' : 'text/html'}); + response.end(output); + } + }) + } + }) + } + }) + } else { + response.writeHead(404, {'Content-Type' : 'text/plain'}); + response.end('Error: Unexpected request!'); + } +}).listen(8080, 'localhost'); + diff --git a/demo/E2E_01.js b/demo/E2E_01.js new file mode 100644 index 0000000..6a9448f --- /dev/null +++ b/demo/E2E_01.js @@ -0,0 +1,66 @@ +var client = require('../index.js').createDefaultCUBRIDDemodbConnection(), + Result2Array = require('../src/resultset/Result2Array'); + +var sql = 'select * from game'; + +client.connect(function (err) { + if (err) { + throw err.message; + } else { + console.log('Connected successfully to ' + client.brokerServer + ':' + client.connectionBrokerPort + '.'); + client.getEngineVersion(function (err, result) { + if (err) { + throw err.message; + } else { + console.log('CUBRID Engine version is: ' + result); + console.log('Querying: [' + sql + ']'); + client.query(sql, function (err, queryResults, queryHandle) { + if (err) { + throw err.message; + } else { + console.log('Query results - Rows count: ' + Result2Array.GetResultsCount(queryResults)); + console.log('Query results - Column names: ' + Result2Array.GetResultsColumnNamesArray(queryResults)); + console.log('Query results - Column data types: ' + Result2Array.GetResultsColumnsTypeArray(queryResults)); + console.log('Query results - Data [displaying only the first 10 rows]:'); + var arr = Result2Array.GetResultsArray(queryResults); + for (var j = 0; j < 10; j++) { + console.log(arr[j].toString()); + } + console.log('Fetching more results:'); + client.fetch(queryHandle, function (err, result) { + if (err) { + throw err.message; + } else { + if (result) { + console.log('Fetch results - Data [displaying only the first 10 rows]:'); + var arr = Result2Array.GetResultsArray(result); + for (var k = 0; k < 10; k++) { + console.log(arr[k].toString()); + } + } else { + console.log('There is no more data to fetch.'); + } + client.closeRequest(queryHandle, function (err) { + if (err) { + throw err.message; + } else { + console.log('Query closed.'); + client.close(function (err) { + if (err) { + throw err.message; + } else { + console.log('Connection closed.'); + } + }) + } + }) + } + }) + } + }) + } + }) + } +}); + + diff --git a/demo/E2E_02.js b/demo/E2E_02.js new file mode 100644 index 0000000..e46d633 --- /dev/null +++ b/demo/E2E_02.js @@ -0,0 +1,59 @@ +var client = require('../index.js').createDefaultCUBRIDDemodbConnection(), + Result2Array = require('../src/resultset/Result2Array'); + +var sqlsSetup = ['drop table if exists node_test', + 'create table node_test(id int)', + 'insert into node_test values(1), (22)']; +var sqlQuery = 'select * from node_test'; +var sqlsCleanup = 'drop table node_test'; + +client.connect(function (err) { + if (err) { + throw err.message; + } else { + console.log('Connected to ' + client.brokerServer + ':' + client.connectionBrokerPort + '.'); + console.log('Creating test data...'); + client.batchExecuteNoQuery(sqlsSetup, function (err) { + if (err) { + throw err.message; + } else { + console.log('Querying: [' + sqlQuery + ']'); + client.query(sqlQuery, function (err, queryResults, queryHandle) { + if (err) { + throw err.message; + } else { + console.log('Query results - Rows count: ' + Result2Array.GetResultsCount(queryResults)); + console.log('Query results - Column names: ' + Result2Array.GetResultsColumnNamesArray(queryResults)); + console.log('Query results - Column data types: ' + Result2Array.GetResultsColumnsTypeArray(queryResults)); + console.log('Query results:'); + var arr = Result2Array.GetResultsArray(queryResults); + for (var j = 0; j < arr.length; j++) { + console.log(arr[j].toString()); + } + client.closeRequest(queryHandle, function (err) { + if (err) { + throw err.message; + } else { + client.batchExecuteNoQuery(sqlsCleanup, function (err) { + if (err) { + throw err.message; + } else { + console.log('Cleanup done.'); + client.close(function (err) { + if (err) { + throw err.message; + } else { + console.log('Connection closed.'); + } + }) + } + }) + } + }) + } + }) + } + }) + } +}); + diff --git a/index.js b/index.js new file mode 100644 index 0000000..a1e3aeb --- /dev/null +++ b/index.js @@ -0,0 +1,24 @@ +var CUBRIDConnection = require('./src/CUBRIDConnection'); + +/** + * Creates a new CUBRID connection + * @param brokerServer + * @param brokerPort + * @param user + * @param password + * @param database + * @return {*} + */ +exports.createCUBRIDConnection = function(brokerServer, brokerPort, user, password, database) { + return new CUBRIDConnection(brokerServer, brokerPort, user, password, database); +}; + +/** + * Creates a new CUBRID connection to the demodb database, with the default parameters + * @return {*} + */ +exports.createDefaultCUBRIDDemodbConnection = function() { + //return new CUBRIDConnection('localhost', 33000, 'public', '', 'demodb'); + return this.createCUBRIDConnection('localhost', 33000, 'public', '', 'demodb'); +}; + diff --git a/installer/CUBRID.ico b/installer/CUBRID.ico new file mode 100644 index 0000000..e585a78 Binary files /dev/null and b/installer/CUBRID.ico differ diff --git a/package.json b/package.json new file mode 100644 index 0000000..55eb500 --- /dev/null +++ b/package.json @@ -0,0 +1,48 @@ +{ + "author" : "CUBRID (http://www.cubrid.org/)", + + "contributors" : [ + { + "name" : "", + "url" : "" + } + ], + + "name" : "node-cubrid", + + "description" : "This is a native Node.js driver for CUBRID (www.cubrid.org). \ + It is developed entirely in JavaScript, so it doesn't require platform compilation, and it is MIT licensed.", + + "version" : "0.1", + + "repository" : { + "type" : "git", + "url" : "git://github.com/cubrid/node-cubrid.git" + }, + + "licenses" : [ + { + "type" : "MIT", + "url" : "http://github.com/cubrid/node-cubrid/raw/master/LICENSE" + } + ], + + "keywords" : [ + "native", + "CUBRID", + "module" + ], + + "engines" : { + "node" : "*" + }, + + "dependencies" : { + }, + + "devDependencies" : { + }, + + "optionalDependencies" : { + } +} diff --git a/src/CUBRIDConnection.js b/src/CUBRIDConnection.js new file mode 100644 index 0000000..79e66d3 --- /dev/null +++ b/src/CUBRIDConnection.js @@ -0,0 +1,537 @@ +var Net = require('net'), + EventEmitter = require('events').EventEmitter, + DATA_TYPES = require('./constants/DataTypes'), + PacketReader = require('./packets/PacketReader'), + PacketWriter = require('./packets/PacketWriter'), + ActionQueue = require('./utils/ActionQueue'), + Helpers = require('../src/utils/Helpers'), + Cache = require('../src/utils/Cache'), + ClientInfoExchangePacket = require('./packets/ClientInfoExchangePacket'), + OpenDatabasePacket = require('./packets/OpenDatabasePacket'), + GetEngineVersionPacket = require('./packets/GetEngineVersionPacket'), + ExecuteQueryPacket = require('./packets/ExecuteQueryPacket'), + CloseQueryPacket = require('./packets/CloseQueryPacket'), + BatchExecuteNoQueryPacket = require('./packets/BatchExecuteNoQueryPacket'), + CloseDatabasePacket = require('./packets/CloseDatabasePacket'), + FetchPacket = require('./packets/FetchPacket'); + +module.exports = CUBRIDConnection; + +//TODO Add Utils.inherit for EventEmitter + +function CUBRIDConnection(brokerServer, brokerPort, user, password, database, cacheTimeout) { + // Using EventEmitter.call on an object will do the setup of instance methods / properties + // (not inherited) of an EventEmitter. + // It is similar in purpose to super(...) in Java or base(...) in C#, but it is not implicit in Javascript. + // Because of this, we must manually call it ourselves: + //EventEmitter.call(this); + + this.queryCache = null; + if (typeof cacheTimeout !== 'undefined' && cacheTimeout > 0) { + this.queryCache = new Cache(); + } + + this.brokerServer = brokerServer || 'localhost'; + this.initialBrokerPort = brokerPort || 33000; + this.connectionBrokerPort = 0; + this.user = user || 'public'; + this.password = password || ''; + this.database = database || 'demodb'; + this.socket = new Net.Socket(); + this.CASInfo = [0, 0xFF, 0xFF, 0xFF]; + this.connectionOpened = false; + this.autoCommitMode = 0; + this.sessionId = 0; + this.queriesHandleList = new Array(); +} + +CUBRIDConnection.prototype._doGetBrokerPort = function (self, callback) { + self.socket = Net.createConnection(self.initialBrokerPort, self.brokerServer); + self.socket.setNoDelay(true); + + self.socket.on('error', function (err) { + this.connectionOpened = false; + delete this.queriesHandleList; + //throw err; + callback.call(err); + }); + self.socket.once('data', function (data) { + var packetReader = new PacketReader(); + packetReader.write(data); + clientInfoExchangePacket.parse(packetReader); + var newPort = clientInfoExchangePacket.newConnectionPort; + self.connectionBrokerPort = newPort; + self.socket.end(); + if (callback && typeof(callback) === 'function') { + if (newPort > 0) { + callback.call(null); + } else { + var err = new Error('Error receiving a new connection port!'); + callback.call(err); + } + } + }); + + var packetWriter = new PacketWriter(); + var clientInfoExchangePacket = new ClientInfoExchangePacket(); + clientInfoExchangePacket.write(packetWriter); + self.socket.write(packetWriter._buffer); +}; + +CUBRIDConnection.prototype._doDatabaseLogin = function (self, callback) { + var err = null; + var responseData = new Buffer(0); + var expectedResponseLength = -1; + + self.socket = Net.createConnection(self.connectionBrokerPort, self.brokerServer); + self.socket.setNoDelay(true); + self.socket.on('error', function (err) { + this.connectionOpened = false; + delete this.queriesHandleList; + callback.call(self, err); + }); + + self.socket.on('data', function (data) { + responseData = Helpers.combineData(responseData, data); + + if (expectedResponseLength === -1 && responseData.length >= DATA_TYPES.DATA_LENGTH_SIZEOF) { + expectedResponseLength = Helpers.getExpectedResponseLength(responseData); + } + if (responseData.length === expectedResponseLength) { +// self.socket.listeners('data').splice(0).forEach(function (listener) { +// self.socket.removeListener('data', listener); +// }); + self.socket.removeAllListeners('data'); + var packetReader = new PacketReader(); + packetReader.write(responseData); + openDatabasePacket.parse(packetReader); + self.CASInfo = openDatabasePacket.casInfo; + var errorCode = openDatabasePacket.errorCode; + var errorMsg = openDatabasePacket.errorMsg; + if (errorCode !== 0) { + err = new Error(errorCode + ':' + errorMsg); + } else { + self.sessionId = openDatabasePacket.sessionId; + self.autoCommitMode = (self.CASInfo[3] & 0x01) ? true : false; + } + if (callback && typeof(callback) === 'function') { + callback.call(self, err); + } + } + }); + + var packetWriter = new PacketWriter(); + var openDatabasePacket = new OpenDatabasePacket( + { + database : self.database, + user : self.user, + password : self.password, + casInfo : self.CASInfo + } + ); + openDatabasePacket.write(packetWriter); + self.socket.write(packetWriter._buffer); +}; + +CUBRIDConnection.prototype.connect = function (callback) { + var self = this; + + ActionQueue.enqueue( + [ + function (cb) { + self._doGetBrokerPort(self, cb); + }, + function (cb) { + self._doDatabaseLogin(self, cb); + } + ], + function (err) { + if (callback && typeof(callback) === 'function') { + if (typeof err != undefined && err != null) { + //self.emit('error', err); + } + if (typeof err === undefined || err === null) { + self.connectionOpened = true; + } + callback.call(self, err); + } + } + ); +}; + +CUBRIDConnection.prototype.getEngineVersion = function (callback) { + var err = null; + var engineVersion = ''; + var self = this; + var responseData = new Buffer(0); + var expectedResponseLength = -1; + + ActionQueue.enqueue( + [ + function (cb) { + if (self.connectionOpened === false) { + self.connect(cb); + } + else { + cb(); + } + }, + function (cb) { + self.socket.on('data', function (data) { + responseData = Helpers.combineData(responseData, data); + + if (expectedResponseLength === -1 && responseData.length >= DATA_TYPES.DATA_LENGTH_SIZEOF) { + expectedResponseLength = Helpers.getExpectedResponseLength(responseData); + } + if (responseData.length === expectedResponseLength) { +// self.socket.listeners('data').splice(0).forEach(function (listener) { +// self.socket.removeListener('data', listener); +// }); + self.socket.removeAllListeners('data'); + var packetReader = new PacketReader(); + packetReader.write(data); + getEngineVersionPacket.parse(packetReader); + var errorCode = getEngineVersionPacket.errorCode; + var errorMsg = getEngineVersionPacket.errorMsg; + if (errorCode !== 0) { + err = new Error(errorCode + ':' + errorMsg); + } else { + engineVersion = getEngineVersionPacket.engineVersion; + } + if (cb && typeof(cb) === 'function') { + if (typeof err != undefined && err != null) { + //self.emit('error', err); + } + cb.call(self, err, engineVersion); + } + } + }); + + var packetWriter = new PacketWriter(); + var getEngineVersionPacket = new GetEngineVersionPacket({casInfo : self.CASInfo}); + getEngineVersionPacket.write(packetWriter); + self.socket.write(packetWriter._buffer); + } + ], + function (err, engineVersion) { + if (callback && typeof(callback) === 'function') { + if (typeof err != undefined && err != null) { + //self.emit('error', err); + } + callback.call(self, err, engineVersion); + } + } + ); +}; + +CUBRIDConnection.prototype.batchExecuteNoQuery = function (sqls, callback) { + var sqlsArr; + + if (Array.isArray(sqls)) { + if (sqls.length == 0) { + callback.call(this, null); + return; + } + sqlsArr = sqls; + } else { + sqlsArr = new Array(sqls); + } + + var err = null; + var self = this; + var responseData = new Buffer(0); + var expectedResponseLength = -1; + + ActionQueue.enqueue( + [ + function (cb) { + if (self.connectionOpened === false) { + self.connect(cb); + } + else { + cb(); + } + }, + function (cb) { + self.socket.on('data', function (data) { + responseData = Helpers.combineData(responseData, data); + + if (expectedResponseLength === -1 && responseData.length >= DATA_TYPES.DATA_LENGTH_SIZEOF) { + expectedResponseLength = Helpers.getExpectedResponseLength(responseData); + } + if (responseData.length === expectedResponseLength) { +// self.socket.listeners('data').splice(0).forEach(function (listener) { +// self.socket.removeListener('data', listener); +// }); + self.socket.removeAllListeners('data'); + var packetReader = new PacketReader(); + packetReader.write(data); + batchExecuteNoQueryPacket.parse(packetReader); + var errorCode = batchExecuteNoQueryPacket.errorCode; + var errorMsg = batchExecuteNoQueryPacket.errorMsg; + if (errorCode !== 0) { + err = new Error(errorCode + ':' + errorMsg); + } + if (cb && typeof(cb) === 'function') { + if (typeof err != undefined && err != null) { + //self.emit('error', err); + } + cb.call(self, err); + } + } + }); + + var packetWriter = new PacketWriter(); + var batchExecuteNoQueryPacket = new BatchExecuteNoQueryPacket( + { + SQLs : sqlsArr, + casInfo : self.CASInfo, + autoCommitMode : self.autoCommitMode + } + ); + batchExecuteNoQueryPacket.write(packetWriter); + self.socket.write(packetWriter._buffer); + } + ], + function (err) { + if (callback && typeof(callback) === 'function') { + if (typeof err != undefined && err != null) { + //self.emit('error', err); + } + callback.call(self, err); + } + } + ); +}; + +CUBRIDConnection.prototype.query = function (sql, callback) { + var err = null; + var self = this; + var responseData = new Buffer(0); + var expectedResponseLength = -1; + + ActionQueue.enqueue( + [ + function (cb) { + if (self.connectionOpened === false) { + self.connect(cb); + } else { + cb(); + } + }, + function (cb) { + if (self.queryCache != null) { + if (self.queryCache.contains(sql)) { + callback(err, self.queryCache.get(sql), null); //null: to prevent fetch (cache is intended for small data) + return; + } + } + + self.socket.on('data', function (data) { + responseData = Helpers.combineData(responseData, data); + + if (expectedResponseLength === -1 && responseData.length >= DATA_TYPES.DATA_LENGTH_SIZEOF) { + expectedResponseLength = Helpers.getExpectedResponseLength(responseData); + } + if (responseData.length === expectedResponseLength) { +// self.socket.listeners('data').splice(0).forEach(function (listener) { +// self.socket.removeListener('data', listener); +// }); + self.socket.removeAllListeners('data'); + var packetReader = new PacketReader(); + packetReader.write(responseData); + var result = executeQueryPacket.parse(packetReader); + var errorCode = executeQueryPacket.errorCode; + var errorMsg = executeQueryPacket.errorMsg; + if (errorCode !== 0) { + err = new Error(errorCode + ':' + errorMsg); + } else { + self.queriesHandleList.push(executeQueryPacket); + } + if (cb && typeof(cb) === 'function') { + if (typeof err != undefined && err != null) { + //self.emit('error', err); + } else { + if (self.queryCache !== null) { + self.queryCache.getSet(sql, result); + } + } + cb.call(self, err, result, executeQueryPacket.handle); + } + } + }); + + var packetWriter = new PacketWriter(); + var executeQueryPacket = new ExecuteQueryPacket( + { + sql : sql, + casInfo : self.CASInfo, + autoCommitMode : self.autoCommitMode + } + ); + executeQueryPacket.write(packetWriter); + self.socket.write(packetWriter._buffer); + } + ], + function (err, result, handle) { + if (callback && typeof(callback) === 'function') { + if (typeof err != undefined && err != null) { + //self.emit('error', err); + } + callback.call(self, err, result, handle); + } + } + ); +}; + +CUBRIDConnection.prototype.fetch = function (queryHandle, callback) { + var err = null; + var self = this; + var responseData = new Buffer(0); + var expectedResponseLength = -1; + + self.socket.on('data', function (data) { + responseData = Helpers.combineData(responseData, data); + + if (expectedResponseLength === -1 && responseData.length >= DATA_TYPES.DATA_LENGTH_SIZEOF) { + expectedResponseLength = Helpers.getExpectedResponseLength(responseData); + } + if (responseData.length === expectedResponseLength) { + self.socket.removeAllListeners('data'); + var packetReader = new PacketReader(); + packetReader.write(responseData); + var result = fetchPacket.parse(packetReader, self.queriesHandleList[i]); + var errorCode = fetchPacket.errorCode; + var errorMsg = fetchPacket.errorMsg; + if (errorCode !== 0) { + err = new Error(errorCode + ':' + errorMsg); + } + if (callback && typeof(callback) === 'function') { + if (typeof err != undefined && err != null) { + //self.emit('error', err); + } + callback.call(self, err, result); + } + } + }); + + var foundQueryHandle = false; + for (var i = 0; i < this.queriesHandleList.length; i++) { + if (this.queriesHandleList[i].handle === queryHandle) { + foundQueryHandle = true; + break; + } + } + if (!foundQueryHandle) { + err = new Error('No active query with this handle!'); + self.socket.removeAllListeners('data'); + callback.call(self, err, null); + } else { + if (this.queriesHandleList[i].currentTupleCount === this.queriesHandleList[i].totalTupleCount) { + self.socket.removeAllListeners('data'); + callback.call(self, err, null); + } else { + var packetWriter = new PacketWriter(); + var fetchPacket = new FetchPacket({casInfo : self.CASInfo}); + fetchPacket.write(packetWriter, this.queriesHandleList[i]); //TODO Verify this + self.socket.write(packetWriter._buffer); + } + } +}; + +CUBRIDConnection.prototype.closeRequest = function (requestHandle, callback) { + var err = null; + var self = this; + var responseData = new Buffer(0); + var expectedResponseLength = -1; + + self.socket.on('data', function (data) { + responseData = Helpers.combineData(responseData, data); + + if (expectedResponseLength === -1 && responseData.length >= DATA_TYPES.DATA_LENGTH_SIZEOF) { + expectedResponseLength = Helpers.getExpectedResponseLength(responseData); + } + if (responseData.length === expectedResponseLength) { +// self.socket.listeners('data').splice(0).forEach(function (listener) { +// self.socket.removeListener('data', listener); +// }); + self.socket.removeAllListeners('data'); + var packetReader = new PacketReader(); + packetReader.write(data); + closeQueryPacket.parse(packetReader); + var errorCode = closeQueryPacket.errorCode; + var errorMsg = closeQueryPacket.errorMsg; + if (errorCode !== 0) { + err = new Error(errorCode + ':' + errorMsg); + } + if (callback && typeof(callback) === 'function') { + if (typeof err != undefined && err != null) { + //self.emit('error', err); + } + callback.call(self, err); + } + } + }); + + var packetWriter = new PacketWriter(); + var closeQueryPacket = new CloseQueryPacket( + { + casInfo : self.CASInfo, + reqHandle : requestHandle + } + ); + for (var i = 0; i < this.queriesHandleList.length; i++) { + if (this.queriesHandleList[i].handle === requestHandle) { + this.queriesHandleList.splice(i, 1); + } + } + closeQueryPacket.write(packetWriter); + self.socket.write(packetWriter._buffer); +}; + +CUBRIDConnection.prototype.close = function (callback) { + var err = null; + var self = this; + var responseData = new Buffer(0); + var expectedResponseLength = -1; + + self.socket.on('data', function (data) { + responseData = Helpers.combineData(responseData, data); + + if (expectedResponseLength === -1 && responseData.length >= DATA_TYPES.DATA_LENGTH_SIZEOF) { + expectedResponseLength = Helpers.getExpectedResponseLength(responseData); + } + if (responseData.length === expectedResponseLength) { +// self.socket.listeners('data').splice(0).forEach(function (listener) { +// self.socket.removeListener('data', listener); +// }); + self.socket.removeAllListeners('data'); + var packetReader = new PacketReader(); + packetReader.write(data); + closeDatabasePacket.parse(packetReader); + // Close socket connection + self.socket.destroy(); + var errorCode = closeDatabasePacket.errorCode; + var errorMsg = closeDatabasePacket.errorMsg; + if (errorCode !== 0) { + err = new Error(errorCode + ':' + errorMsg); + } + + if (callback && typeof(callback) === 'function') { + if (typeof err != undefined && err != null) { + //self.emit('error', err); + } + callback.call(self, err); + } + } + }); + + var packetWriter = new PacketWriter(); + var closeDatabasePacket = new CloseDatabasePacket({casInfo : self.CASInfo}); + closeDatabasePacket.write(packetWriter); + self.socket.write(packetWriter._buffer); +}; + + + + diff --git a/src/Config.js b/src/Config.js new file mode 100644 index 0000000..894f7e8 --- /dev/null +++ b/src/Config.js @@ -0,0 +1,11 @@ +/** + * Debug enabled in application? + * @type {Boolean} + * @private + */ +var _DEBUG_ENABLED = true; +//var _DEBUG_ENABLED = false; + +exports.DEBUG_ENABLED = _DEBUG_ENABLED; + + diff --git a/src/constants/CASConstants.js b/src/constants/CASConstants.js new file mode 100644 index 0000000..ca853c0 --- /dev/null +++ b/src/constants/CASConstants.js @@ -0,0 +1,330 @@ +/** + * Define CUBRID Function codes constants + */ +exports.CASFunctionCode = { + CAS_FC_END_TRAN : 1, + CAS_FC_PREPARE : 2, + CAS_FC_EXECUTE : 3, + CAS_FC_GET_DB_PARAMETER : 4, + CAS_FC_SET_DB_PARAMETER : 5, + CAS_FC_CLOSE_REQ_HANDLE : 6, + CAS_FC_CURSOR : 7, + CAS_FC_FETCH : 8, + CAS_FC_SCHEMA_INFO : 9, + CAS_FC_OID_GET : 10, + CAS_FC_OID_PUT : 11, + CAS_FC_DEPRECATED1 : 12, + CAS_FC_DEPRECATED2 : 13, + CAS_FC_DEPRECATED3 : 14, + CAS_FC_GET_DB_VERSION : 15, + CAS_FC_GET_CLASS_NUM_OBJS : 16, + CAS_FC_OID_CMD : 17, + CAS_FC_COLLECTION : 18, + CAS_FC_NEXT_RESULT : 19, + CAS_FC_EXECUTE_BATCH : 20, + CAS_FC_EXECUTE_ARRAY : 21, + CAS_FC_CURSOR_UPDATE : 22, + CAS_FC_GET_ATTR_TYPE_STR : 23, + CAS_FC_GET_QUERY_INFO : 24, + CAS_FC_DEPRECATED4 : 25, + CAS_FC_SAVEPOINT : 26, + CAS_FC_PARAMETER_INFO : 27, + CAS_FC_XA_PREPARE : 28, + CAS_FC_XA_RECOVER : 29, + CAS_FC_XA_END_TRAN : 30, + CAS_FC_CON_CLOSE : 31, + CAS_FC_CHECK_CAS : 32, + CAS_FC_MAKE_OUT_RS : 33, + CAS_FC_GET_GENERATED_KEYS : 34, + CAS_FC_LOB_NEW : 35, + CAS_FC_LOB_WRITE : 36, + CAS_FC_LOB_READ : 37, + CAS_FC_END_SESSION : 38, + CAS_FC_GET_ROW_COUNT : 39, + CAS_FC_GET_LAST_INSERT_ID : 40, + CAS_FC_PREPARE_AND_EXECUTE : 41 +}; + +/** + * Define CUBRID Statement types constants + */ +exports.CUBRIDStatementType = { + CUBRID_STMT_ALTER_CLASS : 0, + CUBRID_STMT_ALTER_SERIAL : 1, + CUBRID_STMT_COMMIT_WORK : 2, + CUBRID_STMT_REGISTER_DATABASE : 3, + CUBRID_STMT_CREATE_CLASS : 4, + CUBRID_STMT_CREATE_INDEX : 5, + CUBRID_STMT_CREATE_TRIGGER : 6, + CUBRID_STMT_CREATE_SERIAL : 7, + CUBRID_STMT_DROP_DATABASE : 8, + CUBRID_STMT_DROP_CLASS : 9, + CUBRID_STMT_DROP_INDEX : 10, + CUBRID_STMT_DROP_LABEL : 11, + CUBRID_STMT_DROP_TRIGGER : 12, + CUBRID_STMT_DROP_SERIAL : 13, + CUBRID_STMT_EVALUATE : 14, + CUBRID_STMT_RENAME_CLASS : 15, + CUBRID_STMT_ROLLBACK_WORK : 16, + CUBRID_STMT_GRANT : 17, + CUBRID_STMT_REVOKE : 18, + CUBRID_STMT_STATISTICS : 19, + CUBRID_STMT_INSERT : 20, + CUBRID_STMT_SELECT : 21, + CUBRID_STMT_UPDATE : 22, + CUBRID_STMT_DELETE : 23, + CUBRID_STMT_CALL : 24, + CUBRID_STMT_GET_ISO_LVL : 25, + CUBRID_STMT_GET_TIMEOUT : 26, + CUBRID_STMT_GET_OPT_LVL : 27, + CUBRID_STMT_SET_OPT_LVL : 28, + CUBRID_STMT_SCOPE : 29, + CUBRID_STMT_GET_TRIGGER : 30, + CUBRID_STMT_SET_TRIGGER : 31, + CUBRID_STMT_SAVEPOINT : 32, + CUBRID_STMT_PREPARE : 33, + CUBRID_STMT_ATTACH : 34, + CUBRID_STMT_USE : 35, + CUBRID_STMT_REMOVE_TRIGGER : 36, + CUBRID_STMT_RENAME_TRIGGER : 37, + CUBRID_STMT_ON_LDB : 38, + CUBRID_STMT_GET_LDB : 39, + CUBRID_STMT_SET_LDB : 40, + CUBRID_STMT_GET_STATS : 41, + CUBRID_STMT_CREATE_USER : 42, + CUBRID_STMT_DROP_USER : 43, + CUBRID_STMT_ALTER_USER : 44, + CUBRID_STMT_CALL_SP : 0x7e, + CUBRID_STMT_UNKNOWN : 0x7f +}; + +/** + * Define CUBRID Isolation levels constants + */ +exports.CUBRIDIsolationLevel = { + TRAN_UNKNOWN_ISOLATION : 0x00, + TRAN_COMMIT_CLASS_UNCOMMIT_INSTANCE : 0x01, + TRAN_COMMIT_CLASS_COMMIT_INSTANCE : 0x02, + TRAN_REP_CLASS_UNCOMMIT_INSTANCE : 0x03, + TRAN_REP_CLASS_COMMIT_INSTANCE : 0x04, + TRAN_REP_CLASS_REP_INSTANCE : 0x05, + TRAN_SERIALIZABLE : 0x06, + TRAN_DEFAULT_ISOLATION : 0x01 +}; + +/** + * Define CUBRID Data types constants + */ +exports.CUBRIDDataType = { + CCI_U_TYPE_UNKNOWN : 0, + CCI_U_TYPE_NULL : 0, + CCI_U_TYPE_CHAR : 1, + CCI_U_TYPE_STRING : 2, + CCI_U_TYPE_NCHAR : 3, + CCI_U_TYPE_VARNCHAR : 4, + CCI_U_TYPE_BIT : 5, + CCI_U_TYPE_VARBIT : 6, + CCI_U_TYPE_NUMERIC : 7, + CCI_U_TYPE_INT : 8, + CCI_U_TYPE_SHORT : 9, + CCI_U_TYPE_MONETARY : 10, + CCI_U_TYPE_FLOAT : 11, + CCI_U_TYPE_DOUBLE : 12, + CCI_U_TYPE_DATE : 13, + CCI_U_TYPE_TIME : 14, + CCI_U_TYPE_TIMESTAMP : 15, + CCI_U_TYPE_SET : 16, + CCI_U_TYPE_MULTISET : 17, + CCI_U_TYPE_SEQUENCE : 18, + CCI_U_TYPE_OBJECT : 19, + CCI_U_TYPE_RESULTSET : 20, + CCI_U_TYPE_BIGINT : 21, + CCI_U_TYPE_DATETIME : 22, + CCI_U_TYPE_BLOB : 23, + CCI_U_TYPE_CLOB : 24 +}; + +exports.getCUBRIDDataType = function (type) { + switch (type) { + case this.CUBRIDDataType.CCI_U_TYPE_CHAR: + return 'Char'; + case this.CUBRIDDataType.CCI_U_TYPE_NCHAR: + return 'NChar'; + case this.CUBRIDDataType.CCI_U_TYPE_STRING: + return 'String'; + case this.CUBRIDDataType.CCI_U_TYPE_VARNCHAR: + return 'VarNchar'; + case this.CUBRIDDataType.CCI_U_TYPE_SHORT: + return 'Short'; + case this.CUBRIDDataType.CCI_U_TYPE_INT: + return 'Int'; + case this.CUBRIDDataType.CCI_U_TYPE_BIGINT: + return 'Bigint'; + case this.CUBRIDDataType.CCI_U_TYPE_FLOAT: + return 'Float'; + case this.CUBRIDDataType.CCI_U_TYPE_DOUBLE: + return 'Double'; + case this.CUBRIDDataType.CCI_U_TYPE_MONETARY: + return 'Monetary'; + case this.CUBRIDDataType.CCI_U_TYPE_NUMERIC: + return 'Numeric'; + case this.CUBRIDDataType.CCI_U_TYPE_DATE: + return 'Date'; + case this.CUBRIDDataType.CCI_U_TYPE_TIME: + return 'Time'; + case this.CUBRIDDataType.CCI_U_TYPE_DATETIME: + return 'DateTime'; + case this.CUBRIDDataType.CCI_U_TYPE_TIMESTAMP: + return 'Timestamp'; + case this.CUBRIDDataType.CCI_U_TYPE_OBJECT: + return 'Object'; + case this.CUBRIDDataType.CCI_U_TYPE_BIT: + return 'Bit'; + case this.CUBRIDDataType.CCI_U_TYPE_VARBIT: + return 'Varbit'; + case this.CUBRIDDataType.CCI_U_TYPE_SET: + return 'Set'; + case this.CUBRIDDataType.CCI_U_TYPE_MULTISET: + return 'Multiset'; + case this.CUBRIDDataType.CCI_U_TYPE_SEQUENCE: + return 'Sequence'; + case this.CUBRIDDataType.CCI_U_TYPE_BLOB: + return 'Blob'; + case this.CUBRIDDataType.CCI_U_TYPE_CLOB: + return 'Clob'; + case this.CUBRIDDataType.CCI_U_TYPE_RESULTSET: + return 'Resultset'; + default: + return 'UNKNOWN'; + } +}; + +/** + * Define CUBRID Schema type constants + */ +exports.CUBRIDSchemaType = { + CCI_SCH_CLASS : 1, + CCI_SCH_VCLASS : 2, + CCI_SCH_QUERY_SPEC : 3, + CCI_SCH_ATTRIBUTE : 4, + CCI_SCH_CLASS_ATTRIBUTE : 5, + CCI_SCH_METHOD : 6, + CCI_SCH_CLASS_METHOD : 7, + CCI_SCH_METHOD_FILE : 8, + CCI_SCH_SUPERCLASS : 9, + CCI_SCH_SUBCLASS : 10, + CCI_SCH_CONSTRAIT : 11, + CCI_SCH_TRIGGER : 12, + CCI_SCH_CLASS_PRIVILEGE : 13, + CCI_SCH_ATTR_PRIVILEGE : 14, + CCI_SCH_DIRECT_SUPER_CLASS : 15, + CCI_SCH_PRIMARY_KEY : 16, + CCI_SCH_IMPORTED_KEYS : 17, + CCI_SCH_EXPORTED_KEYS : 18, + CCI_SCH_CROSS_REFERENCE : 19 +}; + +/** + * Define CUBRID Prepare statement constants + */ +exports.CCIPrepareOption = { + CCI_PREPARE_NORMAL : 0x00, + CCI_PREPARE_INCLUDE_OID : 0x01, + CCI_PREPARE_UPDATABLE : 0x02, + CCI_PREPARE_QUERY_INFO : 0x04, + CCI_PREPARE_HOLDABLE : 0x08, + CCI_PREPARE_CALL : 0x40 +}; + +/** + * Define CUBRID OID commands constants + */ +exports.OidCommand = { + DROP_BY_OID : 1, + IS_INSTANCE : 2, + GET_READ_LOCK_BY_OID : 3, + GET_WRITE_LOCK_BY_OID : 4, + GET_CLASS_NAME_BY_OID : 5 +}; + +/** + * Define CUBRID Transaction type constants + */ +exports.CCITransactionType = { + CCI_TRAN_COMMIT : 1, + CCI_TRAN_ROLLBACK : 2 +}; + +/** + * Define CUBRID Database parameters constants + */ +exports.CCIDbParam = { + CCI_PARAM_ISOLATION_LEVEL : 1, + CCI_PARAM_LOCK_TIMEOUT : 2, + CCI_PARAM_MAX_STRING_LENGTH : 3, + CCI_PARAM_AUTO_COMMIT : 4 +}; + +/** + * Define CUBRID Collection-related commands constants + */ +exports.CUBRIDCollectionCommand = { + GET_COLLECTION_VALUE : 1, + GET_SIZE_OF_COLLECTION : 2, + DROP_ELEMENT_IN_SET : 3, + ADD_ELEMENT_TO_SET : 4, + DROP_ELEMENT_IN_SEQUENCE : 5, + INSERT_ELEMENT_INTO_SEQUENCE : 6, + PUT_ELEMENT_ON_SEQUENCE : 7 +}; + +/** + * Define CUBRID Connection status constants + */ +exports.ConnectionStatus = { + CON_STATUS_OUT_TRAN : 0, + CON_STATUS_IN_TRAN : 1, + CON_STATUS_CLOSE : 2, + CON_STATUS_CLOSE_AND_CONNECT : 3 +}; + +/** + * Define CUBRID Cursor position constants + */ +exports.CCICursorPosition = { + CCI_CURSOR_FIRST : 0, + CCI_CURSOR_CURRENT : 1, + CCI_CURSOR_LAST : 2 +}; + +/** + * Define CUBRID Statement execution type constants + */ +exports.StmtType = { + NORMAL : 0, + GET_BY_OID : 1, + GET_SCHEMA_INFO : 2, + GET_AUTOINCREMENT_KEYS : 3 +}; + +/** + * Define CUBRID Query execution constants + */ +exports.QueryExecutionMode = { + SYNC_EXEC : 0, + ASYNC_EXEC : 1 +}; + +/** + * Define CUBRID Statement execution constants + */ +exports.CCIExecutionOption = { + CCI_EXEC_NORMAL : 0x00, + CCI_EXEC_ASYNC : 0x01, + CCI_EXEC_QUERY_ALL : 0x02, + CCI_EXEC_QUERY_INFO : 0x04, + CCI_EXEC_ONLY_QUERY_PLAN : 0x08, + CCI_EXEC_THREAD : 0x10, + CCI_EXEC_HOLDABLE : 0x20 +}; + diff --git a/src/constants/DataTypes.js b/src/constants/DataTypes.js new file mode 100644 index 0000000..d59d69c --- /dev/null +++ b/src/constants/DataTypes.js @@ -0,0 +1,25 @@ +/** + * Define standard CUBRID data types size (in bytes) + */ + +exports.UNSPECIFIED_SIZEOF = 0; +exports.BYTE_SIZEOF = 1; +exports.BOOL_SIZEOF = 1; +exports.INT_SIZEOF = 4; +exports.LONG_SIZEOF = 8; +exports.OBJECT_SIZEOF = 8; +exports.RESULTSET_SIZEOF = 4; +exports.SHORT_SIZEOF = 2; +exports.FLOAT_SIZEOF = 4; +exports.DOUBLE_SIZEOF = 8; +exports.DATE_SIZEOF = 14; +exports.TIME_SIZEOF = 14; +exports.DATETIME_SIZEOF = 14; +exports.TIMESTAMP_SIZEOF = 14; +exports.OID_SIZEOF = 8; + +exports.DATA_LENGTH_SIZEOF = 4; +exports.CAS_INFO_SIZE = 4; + + + diff --git a/src/constants/DriverVersion.js b/src/constants/DriverVersion.js new file mode 100644 index 0000000..24571f6 --- /dev/null +++ b/src/constants/DriverVersion.js @@ -0,0 +1,16 @@ +/** + * Define the supported protocol version + * The protocol version is validated by the CUBRID broker, + * to assess the compatibility level with the CUBRID engine. + */ +var CAS_PROTO_INDICATOR = 0x40; +var CAS_PROTOCOL_VERSION = 0x01; + +exports.CAS_VER = CAS_PROTO_INDICATOR | CAS_PROTOCOL_VERSION; + +/** + * Current driver version + * @type {String} + */ +exports.DRIVER_VER = '0.1'; + diff --git a/src/constants/ErrorMessages.js b/src/constants/ErrorMessages.js new file mode 100644 index 0000000..3daf94e --- /dev/null +++ b/src/constants/ErrorMessages.js @@ -0,0 +1,35 @@ +/** + * Define CUBRID error messages + */ +exports.CASErrorMsgId = [ + ['CAS_ER_DBMS' , -1000], + ['CAS_ER_INTERNAL' , -1001], + ['CAS_ER_NO_MORE_MEMORY' , -1002], + ['CAS_ER_COMMUNICATION' , -1003], + ['CAS_ER_ARGS' , -1004], + ['CAS_ER_TRAN_TYPE' , -1005], + ['CAS_ER_SRV_HANDLE' , -1006], + ['CAS_ER_NUM_BIND' , -1007], + ['CAS_ER_UNKNOWN_U_TYPE' , -1008], + ['CAS_ER_DB_VALUE' , -1009], + ['CAS_ER_TYPE_CONVERSION' , -1010], + ['CAS_ER_PARAM_NAME' , -1011], + ['CAS_ER_NO_MORE_DATA' , -1012], + ['CAS_ER_OBJECT' , -1013], + ['CAS_ER_OPEN_FILE' , -1014], + ['CAS_ER_SCHEMA_TYPE' , -1015], + ['CAS_ER_VERSION' , -1016], + ['CAS_ER_FREE_SERVER' , -1017], + ['CAS_ER_NOT_AUTHORIZED_CLIENT' , -1018], + ['CAS_ER_QUERY_CANCEL' , -1019], + ['CAS_ER_NOT_COLLECTION' , -1020], + ['CAS_ER_COLLECTION_DOMAIN' , -1021], + ['CAS_ER_NO_MORE_RESULT_SET' , -1022], + ['CAS_ER_INVALID_CALL_STMT' , -1023], + ['CAS_ER_STMT_POOLING' , -1024], + ['CAS_ER_DBSERVER_DISCONNECTED' , -1025], + ['CAS_ER_MAX_PREPARED_STMT_COUNT_EXCEEDED' , -1026], + ['CAS_ER_HOLDABLE_NOT_ALLOWED' , -1027], + ['CAS_ER_NOT_IMPLEMENTED' , -1100], + ['CAS_ER_IS' , -1200] +]; diff --git a/src/packets/BatchExecuteNoQueryPacket.js b/src/packets/BatchExecuteNoQueryPacket.js new file mode 100644 index 0000000..9c42dad --- /dev/null +++ b/src/packets/BatchExecuteNoQueryPacket.js @@ -0,0 +1,61 @@ +var DATA_TYPES = require('../constants/DataTypes'), + ErrorMessages = require('../constants/ErrorMessages'), + CAS = require('../constants/CASConstants'); + +module.exports = BatchExecuteNoQueryPacket; + +function BatchExecuteNoQueryPacket(options) { + options = options || {}; + + this.SQLs = options.SQLs; + this.casInfo = options.casInfo; + this.autoCommit = options.autoCommitMode; + + this.responseCode = 0; + this.errorCode = 0; + this.errorMsg = ''; +} + +BatchExecuteNoQueryPacket.prototype.write = function (writer) { + var statementsLength = 0; + for (var i = 0; i < this.SQLs.length; i++) { + statementsLength += DATA_TYPES.INT_SIZEOF; + statementsLength += (this.SQLs[i].length + 1); + } + var bufferLength = DATA_TYPES.DATA_LENGTH_SIZEOF + DATA_TYPES.CAS_INFO_SIZE + + DATA_TYPES.BYTE_SIZEOF + DATA_TYPES.INT_SIZEOF + DATA_TYPES.BYTE_SIZEOF + statementsLength; + + writer._writeInt(bufferLength - DATA_TYPES.DATA_LENGTH_SIZEOF - DATA_TYPES.CAS_INFO_SIZE); + writer._writeBytes(4, this.casInfo); + writer._writeByte(CAS.CASFunctionCode.CAS_FC_EXECUTE_BATCH); + writer._writeInt(DATA_TYPES.BYTE_SIZEOF); + writer._writeByte(this.autoCommit ? 1 : 0); //Autocommit + for (var j = 0; j < this.SQLs.length; j++) { + writer._writeNullTerminatedString(this.SQLs[j]); + } + + return writer; +}; + +BatchExecuteNoQueryPacket.prototype.parse = function (parser) { + var reponseLength = parser._parseInt(); + this.casInfo = parser._parseBuffer(4); + + var responseBuffer = parser._parseBuffer(reponseLength); + this.responseCode = parser._parseInt(); + if (this.responseCode < 0) { + this.errorCode = parser._parseInt(); + this.errorMsg = parser._parseNullTerminatedString(responseBuffer.length - 2 * DATA_TYPES.INT_SIZEOF); + if (this.errorMsg.length == 0) { + for (var iter = 0; iter < ErrorMessages.CASErrorMsgId.length; iter++) { + if (this.errorCode == ErrorMessages.CASErrorMsgId[iter][1]) { + this.errorMsg = ErrorMessages.CASErrorMsgId[iter][0]; + break; + } + } + } + } + + return this; +}; + diff --git a/src/packets/ClientInfoExchangePacket.js b/src/packets/ClientInfoExchangePacket.js new file mode 100644 index 0000000..f008145 --- /dev/null +++ b/src/packets/ClientInfoExchangePacket.js @@ -0,0 +1,37 @@ +var DRIVER_VERSION = require('../constants/DriverVersion'); + +module.exports = ClientInfoExchangePacket; + +/** + * Constructor + * @constructor + */ +function ClientInfoExchangePacket() { + this.newConnectionPort = 0; +} + +/** + * Write data + * @param writer + */ +ClientInfoExchangePacket.prototype.write = function (writer) { + writer._writeFixedLengthString('CUBRK', 0, 5); + writer._writeByte(3); // 3 = JDBC client type + writer._writeByte(DRIVER_VERSION.CAS_VER); + writer._writeByte(0); + writer._writeByte(0); + writer._writeByte(0); + + return writer; +}; + +/** + * Read data + * @param parser + */ +ClientInfoExchangePacket.prototype.parse = function (parser) { + this.newConnectionPort = parser._parseInt(); + + return this; +}; + diff --git a/src/packets/CloseDatabasePacket.js b/src/packets/CloseDatabasePacket.js new file mode 100644 index 0000000..484e71f --- /dev/null +++ b/src/packets/CloseDatabasePacket.js @@ -0,0 +1,62 @@ +var DATA_TYPES = require('../constants/DataTypes'), + ErrorMessages = require('../constants/ErrorMessages'), + CAS = require('../constants/CASConstants'); + +module.exports = CloseDatabasePacket; + +/** + * Constructor + * @param options + * @constructor + */ +function CloseDatabasePacket(options) { + options = options || {}; + + this.casInfo = options.casInfo; + + this.responseCode = 0; + this.errorCode = 0; + this.errorMsg = ''; +} + +/** + * Write data + * @param writer + */ +CloseDatabasePacket.prototype.write = function (writer) { + var bufferLength = DATA_TYPES.DATA_LENGTH_SIZEOF + DATA_TYPES.CAS_INFO_SIZE + + DATA_TYPES.BYTE_SIZEOF; + + writer._writeInt(bufferLength - DATA_TYPES.DATA_LENGTH_SIZEOF - DATA_TYPES.CAS_INFO_SIZE); + writer._writeBytes(4, this.casInfo); + writer._writeByte(CAS.CASFunctionCode.CAS_FC_CON_CLOSE); + + return writer; +}; + +/** + * Read data + * @param parser + */ +CloseDatabasePacket.prototype.parse = function (parser) { + var reponseLength = parser._parseInt(); + this.casInfo = parser._parseBytes(4); + + var responseBuffer = parser._parseBuffer(reponseLength); + this.responseCode = parser._parseInt(); + if (this.responseCode < 0) { + this.errorCode = parser._parseInt(); + this.errorMsg = parser._parseNullTerminatedString(responseBuffer.length - 2 * DATA_TYPES.INT_SIZEOF); + if (this.errorMsg.length == 0) { + for (var iter = 0; iter < ErrorMessages.CASErrorMsgId.length; iter++) { + if (this.errorCode == ErrorMessages.CASErrorMsgId[iter][1]) { + this.errorMsg = ErrorMessages.CASErrorMsgId[iter][0]; + break; + } + } + } + } + + return this; +}; + diff --git a/src/packets/CloseQueryPacket.js b/src/packets/CloseQueryPacket.js new file mode 100644 index 0000000..ef32997 --- /dev/null +++ b/src/packets/CloseQueryPacket.js @@ -0,0 +1,68 @@ +var DATA_TYPES = require('../constants/DataTypes'), + ErrorMessages = require('../constants/ErrorMessages'), + CAS = require('../constants/CASConstants'); + +module.exports = CloseQueryPacket; + +/** + * Constructor + * @param options + * @constructor + */ +function CloseQueryPacket(options) { + options = options || {}; + + this.casInfo = options.casInfo; + this.reqHandle = options.reqHandle; + + this.responseCode = 0; + this.errorCode = 0; + this.errorMsg = ''; +} + +/** + * Write data + * @param writer + */ +CloseQueryPacket.prototype.write = function (writer) { + var bufferLength = DATA_TYPES.DATA_LENGTH_SIZEOF + DATA_TYPES.CAS_INFO_SIZE + + DATA_TYPES.BYTE_SIZEOF + DATA_TYPES.INT_SIZEOF + DATA_TYPES.INT_SIZEOF + + DATA_TYPES.INT_SIZEOF + DATA_TYPES.BYTE_SIZEOF; + + writer._writeInt(bufferLength - DATA_TYPES.DATA_LENGTH_SIZEOF - DATA_TYPES.CAS_INFO_SIZE); + writer._writeBytes(4, this.casInfo); + writer._writeByte(CAS.CASFunctionCode.CAS_FC_CLOSE_REQ_HANDLE); + writer._writeInt(DATA_TYPES.INT_SIZEOF); + writer._writeInt(this.reqHandle); + writer._writeInt(1); //TODO Document this value + writer._writeByte(0); //autocommit mode + + return writer; +}; + +/** + * Read data + * @param parser + */ +CloseQueryPacket.prototype.parse = function (parser) { + var reponseLength = parser._parseInt(); + this.casInfo = parser._parseBytes(4); + + var responseBuffer = parser._parseBuffer(reponseLength); + this.responseCode = parser._parseInt(); + if (this.responseCode < 0) { + this.errorCode = parser._parseInt(); + this.errorMsg = parser._parseNullTerminatedString(responseBuffer.length - 2 * DATA_TYPES.INT_SIZEOF); + if (this.errorMsg.length == 0) { + for (var iter = 0; iter < ErrorMessages.CASErrorMsgId.length; iter++) { + if (this.errorCode == ErrorMessages.CASErrorMsgId[iter][1]) { + this.errorMsg = ErrorMessages.CASErrorMsgId[iter][0]; + break; + } + } + } + } + + return this; +}; + diff --git a/src/packets/ExecuteQueryPacket.js b/src/packets/ExecuteQueryPacket.js new file mode 100644 index 0000000..2950153 --- /dev/null +++ b/src/packets/ExecuteQueryPacket.js @@ -0,0 +1,271 @@ +var DATA_TYPES = require('../constants/DataTypes'), + CAS = require('../constants/CASConstants'), + ErrorMessages = require('../constants/ErrorMessages'), + ColumnMetaData = require('../resultset/ColumnMetaData'), + ResultInfo = require('../resultset/ResultInfo'); + +module.exports = ExecuteQueryPacket; + +/** + * Constructor + * @param options + * @constructor + */ +function ExecuteQueryPacket(options) { + options = options || {}; + + this.casInfo = options.casInfo; + this.sql = options.sql; + this.autoCommit = options.autoCommitMode; + this.resultset = ''; + this.resultCacheLifetime = 0; + this.statementType = null; + this.bindCount = 0; + this.isUpdatable = false; + this.totalTupleCount = 0; + this.cache_reusable = 0; + this.resultCount = 0; + this.resultInfos = null; + this.columnCount = 0; + this.infoArray = new ColumnMetaData(); + this.resultInfos = new ResultInfo(); + this.handle = 0; + this.currentTupleCount = 0; + this.tupleCount = 0; + this.errorCode = 0; + this.errorMsg = ''; +} + +/** + * Write data + * @param writer + */ +ExecuteQueryPacket.prototype.write = function (writer) { + var bufferLength = DATA_TYPES.DATA_LENGTH_SIZEOF + DATA_TYPES.CAS_INFO_SIZE + + DATA_TYPES.BYTE_SIZEOF + DATA_TYPES.INT_SIZEOF + DATA_TYPES.INT_SIZEOF + DATA_TYPES.INT_SIZEOF + + this.sql.length + 1 + DATA_TYPES.INT_SIZEOF + DATA_TYPES.BYTE_SIZEOF + + DATA_TYPES.INT_SIZEOF + DATA_TYPES.BYTE_SIZEOF + DATA_TYPES.INT_SIZEOF + DATA_TYPES.BYTE_SIZEOF + + DATA_TYPES.INT_SIZEOF + DATA_TYPES.INT_SIZEOF + DATA_TYPES.INT_SIZEOF + DATA_TYPES.INT_SIZEOF + + DATA_TYPES.INT_SIZEOF + DATA_TYPES.INT_SIZEOF + DATA_TYPES.INT_SIZEOF + DATA_TYPES.INT_SIZEOF + + DATA_TYPES.INT_SIZEOF + DATA_TYPES.INT_SIZEOF; + + //Prepare info + writer._writeInt(bufferLength - DATA_TYPES.DATA_LENGTH_SIZEOF - DATA_TYPES.CAS_INFO_SIZE); + writer._writeBytes(4, this.casInfo); + writer._writeByte(CAS.CASFunctionCode.CAS_FC_PREPARE_AND_EXECUTE); + writer._writeInt(DATA_TYPES.INT_SIZEOF); + writer._writeInt(3);// number of CAS args + writer._writeNullTerminatedString(this.sql);//sql string + writer._writeInt(DATA_TYPES.BYTE_SIZEOF); + writer._writeByte(CAS.CCIPrepareOption.CCI_PREPARE_NORMAL); //prepare flag + writer._writeInt(DATA_TYPES.BYTE_SIZEOF); + writer._writeByte(this.autoCommit ? 1 : 0); //autocommit mode + + //Execute info + writer._writeInt(DATA_TYPES.BYTE_SIZEOF); + writer._writeByte(CAS.CCIExecutionOption.CCI_EXEC_QUERY_ALL); //execute flag + writer._writeInt(DATA_TYPES.INT_SIZEOF); + writer._writeInt(0); //max col size; + writer._writeInt(DATA_TYPES.INT_SIZEOF); + writer._writeInt(0); //max row size; + writer._writeInt(0);//NULL + writer._writeInt(2 * DATA_TYPES.INT_SIZEOF); // write cache time + writer._writeInt(0); //seconds + writer._writeInt(0); //useconds + writer._writeInt(DATA_TYPES.INT_SIZEOF); + writer._writeInt(0);//query timeout + + return writer; +}; + +/** + * Read data + * @param parser + */ +ExecuteQueryPacket.prototype.parse = function (parser) { + var reponseLength = parser._parseInt(); + this.casInfo = parser._parseInt(); + + //var responseBuffer = parser.parseBuffer(reponseLength); + this.handle = parser._parseInt(); + if (this.handle < 0) { + this.errorCode = parser._parseInt(); + this.errorMsg = parser._parseNullTerminatedString(reponseLength - 2 * DATA_TYPES.INT_SIZEOF); + if (this.errorMsg.length == 0) { + for (var iter = 0; iter < ErrorMessages.CASErrorMsgId.length; iter++) { + if (this.errorCode == ErrorMessages.CASErrorMsgId[iter][1]) { + this.errorMsg = ErrorMessages.CASErrorMsgId[iter][0]; + break; + } + } + } + return -1; //TODO Document this + } else { + this.resultCacheLifetime = parser._parseInt(); + this.statementType = parser._parseByte(); + this.bindCount = parser._parseInt(); + this.isUpdatable = (parser._parseByte() === 0); + this.columnCount = parser._parseInt(); + this.infoArray = new Array(); + for (i = 0; i < this.columnCount; i++) { + var info = new ColumnMetaData(); + info.ColumnType = parser._parseByte(); + info.scale = parser._parseShort(); + info.precision = parser._parseInt(); + var len = parser._parseInt(); + info.Name = parser._parseNullTerminatedString(len); + if (true) { //TODO Refactor condition + len = parser._parseInt(); + info.RealName = parser._parseNullTerminatedString(len); + len = parser._parseInt(); + info.TableName = parser._parseNullTerminatedString(len); + info.IsNullable = (parser._parseByte() === 1); + len = parser._parseInt(); + info.DafaultValue = parser._parseNullTerminatedString(len); + info.IsAutoIncrement = (parser._parseByte() === 1); + info.IsUniqueKey = (parser._parseByte() === 1); + info.IsPrimaryKey = (parser._parseByte() === 1); + info.IsReverseIndex = (parser._parseByte() === 1); + info.IsReverseUnique = (parser._parseByte() === 1); + info.IsForeignKey = (parser._parseByte() === 1); + info.IsShared = (parser._parseByte() === 1); + } + this.infoArray[i] = info; + } + + this.totalTupleCount = parser._parseInt(); + this.cache_reusable = parser._parseByte(); + this.resultCount = parser._parseInt(); + //read resultinfo + for (i = 0; i < this.resultCount; i++) { + var resultInfo = new ResultInfo(); + resultInfo.StmtType = parser._parseByte(); + resultInfo.ResultCount = parser._parseInt(); + resultInfo.Oid = parser._parseBytes(8); + resultInfo.CacheTimeSec = parser._parseInt(); + resultInfo.CacheTimeUsec = parser._parseInt(); + this.resultInfos[i] = resultInfo; + } + } + + if (this.statementType === CAS.CUBRIDStatementType.CUBRID_STMT_SELECT) { + var fetchCode = parser._parseInt(); + this.tupleCount = parser._parseInt(); + var columnNames = new Array(this.columnCount); + var columnDataTypes = new Array(this.columnCount); + var columnValues = new Array(this.tupleCount); + for (var i = 0; i < this.columnCount; i++) { + columnNames[i] = this.infoArray[i].Name; + columnDataTypes[i] = CAS.getCUBRIDDataType(this.infoArray[i].ColumnType); + } + + columnValues = this._getData(parser, this.tupleCount); + + return JSON.stringify({ + ColumnNames : columnNames, + ColumnDataTypes : columnDataTypes, + RowsCount : this.totalTupleCount, + ColumnValues : columnValues + }); + } + + return parser; +}; + +ExecuteQueryPacket.prototype._getData = function (parser, tupleCount) { + var columnValues = new Array(tupleCount); + for (var i = 0; i < tupleCount; i++) { + columnValues[i] = new Array(this.columnCount); + var index = parser._parseInt(); + var Oid = parser._parseBytes(8); + for (var j = 0; j < this.columnCount; j++) { + var size = parser._parseInt(); + var val; + if (size <= 0) { + val = null; + } else { + var type = CAS.CUBRIDDataType.CCI_U_TYPE_NULL; + + if (this.statementType === CAS.CUBRIDStatementType.CUBRID_STMT_CALL || + this.statementType === CAS.CUBRIDStatementType.CUBRID_STMT_EVALUATE || + this.statementType === CAS.CUBRIDStatementType.CUBRID_STMT_CALL_SP || + this.infoArray[j].ColumnType === CAS.CUBRIDDataType.CCI_U_TYPE_NULL) { + type = parser._parseByte(); + size--; + } else { + type = this.infoArray[j].ColumnType; + } + + val = this._readValue(j, type, size, parser); + columnValues[i][j] = val; + } + } + } + this.currentTupleCount += tupleCount; + return columnValues; +}; + +ExecuteQueryPacket.prototype._readValue = function (index, type, size, parser) { + switch (type) { + case CAS.CUBRIDDataType.CCI_U_TYPE_CHAR: + case CAS.CUBRIDDataType.CCI_U_TYPE_NCHAR: + case CAS.CUBRIDDataType.CCI_U_TYPE_STRING: + case CAS.CUBRIDDataType.CCI_U_TYPE_VARNCHAR: + return parser._parseNullTerminatedString(size); + + case CAS.CUBRIDDataType.CCI_U_TYPE_SHORT: + return parser._parseShort(); + + case CAS.CUBRIDDataType.CCI_U_TYPE_INT: + return parser._parseInt(); + + case CAS.CUBRIDDataType.CCI_U_TYPE_BIGINT: + return parser._parseLong(); + + case CAS.CUBRIDDataType.CCI_U_TYPE_FLOAT: + return parser._parseFloat(); + + case CAS.CUBRIDDataType.CCI_U_TYPE_DOUBLE: + case CAS.CUBRIDDataType.CCI_U_TYPE_MONETARY: + return parser._parseDouble(); + + case CAS.CUBRIDDataType.CCI_U_TYPE_NUMERIC: + return parser._parseNumeric(size); + + case CAS.CUBRIDDataType.CCI_U_TYPE_DATE: + return parser._parseDate(); + + case CAS.CUBRIDDataType.CCI_U_TYPE_TIME: + return parser._parseTime(); + + case CAS.CUBRIDDataType.CCI_U_TYPE_DATETIME: + return parser._parseDateTime(); + + case CAS.CUBRIDDataType.CCI_U_TYPE_TIMESTAMP: + return parser._parseTimeStamp(); + + case CAS.CUBRIDDataType.CCI_U_TYPE_OBJECT: + return parser._parseObject(); + + case CAS.CUBRIDDataType.CCI_U_TYPE_BIT: + case CAS.CUBRIDDataType.CCI_U_TYPE_VARBIT: + return parser._parseBytes(size)[0]; //TODO: Refactor this + + case CAS.CUBRIDDataType.CCI_U_TYPE_SET: + case CAS.CUBRIDDataType.CCI_U_TYPE_MULTISET: + case CAS.CUBRIDDataType.CCI_U_TYPE_SEQUENCE: + return parser._parseSequence(); + + case CAS.CUBRIDDataType.CCI_U_TYPE_BLOB: + return parser._parseBlob(size); + + case CAS.CUBRIDDataType.CCI_U_TYPE_CLOB: + return parser._parseClob(size); + + case CAS.CUBRIDDataType.CCI_U_TYPE_RESULTSET: + return parser._parseResultSet(); + + default: + return new Error('Invalid data type'); + } +}; diff --git a/src/packets/FetchPacket.js b/src/packets/FetchPacket.js new file mode 100644 index 0000000..c910fcf --- /dev/null +++ b/src/packets/FetchPacket.js @@ -0,0 +1,76 @@ +var DATA_TYPES = require('../constants/DataTypes'), + ErrorMessages = require('../constants/ErrorMessages'), + CAS = require('../constants/CASConstants'); + +module.exports = FetchPacket; + +/** + * Constructor + * @param options + * @constructor + */ +function FetchPacket(options) { + this.casInfo = options.casInfo; + + this.responseCode = 0; + this.errorCode = 0; + this.errorMsg = ''; +} + +/** + * Write data + * @param writer + */ +FetchPacket.prototype.write = function (writer, queryHandle) { + var bufferLength = DATA_TYPES.DATA_LENGTH_SIZEOF + DATA_TYPES.CAS_INFO_SIZE + + DATA_TYPES.BYTE_SIZEOF + DATA_TYPES.INT_SIZEOF + DATA_TYPES.INT_SIZEOF + + DATA_TYPES.INT_SIZEOF + DATA_TYPES.INT_SIZEOF + DATA_TYPES.INT_SIZEOF + DATA_TYPES.INT_SIZEOF + + DATA_TYPES.INT_SIZEOF + DATA_TYPES.BYTE_SIZEOF + DATA_TYPES.INT_SIZEOF + DATA_TYPES.INT_SIZEOF; + + writer._writeInt(bufferLength - DATA_TYPES.DATA_LENGTH_SIZEOF - DATA_TYPES.CAS_INFO_SIZE); + writer._writeBytes(4, this.casInfo); + + writer._writeByte(CAS.CASFunctionCode.CAS_FC_FETCH); + writer._writeInt(4); //int sizeof + writer._writeInt(queryHandle.handle); //serverHandler + writer._writeInt(4); //int sizeof + writer._writeInt(queryHandle.currentTupleCount + 1); //Start position (= current cursor position + 1) + writer._writeInt(4); //int sizeof + writer._writeInt(100); //Fetch size; 0 = default; recommended = 100 + writer._writeInt(1); //byte sizeof + writer._writeByte(0); //Is case sensitive + writer._writeInt(4); //int sizeof + writer._writeInt(0); //Is the ResultSet index...? + + return writer; +}; + +/** + * Read data + * @param parser + * @param queryHandle + */ +FetchPacket.prototype.parse = function (parser, queryHandle) { + var responseLength = parser._parseInt(); + this.casInfo = parser._parseBuffer(4); + + this.responseCode = parser._parseInt(); + if (this.responseCode !== 0) { + this.errorCode = parser._parseInt(); + this.errorMsg = parser._parseNullTerminatedString(responseLength - 2 * DATA_TYPES.INT_SIZEOF); + if (this.errorMsg.length == 0) { + for (var iter = 0; iter < ErrorMessages.CASErrorMsgId.length; iter++) { + if (this.errorCode == ErrorMessages.CASErrorMsgId[iter][1]) { + this.errorMsg = ErrorMessages.CASErrorMsgId[iter][0]; + break; + } + } + } + } + else { + this.tupleCount = parser._parseInt(); + return JSON.stringify({ColumnValues : queryHandle._getData(parser, this.tupleCount)}); + } +}; + + diff --git a/src/packets/GetEngineVersionPacket.js b/src/packets/GetEngineVersionPacket.js new file mode 100644 index 0000000..6f29948 --- /dev/null +++ b/src/packets/GetEngineVersionPacket.js @@ -0,0 +1,67 @@ +var DATA_TYPES = require('../constants/DataTypes'), + ErrorMessages = require('../constants/ErrorMessages'), + CAS = require('../constants/CASConstants'); + +module.exports = GetEngineVersionPacket; + +/** + * Constructor + * @param options + * @constructor + */ +function GetEngineVersionPacket(options) { + options = options || {}; + + this.casInfo = options.casInfo; + + this.engineVersion = ''; + + this.responseCode = 0; + this.errorCode = 0; + this.errorMsg = ''; +} + +/** + * Write data + * @param writer + */ +GetEngineVersionPacket.prototype.write = function (writer) { + var bufferLength = DATA_TYPES.DATA_LENGTH_SIZEOF + DATA_TYPES.CAS_INFO_SIZE + + DATA_TYPES.BYTE_SIZEOF + DATA_TYPES.INT_SIZEOF + DATA_TYPES.BYTE_SIZEOF; + + writer._writeInt(bufferLength - DATA_TYPES.DATA_LENGTH_SIZEOF - DATA_TYPES.CAS_INFO_SIZE); + writer._writeBytes(4, this.casInfo); + writer._writeByte(CAS.CASFunctionCode.CAS_FC_GET_DB_VERSION); + writer._writeInt(DATA_TYPES.BYTE_SIZEOF); + writer._writeByte(1); + + return writer; +}; + +/** + * Read data + * @param parser + */ +GetEngineVersionPacket.prototype.parse = function (parser) { + var reponseLength = parser._parseInt(); + this.casInfo = parser._parseBuffer(4); + this.responseCode = parser._parseInt(); + if (this.responseCode < 0) { + this.errorCode = parser._parseInt(); + this.errorMsg = parser._parseNullTerminatedString(reponseLength - 2 * DATA_TYPES.INT_SIZEOF); + if (this.errorMsg.length == 0) { + for (var iter = 0; iter < ErrorMessages.CASErrorMsgId.length; iter++) { + if (this.errorCode == ErrorMessages.CASErrorMsgId[iter][1]) { + this.errorMsg = ErrorMessages.CASErrorMsgId[iter][0]; + break; + } + } + } + } else { + this.engineVersion = parser._parseNullTerminatedString(reponseLength - DATA_TYPES.INT_SIZEOF); + } + + return this; +}; + + diff --git a/src/packets/OpenDatabasePacket.js b/src/packets/OpenDatabasePacket.js new file mode 100644 index 0000000..f9722a9 --- /dev/null +++ b/src/packets/OpenDatabasePacket.js @@ -0,0 +1,69 @@ +var DATA_TYPES = require('../constants/DataTypes'), + ErrorMessages = require('../constants/ErrorMessages'), + CAS = require('../constants/CASConstants'); + +module.exports = OpenDatabasePacket; + +/** + * Constructor + * @param options + * @constructor + */ +function OpenDatabasePacket(options) { + options = options || {}; + + this.database = options.database; + this.user = options.user; + this.password = options.password; + + this.casInfo = options.casInfo; + + this.responseCode = 0; + this.errorCode = 0; + this.errorMsg = ''; +} + +/** + * Write data + * @param writer + */ +OpenDatabasePacket.prototype.write = function (writer) { + writer._writeFixedLengthString(this.database, 0, 32); + writer._writeFixedLengthString(this.user, 0, 32); + writer._writeFixedLengthString(this.password, 0, 32); + writer._writeFiller(512, 0); + writer._writeFiller(20, 0); + + return writer; +}; + +/** + * Read data + * @param parser + */ +OpenDatabasePacket.prototype.parse = function (parser) { + var reponseLength = parser._parseInt(); + this.casInfo = parser._parseBytes(4); + + this.responseCode = parser._parseInt(); + if (this.responseCode < 0) { + this.errorCode = parser._parseInt(); + this.errorMsg = parser._parseNullTerminatedString(reponseLength - DATA_TYPES.INT_SIZEOF * 2); + if (this.errorMsg.length == 0) { + for (var iter = 0; iter < ErrorMessages.CASErrorMsgId.length; iter++) { + if (this.errorCode == ErrorMessages.CASErrorMsgId[iter][1]) { + this.errorMsg = ErrorMessages.CASErrorMsgId[iter][0]; + break; + } + } + } + } else { + //this.processId = this.responseCode; + this.brokerInfo = parser._parseBytes(8); + this.sessionId = parser._parseInt(); + } + + return this; +}; + + diff --git a/src/packets/PacketReader.js b/src/packets/PacketReader.js new file mode 100644 index 0000000..abb4e61 --- /dev/null +++ b/src/packets/PacketReader.js @@ -0,0 +1,348 @@ +var DATA_TYPES = require('./../constants/DataTypes'), + CAS = require('../constants/CASConstants'); + +module.exports = PacketReader; + +/** + * PacketReader + * @constructor + */ +function PacketReader() { + this._buffer = null; + this._offset = 0; +} + +/** + * Write a buffer value to the internal buffer + * @param buffer + */ +PacketReader.prototype.write = function (buffer) { + this._append(buffer); +}; + +//TODO Optimize the performance of this function +/** + * Append a buffer value to the internal buffer + * @param newBuffer + */ +PacketReader.prototype._append = function (newBuffer) { + var oldBuffer = this._buffer; + + if (!oldBuffer) { + this._buffer = newBuffer; + return; + } + + 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; +}; + +/** + * Returns an short value from the internal buffer + * @return {Number} + */ +PacketReader.prototype._parseShort = function () { + var value = 0; + + for (var i = DATA_TYPES.SHORT_SIZEOF - 1; i >= 0; i--) { + value += this._buffer[this._offset++] * Math.pow(256, i); + } + + if (value & 0x8000) { + return value - 0xFFFF - 1; + } else { + return value; + } +}; + +/** + * Returns an integer value from the internal buffer + * @return {Number} + */ +PacketReader.prototype._parseInt = function () { + var value = 0; + + for (var i = DATA_TYPES.INT_SIZEOF - 1; i >= 0; i--) { + value += this._buffer[this._offset++] * Math.pow(256, i); + } + + if (value & 0x80000000) { + return value - 0xFFFFFFFF - 1; + } else { + return value; + } +}; + +/** + * Returns a byte value from the internal buffer + * @return {Number} + */ +PacketReader.prototype._parseByte = function () { + return this._buffer[this._offset++]; +}; + +/** + * Returns a bytes array from the internal buffer + * @param bytesCount + * @return {Array} + */ +PacketReader.prototype._parseBytes = function (bytesCount) { + var buffer = this._buffer.slice(this._offset, this._offset + bytesCount); + + this._offset += bytesCount; + + return buffer; +}; + +/** + * Returns a buffer object from the internal buffer + * @param bytesCount + * @return {*} + */ +PacketReader.prototype._parseBuffer = function (bytesCount) { + var buffer = this._buffer.slice(this._offset, this._offset + bytesCount); + + this._offset += bytesCount; + + return buffer; +}; + +/** + * Returns a string value from the internal buffer + * @param bytesCount + * @return {Buffer} + */ +PacketReader.prototype._parseString = function (bytesCount) { + if (bytesCount <= 0) { + return ''; + } + + var start = this._offset; + var end = start + bytesCount; + var buffer = this._buffer.slice(start, end); + + var value = ''; + for (var i = 0; i < buffer.length; i++) { + value += String.fromCharCode(buffer[i]); + } + + this._offset = end; + + return value; +}; + +/** + * Returns a string value from the internal buffer + * @return {String} + */ +PacketReader.prototype._parseNullTerminatedString = function (length) { + if (length <= 0) { + return ''; + } + + var valueLen = length - 1; //get the actual null-terminated string length + var buffer = this._buffer.slice(this._offset, this._offset + valueLen); + var value = ''; + + for (var i = 0; i < buffer.length; i++) { + value += String.fromCharCode(buffer[i]); + } + + this._offset += valueLen; + this._parseByte(); //read also the null-terminate + + return value; +}; + +/** + * Returns a date value from the internal buffer + * @return {Date} + */ +PacketReader.prototype._parseDate = function () { + var year = this._parseShort(); + var month = this._parseShort(); + var day = this._parseShort(); + var hour = 0; + var min = 0; + var sec = 0; + var msec = 0; + + return new Date(year, month, day, hour, min, sec, msec); +}; + +/** + * Returns a datetime value from the internal buffer + * @return {Date} + */ +PacketReader.prototype._parseDateTime = function () { + var year = this._parseShort(); + var month = this._parseShort(); + var day = this._parseShort(); + var hour = this._parseShort(); + var min = this._parseShort(); + var sec = this._parseShort(); + var msec = this._parseShort(); + + return new Date(year, month, day, hour, min, sec, msec); +}; + +/** + * Returns a time value from the internal buffer + * @return {Date} + */ +PacketReader.prototype._parseTime = function () { + var year = 0; + var month = 0; + var day = 0; + var hour = this._parseShort(); + var min = this._parseShort(); + var sec = this._parseShort(); + var msec = 0; + + return new Date(year, month, day, hour, min, sec, msec); +}; + +/** + * Returns a timestamp value from the internal buffer + * @return {Date} + */ +PacketReader.prototype._parseTimeStamp = function () { + var year = this._parseShort(); + var month = this._parseShort(); + var day = this._parseShort(); + var hour = this._parseShort(); + var min = this._parseShort(); + var sec = this._parseShort(); + var msec = 0; + + return new Date(year, month, day, hour, min, sec, msec); +}; + +/** + * Returns a char value from the internal buffer + * @return {String} + */ +PacketReader.prototype._parseChar = function () { + var val = this._parseByte(); + + return String.fromCharCode(val); +}; + +/** + * Returns a long value from the internal buffer + * @return {String} + */ +PacketReader.prototype._parseLong = function () { + this._offset += DATA_TYPES.LONG_SIZEOF; + + return null; //Not implemented yet! +}; + +/** + * Returns a double value from the internal buffer + * @return {String} + */ +PacketReader.prototype._parseDouble = function () { + this._offset += DATA_TYPES.DOUBLE_SIZEOF; + + return null; //Not implemented yet! +}; + +/** + * Returns a floating point value from the internal buffer + * @return {String} + */ +PacketReader.prototype._parseFloat = function () { + this._offset += DATA_TYPES.FLOAT_SIZEOF; + + return null; //Not implemented yet! +}; + +/** + * Returns a numeric value from the internal buffer + * @return {String} + */ +PacketReader.prototype._parseNumeric = function (size) { + this._offset += size; + + return null; //Not implemented yet! +}; + +/** + * Returns a object value from the internal buffer + * @return {String} + */ +PacketReader.prototype._parseObject = function () { + this._offset += DATA_TYPES.OBJECT_SIZEOF; + + return null; //Not implemented yet! +}; + +/** + * Returns a blob object from the internal buffer + * @return {String} + */ +PacketReader.prototype._parseBlob = function (size) { + this._offset += size; + + return null; //Not implemented yet! +}; + +/** + * Returns a clob object from the internal buffer + * @return {String} + */ +PacketReader.prototype._parseClob = function (size) { + this._offset += size; + + return null; //Not implemented yet! +}; + +/** + * Returns a sequence of values from the internal buffer + * @return {String} + */ +PacketReader.prototype._parseSequence = function () { + var count = this._parseInt(); + var size = this._parseInt(); + this._offset += count * size; + + return null; //Not implemented yet! +}; + +/** + * Returns a ResultSet from the internal buffer + * @return {String} + */ +PacketReader.prototype._parseResultSet = function () { + this._offset += DATA_TYPES.RESULTSET_SIZEOF; + + return null; //Not implemented yet! +}; + +/** + * Return the number of bytes remaining unparsed/unread in the buffer + * @return {Number} + */ +PacketReader.prototype._bytesRemaining = function () { + return this._buffer.length - this._offset; +}; + +/** + * Return the internal buffer length + * @return {Number} + */ +PacketReader.prototype._packetLength = function () { + return this._buffer.length; +}; + diff --git a/src/packets/PacketWriter.js b/src/packets/PacketWriter.js new file mode 100644 index 0000000..272a9c9 --- /dev/null +++ b/src/packets/PacketWriter.js @@ -0,0 +1,240 @@ +var DATA_TYPES = require('./../constants/DataTypes'); + +module.exports = PacketWriter; + +/** + * Create a new instance + * @constructor + */ +function PacketWriter() { + this._buffer = new Buffer(0); + this._offset = 0; +} + +/** + * Write the current buffer content + * @return {*} + */ +PacketWriter.prototype._toBuffer = function () { + return this._buffer.slice(0, this._offset); +}; + +/** + * Write a byte value to the internal buffer + * @param value + */ +PacketWriter.prototype._writeByte = function (value) { + this._allocate(DATA_TYPES.BYTE_SIZEOF); + + this._buffer[this._offset++] = value & 0xFF; +}; + +/** + * Write a char value to the internal buffer + * @param value + */ +PacketWriter.prototype._writeChar = function (value) { + this._allocate(DATA_TYPES.BYTE_SIZEOF); + + this._buffer[this._offset++] = value.charCodeAt(0); +}; + +/** + * Write a bytes array to the internal buffer + * @param bytesCount + * @param value + */ +PacketWriter.prototype._writeBytes = function (bytesCount, value) { + this._allocate(bytesCount); + + for (var i = 0; i < bytesCount; i++) { + this._buffer[this._offset++] = value[i] & 0xFF; + } +}; + +/** + * Write a short value to the internal buffer + * @param value + */ +PacketWriter.prototype._writeShort = function (value) { + this._allocate(DATA_TYPES.SHORT_SIZEOF); + + this._writeByte((value >> 8) & 0xFF); + this._writeByte((value >> 0) & 0xFF); +}; + +/** + * Write a integer value to the internal buffer + * @param value + */ +PacketWriter.prototype._writeInt = function (value) { + this._allocate(DATA_TYPES.INT_SIZEOF); + + this._writeByte((value >> 24) & 0xFF); + this._writeByte((value >> 16) & 0xFF); + this._writeByte((value >> 8) & 0xFF); + this._writeByte((value >> 0) & 0xFF); +}; + +/** + * Write the specified value to the internal buffer + * @param bytesCount + * @param fillerValue + */ +PacketWriter.prototype._writeFiller = function (bytesCount, fillerValue) { + var fillerVal; + this._allocate(bytesCount); + + fillerValue = typeof fillerValue != 'undefined' ? fillerValue : 0x00; + + if (typeof fillerValue == 'string') { + fillerVal = fillerValue.charCodeAt(0); + } else { + fillerVal = fillerValue & 0xFF; + } + + for (var i = 0; i < bytesCount; i++) { + this._buffer[this._offset++] = fillerVal; + } +}; + +/** + * Write a null-terminate string to the internal buffer + * @param value + */ +PacketWriter.prototype._writeNullTerminatedString = function (value) { + //Typecast undefined into '' and numbers into strings + value = value || ''; + value = value + ''; + + var count = DATA_TYPES.INT_SIZEOF + value.length + DATA_TYPES.BYTE_SIZEOF; + this._allocate(count); + + //Write length + this._writeInt(value.length + 1); + + //Write string content + for (var i = 0; i < value.length; i++) { + this._buffer[this._offset++] = value.charCodeAt(i); + } + + //Write null-terminate + this._buffer[this._offset++] = 0; +}; + +/** + * Write a fixed-length string to the internal buffer + * @param value + * @param fillerValue + * @param fixedLength + */ +PacketWriter.prototype._writeFixedLengthString = function (value, fillerValue, fixedLength) { + var fillerVal; + //Typecast undefined into '' and numbers into strings + value = value || ''; + value = value + ''; + + var count = value.length; + if (count >= fixedLength) { + count = fixedLength; + } + + this._allocate(fixedLength); + + for (var i = 0; i < value.length; i++) { + this._buffer[this._offset++] = value[i].charCodeAt(0); + } + + if (typeof fillerValue == 'string') { + fillerVal = fillerValue.charCodeAt(0); + } else { + fillerVal = fillerValue & 0xFF; + } + + for (var j = 1; j <= fixedLength - count; j++) { + this._buffer[this._offset++] = fillerVal; + } +}; + +PacketWriter.prototype._writeDate = function (year, month, day) { + this._allocate(DATA_TYPES.DATETIME_SIZEOF); + + this._writeShort(year); + this._writeShort(month); + this._writeShort(day); + this._writeShort(0); + this._writeShort(0); + this._writeShort(0); + this._writeShort(0); +}; + +PacketWriter.prototype._writeDateTime = function (year, month, day, hour, min, sec, msec) { + this._allocate(DATA_TYPES.DATETIME_SIZEOF); + + this._writeShort(year); + this._writeShort(month); + this._writeShort(day); + this._writeShort(hour); + this._writeShort(min); + this._writeShort(sec); + this._writeShort(msec); +}; + +PacketWriter.prototype._writeTime = function (hour, min, sec) { + this._allocate(DATA_TYPES.DATETIME_SIZEOF); + + this._writeShort(0); + this._writeShort(0); + this._writeShort(0); + this._writeShort(hour); + this._writeShort(min); + this._writeShort(sec); + this._writeShort(0); +}; + +PacketWriter.prototype._writeTimestamp = function (year, month, day, hour, min, sec) { + this._allocate(DATA_TYPES.DATETIME_SIZEOF); + + this._writeShort(year); + this._writeShort(month); + this._writeShort(day); + this._writeShort(hour); + this._writeShort(min); + this._writeShort(sec); + this._writeShort(0); +}; + +/** + * Write a generic object value to the internal buffer + * @param value + */ +PacketWriter.prototype._writeBuffer = function (value) { + var count = value.length; + + this._allocate(count); + value.copy(this._buffer, this._offset); + this._offset += count; +}; + +//TODO Optimize the performance of this function +/** + * Allocate space to the internal buffer + * @param count + * @private + */ +PacketWriter.prototype._allocate = function (count) { + if (!this._buffer) { + this._buffer = new Buffer(count); + return; + } + + //Verify if we need to allocate more space + var bytesRemaining = this._buffer.length - this._offset; + if (bytesRemaining >= count) { + return; + } + + var oldBuffer = this._buffer; + this._buffer = new Buffer(oldBuffer.length + count); + oldBuffer.copy(this._buffer); +}; diff --git a/src/resultset/ColumnMetaData.js b/src/resultset/ColumnMetaData.js new file mode 100644 index 0000000..6a65a6c --- /dev/null +++ b/src/resultset/ColumnMetaData.js @@ -0,0 +1,28 @@ +var CAS = require('../constants/CASConstants'); + +module.exports = ColumnMetaData; + +/** + * Column meta data + * @constructor + */ +function ColumnMetaData() { + this.ColumnType = null; + this.CollectionElementType = CAS.CUBRIDDataType.CCI_U_TYPE_UNKNOWN; + this.Scale = -1; + this.Precision = -1; + this.RealName = null; + this.TableName = null; + this.Name = null; + this.IsNullabe = false; + + this.DefaultValue = null; + this.IsAutoIncrement = false; + this.IsUniqueKey = false; + this.IsPrimaryKey = false; + this.IsForeignKey = false; + this.IsReverseIndex = false; + this.IsReverseUnique = false; + this.IsShared = false; +} + diff --git a/src/resultset/Result2Array.js b/src/resultset/Result2Array.js new file mode 100644 index 0000000..17b52c0 --- /dev/null +++ b/src/resultset/Result2Array.js @@ -0,0 +1,96 @@ +/** + * Returns an array with the query results + * @param data + * @return {*} + * @constructor + */ +exports.GetResultsArray = function GetResultsArray(data) { + var fullArray = JSON.parse(data); + var arr = new Array(); + + var rowsCount = fullArray['ColumnValues'].length; + if (rowsCount <= 0) { + return null; + } else { + for (var i = 0; i < rowsCount; i++) { + arr[i] = fullArray['ColumnValues'][i]; + } + } + + return arr; +}; + +/** + * Returns an array with the query results column names + * @param data + * @return {*} + * @constructor + */ +exports.GetResultsColumnNamesArray = function GetResultsColumnNamesArray(data) { + var fullArray = ''; + + try { + fullArray = JSON.parse(data); + + var rowsCount = fullArray['ColumnNames'].length; + if (rowsCount <= 0) { + return null; + } else { + return fullArray['ColumnNames']; + } + } + catch (ex) { + return 'error'; + } +}; + +/** + * Returns an array with the query results columns data types + * @param data + * @return {*} + * @constructor + */ +exports.GetResultsColumnsTypeArray = function GetResultsColumnsTypeArray(data) { + var fullArray = ''; + + try { + fullArray = JSON.parse(data); + + var rowsCount = fullArray['ColumnDataTypes'].length; + if (rowsCount <= 0) { + return null; + } else { + return fullArray['ColumnDataTypes']; + } + } + catch (ex) { + return 'error'; + } +}; + +/** + * Returns the query results rows count + * Please note that this is the total query rows count and not the current "batch of data" rows count + * @param data + * @return {*} + * @constructor + */ +exports.GetResultsCount = function GetResultsCount(data) { + var fullArray = ''; + + try { + fullArray = JSON.parse(data); + + var rowsCount = fullArray['RowsCount']; + if (rowsCount <= 0) { + return null; + } else { + return fullArray['RowsCount']; + } + } + catch (ex) { + return -1; + } +}; + + diff --git a/src/resultset/ResultInfo.js b/src/resultset/ResultInfo.js new file mode 100644 index 0000000..2c76e7c --- /dev/null +++ b/src/resultset/ResultInfo.js @@ -0,0 +1,14 @@ +module.exports = ResultInfo; + +/** + * Result info + * @constructor + */ +function ResultInfo() { + this.StmtType = null; + this.ResultCount = 0; + this.Oid = null; + this.CacheTimeSec = 0; + this.CacheTimeUsec = 0; +} + diff --git a/src/unit_tests/test_ActionQueue.js b/src/unit_tests/test_ActionQueue.js new file mode 100644 index 0000000..031625e --- /dev/null +++ b/src/unit_tests/test_ActionQueue.js @@ -0,0 +1,50 @@ +var assert = require('assert'), + ActionQueue = require('../utils/ActionQueue'); + +var count = 0; +var startTime = (new Date()).getTime(); + +console.log('Unit test ' + module.filename.toString() + ' started...'); + +ActionQueue.enqueue( + [ + function (callback) { + callback(null, '1'); + }, + function (data, callback) { + setTimeout(callback(null, data + ',2'), 5000); + }, + function (data, callback) { + setTimeout(callback(null, data + ',3'), 1000); + } + , + function (data, callback) { + callback(null, data + ',4'); + } + ], + function (err, results) { + assert.equal(results, '1,2,3,4'); + console.log('enqueue unit test ended OK.'); + } +); + +ActionQueue.while( + function () { + return count < 5; + }, + function (callback) { + count++; + setTimeout(callback, 1000); + }, + function (err) { + if (err) { + throw err.toString(); + } else { + var endTime = (new Date()).getTime(); + // 5 seconds have passed + assert(endTime - startTime > 5 * 1000); + console.log('while unit test ended OK.'); + } + } +); + diff --git a/src/unit_tests/test_BatchExecuteNoQueryPacket.js b/src/unit_tests/test_BatchExecuteNoQueryPacket.js new file mode 100644 index 0000000..52a55ab --- /dev/null +++ b/src/unit_tests/test_BatchExecuteNoQueryPacket.js @@ -0,0 +1,45 @@ +var PacketReader = require('../packets/PacketReader'), + PacketWriter = require('../packets/PacketWriter'), + BatchExecuteNoQueryPacket = require('../packets/BatchExecuteNoQueryPacket'), + CAS = require('../constants/CASConstants'), + assert = require('assert'); + +function testBatchExecuteNoQueryPacket_01() { + var packetReader = new PacketReader(); + var packetWriter = new PacketWriter(); + var options = {SQLs : ['create table t1(id int)', 'drop table t1'], casInfo : [0, 255, 255, 255], autoCommitMode : 1}; + var batchExecuteNoQueryPacket = new BatchExecuteNoQueryPacket(options); + + batchExecuteNoQueryPacket.write(packetWriter); + assert.equal(packetWriter._toBuffer()[3], 52); //total length + + assert.equal(packetWriter._toBuffer()[4], 0); //casInfo + assert.equal(packetWriter._toBuffer()[5], 255); //casInfo + assert.equal(packetWriter._toBuffer()[6], 255); //casInfo + assert.equal(packetWriter._toBuffer()[7], 255); //casInfo + + assert.equal(packetWriter._toBuffer()[8], CAS.CASFunctionCode.CAS_FC_EXECUTE_BATCH); + assert.equal(packetWriter._toBuffer()[13], 1); + assert.equal(packetWriter._toBuffer().slice(18, 41).toString(), 'create table t1(id int)'); + assert.equal(packetWriter._toBuffer().slice(41 + 1 + 4, 59).toString(), 'drop table t1'); + + packetReader.write(new Buffer([0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0])); + batchExecuteNoQueryPacket.parse(packetReader); + + assert.equal(batchExecuteNoQueryPacket.casInfo[0], 0); //casInfo + assert.equal(batchExecuteNoQueryPacket.casInfo[1], 255); //casInfo + assert.equal(batchExecuteNoQueryPacket.casInfo[2], 255); //casInfo + assert.equal(batchExecuteNoQueryPacket.casInfo[3], 255); //casInfo + + assert.equal(batchExecuteNoQueryPacket.responseCode, 0); + + assert.equal(batchExecuteNoQueryPacket.errorCode, 0); + assert.equal(batchExecuteNoQueryPacket.errorMsg, ''); +} + +console.log('Unit test ' + module.filename.toString() + ' started...'); + +testBatchExecuteNoQueryPacket_01(); + +console.log('Unit test ended OK.'); + diff --git a/src/unit_tests/test_Cache.js b/src/unit_tests/test_Cache.js new file mode 100644 index 0000000..a067e14 --- /dev/null +++ b/src/unit_tests/test_Cache.js @@ -0,0 +1,36 @@ +var assert = require('assert'), + Cache = require('../utils/Cache'); + +console.log('Unit test ' + module.filename.toString() + ' started...'); + +var cache = new Cache(); + +cache.getSet(1, '1'); +cache.getSet(2, '22'); +cache.getSet(3, '333'); + +assert.equal(cache.contains(1), true); +assert.equal(cache.contains(2), true); +assert.equal(cache.contains(3), true); +assert.equal(cache.get(1), '1'); +assert.equal(cache.get(2), '22'); +assert.equal(cache.get(3), '333'); +assert.equal(cache.contains(9), false); + +cache.clear(); + +assert.equal(cache.contains(1), false); + +var cache2 = new Cache(3); + +cache2.getSet(1, '1'); + +setTimeout(function () { + assert.equal(cache2.contains(1), false); + console.log('Unit test ended OK.'); + }, + 4000 +); + + + diff --git a/src/unit_tests/test_ClientInfoExchangePacket.js b/src/unit_tests/test_ClientInfoExchangePacket.js new file mode 100644 index 0000000..e5f0d8b --- /dev/null +++ b/src/unit_tests/test_ClientInfoExchangePacket.js @@ -0,0 +1,25 @@ +var PacketReader = require('../packets/PacketReader'), + PacketWriter = require('../packets/PacketWriter'), + ClientInfoExchange = require('../packets/ClientInfoExchangePacket'), + assert = require('assert'); + +function testClientInfoExchangePacket_01() { + var packetReader = new PacketReader(); + var packetWriter = new PacketWriter(); + var clientInfoExchange = new ClientInfoExchange(); + + clientInfoExchange.write(packetWriter); + assert.equal(packetWriter._toBuffer().slice(0, 5), 'CUBRK'); + assert.equal(packetWriter._toBuffer()[5], 3); + + packetReader.write(new Buffer([0, 0, 1, 2])); //=258 + clientInfoExchange.parse(packetReader); + assert.equal(clientInfoExchange.newConnectionPort, 258); +} + +console.log('Unit test ' + module.filename.toString() + ' started...'); + +testClientInfoExchangePacket_01(); + +console.log('Unit test ended OK.'); + diff --git a/src/unit_tests/test_CloseDatabasePacket.js b/src/unit_tests/test_CloseDatabasePacket.js new file mode 100644 index 0000000..d59a797 --- /dev/null +++ b/src/unit_tests/test_CloseDatabasePacket.js @@ -0,0 +1,45 @@ +var PacketReader = require('../packets/PacketReader'), + PacketWriter = require('../packets/PacketWriter'), + CloseDatabasePacket = require('../packets/CloseDatabasePacket'), + CAS = require('../constants/CASConstants'), + assert = require('assert'); + +function testCloseConnectionPacket_01() { + var packetReader = new PacketReader(); + var packetWriter = new PacketWriter(); + var options = {casInfo : [0, 255, 255, 255]}; + var closeDatabasePacket = new CloseDatabasePacket(options); + + closeDatabasePacket.write(packetWriter); + assert.equal(packetWriter._toBuffer()[3], 1); //total length + + assert.equal(packetWriter._toBuffer()[4], 0); //casInfo + assert.equal(packetWriter._toBuffer()[5], 255); //casInfo + assert.equal(packetWriter._toBuffer()[6], 255); //casInfo + assert.equal(packetWriter._toBuffer()[7], 255); //casInfo + + assert.equal(packetWriter._toBuffer()[8], CAS.CASFunctionCode.CAS_FC_CON_CLOSE); + + packetReader.write(new Buffer([0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0])); + + assert.equal(packetReader._packetLength(), 12); + + closeDatabasePacket.parse(packetReader); + + assert.equal(closeDatabasePacket.casInfo[0], 0); //casInfo + assert.equal(closeDatabasePacket.casInfo[1], 255); //casInfo + assert.equal(closeDatabasePacket.casInfo[2], 255); //casInfo + assert.equal(closeDatabasePacket.casInfo[3], 255); //casInfo + + assert.equal(closeDatabasePacket.responseCode, 0); + + assert.equal(closeDatabasePacket.errorCode, 0); + assert.equal(closeDatabasePacket.errorMsg, ''); +} + +console.log('Unit test ' + module.filename.toString() + ' started...'); + +testCloseConnectionPacket_01(); + +console.log('Unit test ended OK.'); + diff --git a/src/unit_tests/test_ExecuteQueryPacket.js b/src/unit_tests/test_ExecuteQueryPacket.js new file mode 100644 index 0000000..0a7aac3 --- /dev/null +++ b/src/unit_tests/test_ExecuteQueryPacket.js @@ -0,0 +1,52 @@ +var PacketReader = require('../packets/PacketReader'), + PacketWriter = require('../packets/PacketWriter'), + ExecuteQueryPacket = require('../packets/ExecuteQueryPacket'), + CAS = require('../constants/CASConstants'), + assert = require('assert'); + +function testExecuteQueryPacket_01() { + var packetReader = new PacketReader(); + var packetWriter = new PacketWriter(); + var options = {sql : 'select * from code', casInfo : [0, 255, 255, 255], autoCommitMode : 1}; + var executeQueryPacket = new ExecuteQueryPacket(options); + + executeQueryPacket.write(packetWriter); + assert.equal(packetWriter._toBuffer()[3], 87); //total length + + assert.equal(packetWriter._toBuffer()[4], 0); //casInfo + assert.equal(packetWriter._toBuffer()[5], 255); //casInfo + assert.equal(packetWriter._toBuffer()[6], 255); //casInfo + assert.equal(packetWriter._toBuffer()[7], 255); //casInfo + + assert.equal(packetWriter._toBuffer()[8], CAS.CASFunctionCode.CAS_FC_PREPARE_AND_EXECUTE); + assert.equal(packetWriter._toBuffer()[16], 3); + assert.equal(packetWriter._toBuffer().slice(21, 39).toString(), 'select * from code'); + assert.equal(packetWriter._toBuffer()[44], 0); + assert.equal(packetWriter._toBuffer()[49], 1); + assert.equal(packetWriter._toBuffer()[54], CAS.CCIExecutionOption.CCI_EXEC_QUERY_ALL); + assert.equal(packetWriter._toBuffer()[62], 0); + assert.equal(packetWriter._toBuffer()[70], 0); + assert.equal(packetWriter._toBuffer()[82], 0); + assert.equal(packetWriter._toBuffer()[86], 0); + assert.equal(packetWriter._toBuffer()[94], 0); + + packetReader.write(new Buffer([0, 0, 1, 57, 0, 255, 255, 255, 0, 0, 0, 4, 255, 255, 255, 255, 21, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 7, 115, 95, 110, 97, 109, 101, 0, + 0, 0, 0, 1, 0, 0, 0, 0, 5, 99, 111, 100, 101, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 6, 0, 0, 0, 7, 102, + 95, 110, 97, 109, 101, 0, 0, 0, 0, 1, 0, 0, 0, 0, 5, 99, 111, 100, 101, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, + 0, 0, 0, 0, 1, 21, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 2, 88, 0, 0, 0, 0, 6, 77, 105, 120, 101, 100, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 87, 0, 0, + 0, 0, 6, 87, 111, 109, 97, 110, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 77, 0, 0, 0, 0, 4, 77, 97, 110, 0, 0, 0, 0, + 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 66, 0, 0, 0, 0, 7, 66, 114, 111, 110, 122, 101, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2, 83, 0, 0, 0, 0, 7, 83, 105, 108, 118, 101, 114, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 71, 0, 0, 0, 0, 5, + 71, 111, 108, 100, 0])); + + var resultSet = executeQueryPacket.parse(packetReader); + assert.equal(resultSet, '{"ColumnNames":["s_name","f_name"],"ColumnDataTypes":["Char","String"],"RowsCount":6,"ColumnValues":[["X","Mixed"],["W","Woman"],["M","Man"],["B","Bronze"],["S","Silver"],["G","Gold"]]}'); +} + +console.log('Unit test ' + module.filename.toString() + ' started...'); + +testExecuteQueryPacket_01(); + +console.log('Unit test ended OK.'); + diff --git a/src/unit_tests/test_GetEngineVersionPacket.js b/src/unit_tests/test_GetEngineVersionPacket.js new file mode 100644 index 0000000..7a6d978 --- /dev/null +++ b/src/unit_tests/test_GetEngineVersionPacket.js @@ -0,0 +1,50 @@ +var PacketReader = require('../packets/PacketReader'), + PacketWriter = require('../packets/PacketWriter'), + GetEngineVersionPacket = require('../packets/GetEngineVersionPacket'), + CAS = require('../constants/CASConstants'), + assert = require('assert'); + +function testGetEngineVersionPacket_01() { + var packetReader = new PacketReader(); + var packetWriter = new PacketWriter(); + var options = {casInfo : [0, 255, 255, 255]}; + var getEngineVersionPacket = new GetEngineVersionPacket(options); + + getEngineVersionPacket.write(packetWriter); + assert.equal(packetWriter._toBuffer()[3], 6); //total length + + assert.equal(packetWriter._toBuffer()[4], 0); //casInfo + assert.equal(packetWriter._toBuffer()[5], 255); //casInfo + assert.equal(packetWriter._toBuffer()[6], 255); //casInfo + assert.equal(packetWriter._toBuffer()[7], 255); //casInfo + + assert.equal(packetWriter._toBuffer()[8], CAS.CASFunctionCode.CAS_FC_GET_DB_VERSION); + assert.equal(packetWriter._toBuffer()[12], 1); + assert.equal(packetWriter._toBuffer()[13], 1); + + packetReader.write(new Buffer([0, 0, 0, 15, + 0, 255, 255, 255, + 0, 0, 0, 0])); + packetReader._append(new Buffer('8.4.1.0056')); + packetReader._append(new Buffer([0])); + getEngineVersionPacket.parse(packetReader); + + assert.equal(getEngineVersionPacket.casInfo[0], 0); //casInfo + assert.equal(getEngineVersionPacket.casInfo[1], 255); //casInfo + assert.equal(getEngineVersionPacket.casInfo[2], 255); //casInfo + assert.equal(getEngineVersionPacket.casInfo[3], 255); //casInfo + + assert.equal(getEngineVersionPacket.responseCode, 0); + + assert.equal(getEngineVersionPacket.errorCode, 0); + assert.equal(getEngineVersionPacket.errorMsg, ''); + + assert.equal(getEngineVersionPacket.engineVersion, '8.4.1.0056'); +} + +console.log('Unit test ' + module.filename.toString() + ' started...'); + +testGetEngineVersionPacket_01(); + +console.log('Unit test ended OK.'); + diff --git a/src/unit_tests/test_Helpers.js b/src/unit_tests/test_Helpers.js new file mode 100644 index 0000000..ab8bbc6 --- /dev/null +++ b/src/unit_tests/test_Helpers.js @@ -0,0 +1,29 @@ +var assert = require('assert'), + Helpers = require('../utils/Helpers'); + +console.log('Unit test ' + module.filename.toString() + ' started...'); + +var buffer = new Buffer(5); +buffer.write('12345'); +var value1 = ['6', '7', '8']; +var comb1 = Helpers.combineData(buffer, value1); + +assert.equal(comb1.toString(), '12345678'); + +var value2 = new Buffer('678'); +var comb2 = Helpers.combineData(buffer, value2); + +assert.equal(comb2.toString(), '12345678'); + +var value3 = new Buffer(3); +value3[0] = '6'.charCodeAt(0); +value3[1] = '7'.charCodeAt(0); +value3[2] = '8'.charCodeAt(0); + +var comb3 = Helpers.combineData(buffer, value3); + +assert.equal(comb3.toString(), '12345678'); + +console.log('Unit test ended OK.'); + + diff --git a/src/unit_tests/test_OpenDatabasePacket.js b/src/unit_tests/test_OpenDatabasePacket.js new file mode 100644 index 0000000..6a27b3c --- /dev/null +++ b/src/unit_tests/test_OpenDatabasePacket.js @@ -0,0 +1,44 @@ +var PacketReader = require('../packets/PacketReader'), + PacketWriter = require('../packets/PacketWriter'), + OpenDatabasePacket = require('../packets/OpenDatabasePacket'), + CAS = require('../constants/CASConstants'), + assert = require('assert'); + +function testLoginToDatabasePacket_01() { + var packetReader = new PacketReader(); + var packetWriter = new PacketWriter(); + var options = {database : 'demodb', user : 'public', password : ''}; + var openDatabasePacket = new OpenDatabasePacket(options); + + openDatabasePacket.write(packetWriter); + assert.equal(packetWriter._toBuffer().slice(0, 6).toString(), options.database); + assert.equal(packetWriter._toBuffer().slice(32, 38).toString(), options.user); + assert.equal(packetWriter._toBuffer().slice(64, 65)[0], 0); + + packetReader.write(new Buffer([0, 0, 0, 15, + 0, 255, 255, 255, + 0, 0, 0, 0, + 5, 5, 5, 5, 5, 5, 5, 5, + 0, 0, 0, 3])); + openDatabasePacket.parse(packetReader); + + assert.equal(openDatabasePacket.casInfo[0], 0); //casInfo + assert.equal(openDatabasePacket.casInfo[1], 255); //casInfo + assert.equal(openDatabasePacket.casInfo[2], 255); //casInfo + assert.equal(openDatabasePacket.casInfo[3], 255); //casInfo + + assert.equal(openDatabasePacket.responseCode, 0); + + assert.equal(openDatabasePacket.errorCode, 0); + assert.equal(openDatabasePacket.errorMsg, ''); + + assert.equal(openDatabasePacket.brokerInfo[0], 5); + assert.equal(openDatabasePacket.sessionId, 3); +} + +console.log('Unit test ' + module.filename.toString() + ' started...'); + +testLoginToDatabasePacket_01(); + +console.log('Unit test ended OK.'); + diff --git a/src/unit_tests/test_PacketWriter_PacketReader.js b/src/unit_tests/test_PacketWriter_PacketReader.js new file mode 100644 index 0000000..3ed2557 --- /dev/null +++ b/src/unit_tests/test_PacketWriter_PacketReader.js @@ -0,0 +1,251 @@ +var PacketReader = require('../packets/PacketReader'), + PacketWriter = require('../packets/PacketWriter'), + assert = require('assert'); + +var bValue = 0xEF; //=239 +var cValue = 'x'; +var shortValue = 0x70; +var iValue = 0x7ABC; +var shortValueSigned = 0x8001; +var iValueSigned = 0x80000001; +var sValue = '0987654321'; +var dValue = new Date(2012, 1, 2, 0, 0, 0, 0); + +function createPacketReader(bytes) { + var buffer = new Buffer(bytes); + var parser = new PacketReader(); + + parser._append(buffer); + + return parser; +} + +function testByte(value) { + var packetWriter = new PacketWriter(); + packetWriter._writeByte(value); + var packetReader = new PacketReader(); + packetReader.write(packetWriter._toBuffer()); + + var newValue = packetReader._parseByte(); + assert.equal(newValue, value); +} + +function testChar(value) { + var packetWriter = new PacketWriter(); + packetWriter._writeChar(value); + var packetReader = new PacketReader(); + packetReader.write(packetWriter._toBuffer()); + + var newValue = packetReader._parseChar(); + assert.equal(newValue, value); +} + +function testShort(value) { + var packetWriter = new PacketWriter(); + packetWriter._writeShort(value); + var packetReader = new PacketReader(); + packetReader.write(packetWriter._toBuffer()); + + var newValue = packetReader._parseShort(); + assert.equal(newValue, value); +} + +function testShortSigned(value, expectedValue) { + var packetWriter = new PacketWriter(); + packetWriter._writeShort(value); + var packetReader = new PacketReader(); + packetReader.write(packetWriter._toBuffer()); + + var newValue = packetReader._parseShort(); + assert.equal(newValue, expectedValue); +} + +function testInt(value) { + var packetWriter = new PacketWriter(); + packetWriter._writeInt(value); + var packetReader = new PacketReader(); + packetReader.write(packetWriter._toBuffer()); + + var newValue = packetReader._parseInt(); + assert.equal(newValue, value); +} + +function testIntSigned(value, expectedValue) { + var packetWriter = new PacketWriter(); + packetWriter._writeInt(value); + var packetReader = new PacketReader(); + packetReader.write(packetWriter._toBuffer()); + + var newValue = packetReader._parseInt(); + assert.equal(newValue, expectedValue); +} + +function testDate(year, month, day) { + var packetWriter = new PacketWriter(); + packetWriter._writeDate(year, month, day); + var packetReader = new PacketReader(); + packetReader.write(packetWriter._toBuffer()); + + var newValue = packetReader._parseDate(); + assert.equal(newValue.getFullYear(), year); + assert.equal(newValue.getMonth(), month); + assert.equal(newValue.getDate(), day); +} + +function testTime(hour, min, sec) { + var packetWriter = new PacketWriter(); + packetWriter._writeTime(hour, min, sec); + var packetReader = new PacketReader(); + packetReader.write(packetWriter._toBuffer()); + + var newValue = packetReader._parseTime(); + assert.equal(newValue.getHours(), hour); + assert.equal(newValue.getMinutes(), min); + assert.equal(newValue.getSeconds(), sec); +} + +function testDateTime(year, month, day, hour, min, sec, msec) { + var packetWriter = new PacketWriter(); + packetWriter._writeDateTime(year, month, day, hour, min, sec, msec); + var packetReader = new PacketReader(); + packetReader.write(packetWriter._toBuffer()); + + var newValue = packetReader._parseDateTime(); + assert.equal(newValue.getFullYear(), year); + assert.equal(newValue.getMonth(), month); + assert.equal(newValue.getDate(), day); + assert.equal(newValue.getHours(), hour); + assert.equal(newValue.getMinutes(), min); + assert.equal(newValue.getSeconds(), sec); + assert.equal(newValue.getMilliseconds(), msec); +} + +function testTimestamp(year, month, day, hour, min, sec) { + var packetWriter = new PacketWriter(); + packetWriter._writeTimestamp(year, month, day, hour, min, sec); + var packetReader = new PacketReader(); + packetReader.write(packetWriter._toBuffer()); + + var newValue = packetReader._parseTimeStamp(); + assert.equal(newValue.getFullYear(), year); + assert.equal(newValue.getMonth(), month); + assert.equal(newValue.getDate(), day); + assert.equal(newValue.getHours(), hour); + assert.equal(newValue.getMinutes(), min); + assert.equal(newValue.getSeconds(), sec); +} + +function testString(value) { + var packetWriter = new PacketWriter(); + packetWriter._writeNullTerminatedString(value); + var packetReader = new PacketReader(); + packetReader.write(packetWriter._toBuffer()); + + var len = packetReader._parseInt(); + var newValue = packetReader._parseNullTerminatedString(len); + assert.equal(newValue, value); +} + +function testFixedLengthString(value) { + var packetWriter = new PacketWriter(); + packetWriter._writeFixedLengthString(value, 'x', 15); + var packetReader = new PacketReader(); + packetReader.write(packetWriter._toBuffer()); + + var newValue = packetReader._parseString(15); + assert.equal(newValue, value + 'xxxxx'); +} + +function testFiller() { + var packetWriter = new PacketWriter(); + packetWriter._writeFiller(5, 'x'); + var packetReader = new PacketReader(); + packetReader.write(packetWriter._toBuffer()); + + var newValue = packetReader._parseString(5); + assert.equal(newValue, 'xxxxx'); + + packetWriter = new PacketWriter(); + packetWriter._writeFiller(5, 120); + packetReader = new PacketReader(); + packetReader.write(packetWriter._toBuffer()); + + newValue = packetReader._parseString(5); + assert.equal(newValue, 'xxxxx'); +} + +function testAllTypes(value1, value2, value3) { + var packetWriter = new PacketWriter(); + packetWriter._writeByte(value1); + packetWriter._writeInt(value2); + packetWriter._writeNullTerminatedString(value3); + var packetReader = new PacketReader(); + packetReader.write(packetWriter._toBuffer()); + + var newValue1 = packetReader._parseByte(); + assert.equal(newValue1, value1); + + var newValue2 = packetReader._parseInt(); + assert.equal(newValue2, value2); + + var len = packetReader._parseInt(); + var newValue3 = packetReader._parseNullTerminatedString(len); + assert.equal(newValue3, value3); +} + +function testPacketReaderBytes() { + var packetReader = createPacketReader([1, 2]); + + var newValue = packetReader._parseBytes(2); + assert.equal(newValue[0], 1); + assert.equal(newValue[1], 2); +} + +function testPacketReaderBuffer() { + var packetWriter = new PacketWriter(); + packetWriter._writeBuffer(new Buffer([1, 2])); + + var packetReader = new PacketReader(); + packetReader.write(packetWriter._toBuffer()); + var newValue = packetReader._parseBuffer(2); + assert.equal(newValue[0], 1); + assert.equal(newValue[1], 2); +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +console.log('Unit test ' + module.filename.toString() + ' started...'); + +//Test integer-types +testByte(bValue); +testShort(shortValue); +testInt(iValue); + +//Test signed integer-types +testShortSigned(shortValueSigned, -32767); +//testShortSigned(shortValueSigned, -1); +testIntSigned(iValueSigned, -2147483647); +//testIntSigned(iValueSigned, -1); + +//Test strings +testChar(cValue); +testString(sValue); +testFixedLengthString(sValue); + +//Test dates +testDate(dValue.getFullYear(), dValue.getMonth(), dValue.getDate()); +testTime(dValue.getHours(), dValue.getMinutes(), dValue.getSeconds()); +testDateTime(dValue.getFullYear(), dValue.getMonth(), dValue.getDate(), + dValue.getHours(), dValue.getMinutes(), dValue.getSeconds(), dValue.getMilliseconds()); +testTimestamp(dValue.getFullYear(), dValue.getMonth(), dValue.getDate(), + dValue.getHours(), dValue.getMinutes(), dValue.getSeconds()); + +//Other tests +testAllTypes(bValue, iValue, sValue); +testPacketReaderBytes(); +testPacketReaderBuffer(); +testFiller(); + +console.log('Unit test ended OK.'); + + diff --git a/src/unit_tests/test_Utils.js b/src/unit_tests/test_Utils.js new file mode 100644 index 0000000..6eda3d9 --- /dev/null +++ b/src/unit_tests/test_Utils.js @@ -0,0 +1,19 @@ +var assert = require('assert'), + GetResultsArray = require('./../resultset/Result2Array').GetResultsArray, + GetResultsColumnNamesArray = require('./../resultset/Result2Array').GetResultsColumnNamesArray, + GetResultsColumnTypesArray = require('./../resultset/Result2Array').GetResultsColumnsTypeArray, + GetResultsCount = require('./../resultset/Result2Array').GetResultsCount; + +var json_str = '{"ColumnNames":["s_name","f_name"],' + + '"ColumnDataTypes":["char","string"],' + + '"RowsCount":99,' + + '"ColumnValues":[["X","Mixed"],["W","Woman"],["M","Man"]]}'; + +console.log('Unit test ' + module.filename.toString() + ' started...'); + +assert.equal(GetResultsArray(json_str).toString(), 'X,Mixed,W,Woman,M,Man'); +assert.equal(GetResultsColumnNamesArray(json_str).toString(), 's_name,f_name'); +assert.equal(GetResultsColumnTypesArray(json_str).toString(), 'char,string'); +assert.equal(GetResultsCount(json_str), 99); + +console.log('Unit test ended OK.'); diff --git a/src/utils/ActionQueue.js b/src/utils/ActionQueue.js new file mode 100644 index 0000000..b03dc13 --- /dev/null +++ b/src/utils/ActionQueue.js @@ -0,0 +1,103 @@ +/* + Based on code originating from the 'async' module - author license included here below: + + Copyright (c) 2010 Caolan McMahon + + 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. + */ + +(function () { + var actionQueue = {}; + var self = this; + + if (typeof module != 'undefined' && module.exports) { + module.exports = actionQueue; + } else { + self.actionQueue = actionQueue; + } + + // nextTick implementation with browser-compatible fallback + if (typeof process === 'undefined' || !(process.nextTick)) { + actionQueue.nextTick = function (fn) { + setTimeout(fn, 0); + }; + } else { + actionQueue.nextTick = process.nextTick; + } + + actionQueue.enqueue = function (tasks, callback) { + callback = callback || function () { + }; + if (!tasks.length) { + return callback(); + } + var wrapIterator = function (iterator) { + return function (err) { + if (err) { + callback(err); + callback = function () { + }; + } else { + var args = Array.prototype.slice.call(arguments, 1); + var next = iterator.next(); + if (next) { + args.push(wrapIterator(next)); + } else { + args.push(callback); + } + actionQueue.nextTick(function () { + iterator.apply(null, args); + }); + } + }; + }; + wrapIterator(actionQueue._iterator(tasks))(); + }; + + actionQueue._iterator = function (tasks) { + var makeCallback = function (index) { + var fn = function () { + if (tasks.length) { + tasks[index].apply(null, arguments); + } + return fn.next(); + }; + fn.next = function () { + return (index < tasks.length - 1) ? makeCallback(index + 1) : null; + }; + return fn; + }; + return makeCallback(0); + }; + + actionQueue.while = function (test, iterator, callback) { + if (test()) { + iterator(function (err) { + if (err) { + return callback(err); + } + actionQueue.while(test, iterator, callback); + }); + } else { + callback(); + } + }; + +}()); + diff --git a/src/utils/Cache.js b/src/utils/Cache.js new file mode 100644 index 0000000..0fece97 --- /dev/null +++ b/src/utils/Cache.js @@ -0,0 +1,60 @@ +var Helpers = require('./Helpers'); + +/** + * Code adapted from MicroCache library: + * https://github.com/jeromeetienne/microcache.js + * License: https://github.com/jeromeetienne/MicroCache.js/blob/master/MIT-LICENSE.txt + */ + +var Cache = function (expireAfterSeconds) { + var _values = {}; + var createdTime = (new Date()).getTime(); + + var _expire = function () { + if (typeof expireAfterSeconds != 'undefined' && expireAfterSeconds > 0) { + if ((new Date()).getTime() - createdTime >= expireAfterSeconds * 1000) { + _values = {}; + createdTime = (new Date()).getTime(); + } + } + }; + + return { + get : function (key) { + _expire(); + return _values[key]; + }, + _set : function (key, value) { + _expire(); + _values[key] = value; + }, + contains : function (key) { + _expire(); + return key in _values; + }, + /** + * Get a value from cache; if the value is not found, then add it to the cache + */ + getSet : function (key, value) { + _expire(); + if (!this.contains(key)) { + this._set(key, value) + } else { + Helpers.logInfo('Value found in cache.'); + } + return this.get(key); + }, + /** + * Clear all values in cache + */ + clear : function () { + _values = {}; + } + } +}; + +if (typeof module != 'undefined' && ('exports' in module)) { + module.exports = Cache; +} + + diff --git a/src/utils/DBUtils.js b/src/utils/DBUtils.js new file mode 100644 index 0000000..2fa0171 --- /dev/null +++ b/src/utils/DBUtils.js @@ -0,0 +1,76 @@ +var CUBRIDConnection = require('../CUBRIDConnection'), + ActionQueue = require('../utils/ActionQueue'), + Helpers = require('../utils/Helpers'), + Result2Array = require('../resultset/Result2Array'); + +exports.getSingleValue = function (sql, brokerServer, brokerPort, user, password, database) { + var client = new CUBRIDConnection(brokerServer, brokerPort, user, password, database); + var ret = null; + + client.connect(function (err) { + if (err) { + throw err.message; + } else { + Helpers.logInfo('Connected.'); + Helpers.logInfo('Querying: ' + sql); + client.query(sql, function (err, result, queryHandle) { + if (err) { + throw err.message; + } else { + ret = Result2Array.GetResultsArray(result)[0]; + client.closeRequest(queryHandle, function (err) { + if (err) { + throw err.message; + } else { + Helpers.logInfo('Query closed.'); + client.close(function (err) { + if (err) { + throw err.message; + } else { + Helpers.logInfo('Connection closed.'); + Helpers.logInfo('Result: ' + ret); + return ret; + } + }) + } + }) + } + }) + } + }); +}; + +exports.getSingleValue2 = function (sql, brokerServer, brokerPort, user, password, database) { + var client = new CUBRIDConnection(brokerServer, brokerPort, user, password, database); + var ret = null; + + ActionQueue.enqueue( + [ + function (cb) { + client.connect(cb); + }, + function (cb) { + client.query(sql, cb); + }, + function (result, queryHandle, cb) { + ret = Result2Array.GetResultsArray(result)[0][0]; + client.closeRequest(queryHandle, cb); + }, + function (cb) { + client.close(cb); + } + ], + function (err) { + if (err == null) { + Helpers.logInfo('Value: ' + ret); + setTimeout(function () { + return ret; + }, 0); + //return ret; + } else { + throw err.message; + } + } + ); +}; + diff --git a/src/utils/Helpers.js b/src/utils/Helpers.js new file mode 100644 index 0000000..2b5e83a --- /dev/null +++ b/src/utils/Helpers.js @@ -0,0 +1,111 @@ +var DEBUG_ENABLED = require('../Config').DEBUG_ENABLED, + DATA_TYPES = require('../constants/DataTypes'), + ErrorMessages = require('../constants/ErrorMessages'), + CAS = require('../constants/CASConstants'); + +/** + * String extension + */ +if (typeof String.prototype.startsWith != 'function') { + // see below for better implementation! + String.prototype.startsWith = function (str) { + return this.indexOf(str) == 0; + }; +} + +exports.combineData = function (buffer, value) { + var newBuffer = new Buffer(buffer.length + value.length); + + buffer.copy(newBuffer, 0); + if (Array.isArray(value)) { + for (var i = 0; i < value.length; i++) { + if (typeof value[i] == 'string') { + newBuffer[buffer.length + i] = value[i].charCodeAt(0); + } else { + newBuffer[buffer.length + i] = value[i]; + } + } + } else { + if (typeof value == 'Buffer') { + value.copy(newBuffer, buffer.length); + } else { + new Buffer(value).copy(newBuffer, buffer.length); + } + } + + return newBuffer; +}; + +/** + * Overrides the console output + * Logs data to the standard console output + * @param data + */ +exports.logInfo = function logInfo(data) { + if (DEBUG_ENABLED) { + if (typeof window != 'undefined') { + if (!("console" in window) || !("firebug" in console)) { + var names = ["log", "debug", "info", "warn", "error", "assert", "dir", "dirxml", "group", "groupEnd", "time", "timeEnd", "count", "trace", "profile", "profileEnd"]; + window.console = {}; + for (var i = 0, len = names.length; i < len; ++i) { + window.console[names[i]] = function () { + }; + } + } + } + + console.warn(data); + } +}; + +/** + * Overrides the console output + * Logs data to the standard console output + * @param data + */ +exports.logError = function logError(data) { + if (DEBUG_ENABLED) { + if (typeof window != 'undefined') { + if (!("console" in window) || !("firebug" in console)) { + var names = ["log", "debug", "info", "warn", "logError", "assert", "dir", "dirxml", "group", "groupEnd", "time", "timeEnd", "count", "trace", "profile", "profileEnd"]; + window.console = {}; + for (var i = 0, len = names.length; i < len; ++i) { + window.console[names[i]] = function () { + }; + } + } + } + + console.error(data); + } +}; + +/** + * Get expected response length from the server + * @param buffer + * @return {Number} + */ +exports.getExpectedResponseLength = function (buffer) { + var value = 0; + + for (var i = 0; i < DATA_TYPES.INT_SIZEOF; i++) { + value += buffer[i] * Math.pow(256, DATA_TYPES.INT_SIZEOF - i - 1); + } + + return value + DATA_TYPES.DATA_LENGTH_SIZEOF + DATA_TYPES.CAS_INFO_SIZE; +}; + +/** + * Try to resolve the error code to a CUBRID error message + * @param errorCode + * @return {*} + */ +exports.resolveErrorCode = function (errorCode) { + for (var iter = 0; iter < ErrorMessages.CASErrorMsgId.length; iter++) { + if (errorCode == ErrorMessages.CASErrorMsgId[iter][1]) { + return ErrorMessages.CASErrorMsgId[iter][0]; + } + } +}; + + diff --git a/test/test_BadConnect.js b/test/test_BadConnect.js new file mode 100644 index 0000000..0794440 --- /dev/null +++ b/test/test_BadConnect.js @@ -0,0 +1,16 @@ +var assert = require('assert'), + CUBRIDConnection = require('../src/CUBRIDConnection'), + Helpers = require('../src/utils/Helpers'); + +var client = new CUBRIDConnection('localhost', 33000, 'public', 'xyz', 'demodb_xyz'); + +client.connect(function (err) { + if (err) { + assert(err.message == '-677:Failed to connect to database server, \'demodb_xyz\', on the following host(s): localhost:localhost'); + Helpers.logInfo('Test passed.'); + } else { + throw 'We should not get here!'; + } +}); + + diff --git a/test/test_BadSQLSyntax.js b/test/test_BadSQLSyntax.js new file mode 100644 index 0000000..e124349 --- /dev/null +++ b/test/test_BadSQLSyntax.js @@ -0,0 +1,31 @@ +var CUBRIDConnection = require('../src/CUBRIDConnection'), + Helpers = require('../src/utils/Helpers'), + assert = require('assert'); + +var CUBRIDClient = new CUBRIDConnection('localhost', 33000, 'public', '', 'demodb'); + +function errorHandler(err) { + Helpers.logError(err.message); + assert(err.message === '-493:Syntax: Unknown class "game_xyz". select * from game_xyz'); +} + +CUBRIDClient.connect(function (err) { + if (err) { + errorHandler(err); + } else { + Helpers.logInfo('Connected.'); + Helpers.logInfo('Querying: select * from game_xyz'); + CUBRIDClient.query('select * from game_xyz', function (err) { + if (err) { + errorHandler(err); + CUBRIDClient.close(); + Helpers.logInfo('Connection closed.'); + Helpers.logInfo('Test passed.'); + } else { + throw 'We should never get here!'; + } + }); + } +}); + + diff --git a/test/test_BasicBatchExecute.js b/test/test_BasicBatchExecute.js new file mode 100644 index 0000000..714fac8 --- /dev/null +++ b/test/test_BasicBatchExecute.js @@ -0,0 +1,45 @@ +var CUBRIDConnection = require('../src/CUBRIDConnection'), + Helpers = require('../src/utils/Helpers'), + assert = require('assert'); + +var CUBRIDClient = new CUBRIDConnection('localhost', 33000, 'public', '', 'demodb'); + +function errorHandler(err) { + throw err.message; +} + +CUBRIDClient.connect(function (err) { + if (err) { + errorHandler(err); + } else { + Helpers.logInfo('Connected.'); + CUBRIDClient.batchExecuteNoQuery(['drop table if exists node_test', 'create table node_test(id int)'], function (err) { + if (err) { + errorHandler(err); + } else { + CUBRIDClient.batchExecuteNoQuery(['insert into node_test values(1)'], function (err) { + if (err) { + errorHandler(err); + } else { + CUBRIDClient.batchExecuteNoQuery(['drop table node_test'], function (err) { + if (err) { + errorHandler(err); + } else { + CUBRIDClient.close(function (err) { + if (err) { + errorHandler(err); + } else { + Helpers.logInfo('Connection closed.'); + Helpers.logInfo('Test passed.'); + } + }) + } + }) + } + }) + } + }) + } +}); + + diff --git a/test/test_BasicBatchExecute_ConnectImplicit.js b/test/test_BasicBatchExecute_ConnectImplicit.js new file mode 100644 index 0000000..e9f47be --- /dev/null +++ b/test/test_BasicBatchExecute_ConnectImplicit.js @@ -0,0 +1,39 @@ +var CUBRIDConnection = require('../src/CUBRIDConnection'), + Helpers = require('../src/utils/Helpers'), + assert = require('assert'); + +var CUBRIDClient = new CUBRIDConnection('localhost', 33000, 'public', '', 'demodb'); + +function errorHandler(err) { + throw err.message; +} + +Helpers.logInfo('Connected.'); +CUBRIDClient.batchExecuteNoQuery(['drop table if exists node_test', 'create table node_test(id int)'], function (err) { + if (err) { + errorHandler(err); + } else { + CUBRIDClient.batchExecuteNoQuery(['insert into node_test values(1)'], function (err) { + if (err) { + errorHandler(err); + } else { + CUBRIDClient.batchExecuteNoQuery(['drop table node_test'], function (err) { + if (err) { + errorHandler(err); + } else { + CUBRIDClient.close(function (err) { + if (err) { + errorHandler(err); + } else { + Helpers.logInfo('Connection closed.'); + Helpers.logInfo('Test passed.'); + } + }) + } + }) + } + }) + } +}); + + diff --git a/test/test_BasicExtendedSelect.js b/test/test_BasicExtendedSelect.js new file mode 100644 index 0000000..c6a1234 --- /dev/null +++ b/test/test_BasicExtendedSelect.js @@ -0,0 +1,81 @@ +var CUBRIDConnection = require('../src/CUBRIDConnection'), + Helpers = require('../src/utils/Helpers'), + Result2Array = require('../src/resultset/Result2Array'), + assert = require('assert'); + +var CUBRIDClient = new CUBRIDConnection('localhost', 33000, 'public', '', 'demodb'); + +function errorHandler(err) { + throw err.message; +} + +CUBRIDClient.connect(function (err) { + if (err) { + errorHandler(err); + } else { + Helpers.logInfo('Connected.'); + CUBRIDClient.getEngineVersion(function (err, result) { + if (err) { + errorHandler(err); + } else { + Helpers.logInfo('CUBRID engine version: ' + result); + Helpers.logInfo('Querying: select * from game'); + CUBRIDClient.query('select * from game', function (err, result, queryHandle) { + if (err) { + errorHandler(err); + } else { + assert(Result2Array.GetResultsCount(result) === 235); + Helpers.logInfo('Query result rows count: ' + Result2Array.GetResultsCount(result)); + assert(Result2Array.GetResultsColumnNamesArray(result).toString() === 'host_year,event_code,athlete_code,stadium_code,nation_code,medal,game_date'); + Helpers.logInfo('Query result column names: ' + Result2Array.GetResultsColumnNamesArray(result)); + assert(Result2Array.GetResultsColumnsTypeArray(result).toString() === 'Int,Int,Int,Int,Char,Char,Date'); + Helpers.logInfo('Query result column data types: ' + Result2Array.GetResultsColumnsTypeArray(result)); + Helpers.logInfo('Query results:'); + var arr = Result2Array.GetResultsArray(result); + assert(arr.length == 235); + assert(arr[0].toString().startsWith('2004,20021,14345,30116,NGR,B,2004-09-27T') == true); + assert(arr[arr.length - 1].toString().startsWith('2004,20317,14457,30124,ITA,G,2004-09-25T') == true); + for (var j = 0; j < arr.length; j++) { + Helpers.logInfo(arr[j].toString()); + } + CUBRIDClient.fetch(queryHandle, function (err, result) { + if (err) { + errorHandler(err); + } else { + if (result) { + Helpers.logInfo('Fetch results:'); + var arr = Result2Array.GetResultsArray(result); + assert(arr.length == 241); + assert(arr[0].toString().startsWith('2004,20317,14375,30124,GRE,S,2004-09-25T') == true); + assert(arr[arr.length - 1].toString().startsWith('2004,20060,14340,30125,JPN,B,2004-09-24T') == true); + for (var k = 0; k < arr.length; k++) { + Helpers.logInfo(arr[k].toString()); + } + } else { + Helpers.logInfo('There is no more data to fetch.'); + } + CUBRIDClient.closeRequest(queryHandle, function (err) { + if (err) { + errorHandler(err); + } else { + Helpers.logInfo('Query closed.'); + CUBRIDClient.close(function (err) { + if (err) { + errorHandler(err); + } else { + Helpers.logInfo('Connection closed.'); + Helpers.logInfo('Test passed.'); + } + }) + } + }) + } + }) + } + }); + } + }); + } +}); + + diff --git a/test/test_BasicExtendedSelect_Sequence.js b/test/test_BasicExtendedSelect_Sequence.js new file mode 100644 index 0000000..ea8c37f --- /dev/null +++ b/test/test_BasicExtendedSelect_Sequence.js @@ -0,0 +1,51 @@ +var CUBRIDConnection = require('../src/CUBRIDConnection'), + ActionQueue = require('../src/utils/ActionQueue'), + Helpers = require('../src/utils/Helpers'), + Result2Array = require('../src/resultset/Result2Array'), + assert = require('assert'); + +var CUBRIDClient = new CUBRIDConnection('localhost', 33000, 'public', '', 'demodb'); + +ActionQueue.enqueue( + [ + function (cb) { + CUBRIDClient.connect(cb); + }, + function (cb) { + CUBRIDClient.getEngineVersion(cb); + }, + function (engineVersion, cb) { + Helpers.logInfo('Engine version is: ' + engineVersion); + CUBRIDClient.query('select * from code', cb); + }, + function (result, queryHandle, cb) { + assert(Result2Array.GetResultsCount(result) === 6); + Helpers.logInfo('Query result rows count: ' + Result2Array.GetResultsCount(result)); + Helpers.logInfo('Query results:'); + var arr = Result2Array.GetResultsArray(result); + assert(arr.length === 6); + assert(arr[0].toString() === 'X,Mixed'); + assert(arr[1].toString() === 'W,Woman'); + assert(arr[2].toString() === 'M,Man'); + assert(arr[3].toString() === 'B,Bronze'); + assert(arr[4].toString() === 'S,Silver'); + assert(arr[5].toString() === 'G,Gold'); + for (var k = 0; k < arr.length; k++) { + Helpers.logInfo(arr[k].toString()); + } + CUBRIDClient.closeRequest(queryHandle, cb); + Helpers.logInfo('Query closed.'); + }, + function (cb) { + CUBRIDClient.close(cb); + Helpers.logInfo('Connection closed.'); + } + ], + function (err) { + if (err == null) { + Helpers.logInfo('Test passed.'); + } else { + throw err.message; + } + } +); diff --git a/test/test_BasicMultiFetch.js b/test/test_BasicMultiFetch.js new file mode 100644 index 0000000..0e9f9a1 --- /dev/null +++ b/test/test_BasicMultiFetch.js @@ -0,0 +1,97 @@ +var CUBRIDConnection = require('../src/CUBRIDConnection'), + Helpers = require('../src/utils/Helpers'), + ActionQueue = require('../src/utils/ActionQueue'), + Result2Array = require('../src/resultset/Result2Array'), + assert = require('assert'); + +var CUBRIDClient = new CUBRIDConnection('localhost', 33000, 'public', '', 'demodb'); +var fetchResult; + +CUBRIDClient.connect(function (err) { + var errHandler = function (err) { + Helpers.logInfo('Error - ' + err.message); + throw err.message; + }; + + if (err) { + errHandler(err); + } else { + Helpers.logInfo('Connected.'); + CUBRIDClient.getEngineVersion(function (err, result) { + if (err) { + errHandler(err); + } else { + Helpers.logInfo('CUBRID engine version: ' + result); + CUBRIDClient.query('select * from game', function (err, result, queryHandle) { + if (err) { + errHandler(err); + } else { + assert(Result2Array.GetResultsCount(result) === 235); + Helpers.logInfo('Query result rows count: ' + Result2Array.GetResultsCount(result)); + assert(Result2Array.GetResultsColumnNamesArray(result).toString() === 'host_year,event_code,athlete_code,stadium_code,nation_code,medal,game_date'); + Helpers.logInfo('Query result column names: ' + Result2Array.GetResultsColumnNamesArray(result)); + assert(Result2Array.GetResultsColumnsTypeArray(result).toString() === 'Int,Int,Int,Int,Char,Char,Date'); + Helpers.logInfo('Query result column data types: ' + Result2Array.GetResultsColumnsTypeArray(result)); + Helpers.logInfo('Query results:'); + var arr = Result2Array.GetResultsArray(result); + assert(arr.length == 235); + assert(arr[0].toString().startsWith('2004,20021,14345,30116,NGR,B,2004-09-27T') == true); + assert(arr[arr.length - 1].toString().startsWith('2004,20317,14457,30124,ITA,G,2004-09-25T') == true); + for (var j = 0; j < arr.length; j++) { + Helpers.logInfo(arr[j].toString()); + } + + ActionQueue.while( + function () { + return fetchResult !== null; + }, + function (callback) { + CUBRIDClient.fetch(queryHandle, function (err, result) { + if (err) { + errHandler(err); + } else { + if (result !== null) { + Helpers.logInfo('Fetch results:'); + var arr = Result2Array.GetResultsArray(result); + assert(arr.length == 241 || arr.length == 224); + for (var k = 0; k < arr.length; k++) { + Helpers.logInfo(arr[k].toString()); + } + } else { + Helpers.logInfo('There is no more data to fetch.'); + } + fetchResult = result; + callback.call(err); + } + }) + }, + function (err) { + if (err) { + errHandler(err); + } else { + CUBRIDClient.closeRequest(queryHandle, function (err) { + if (err) { + errHandler(err); + } else { + Helpers.logInfo('Query closed.'); + CUBRIDClient.close(function (err) { + if (err) { + errHandler(err); + } else { + Helpers.logInfo('Connection closed.'); + Helpers.logInfo('Test passed.'); + } + }) + } + }) + } + } + ) + } + }) + } + }) + } +}); + + diff --git a/test/test_BasicMultiFetch_Sequence.js b/test/test_BasicMultiFetch_Sequence.js new file mode 100644 index 0000000..0cc2b24 --- /dev/null +++ b/test/test_BasicMultiFetch_Sequence.js @@ -0,0 +1,84 @@ +var CUBRIDConnection = require('../src/CUBRIDConnection'), + ActionQueue = require('../src/utils/ActionQueue'), + Helpers = require('../src/utils/Helpers'), + Result2Array = require('../src/resultset/Result2Array'), + assert = require('assert'); + +var CUBRIDClient = new CUBRIDConnection('localhost', 33000, 'public', '', 'demodb'); + +var returnedQueryHandle; +var fetchResult; + +ActionQueue.enqueue( + [ + function (cb) { + CUBRIDClient.connect(cb); + }, + function (cb) { + CUBRIDClient.getEngineVersion(cb); + }, + function (engineVersion, cb) { + Helpers.logInfo('EngineVersion is: ' + engineVersion); + CUBRIDClient.query('select * from game', cb); + }, + function (result, queryHandle, cb) { + assert(Result2Array.GetResultsCount(result) === 235); + Helpers.logInfo('Query result rows count: ' + Result2Array.GetResultsCount(result)); + assert(Result2Array.GetResultsColumnNamesArray(result).toString() === 'host_year,event_code,athlete_code,stadium_code,nation_code,medal,game_date'); + Helpers.logInfo('Query result column names: ' + Result2Array.GetResultsColumnNamesArray(result)); + assert(Result2Array.GetResultsColumnsTypeArray(result).toString() === 'Int,Int,Int,Int,Char,Char,Date'); + Helpers.logInfo('Query result column data types: ' + Result2Array.GetResultsColumnsTypeArray(result)); + Helpers.logInfo('Query results:'); + var arr = Result2Array.GetResultsArray(result); + assert(arr.length === 235); + assert(arr[0].toString().startsWith('2004,20021,14345,30116,NGR,B,2004-09-27T') == true); + assert(arr[arr.length - 1].toString().startsWith('2004,20317,14457,30124,ITA,G,2004-09-25T') == true); + for (var j = 0; j < arr.length; j++) { + Helpers.logInfo(arr[j].toString()); + } + returnedQueryHandle = queryHandle; + ActionQueue.while( + function () { + return fetchResult !== null; + }, + function (callback) { + CUBRIDClient.fetch(returnedQueryHandle, function (err, result) { + if (result !== null) { + Helpers.logInfo('Fetch results:'); + var arr = Result2Array.GetResultsArray(result); + assert(arr.length === 241 || arr.length === 224); + for (var k = 0; k < arr.length; k++) { + Helpers.logInfo(arr[k].toString()); + } + } else { + Helpers.logInfo('There is no more data to fetch.'); + } + fetchResult = result; + callback.call(err); + } + ) + }, + function (err) { + if (err) { + throw err.message; + } else { + cb.call(err); + } + } + ) + }, + function (cb) { + CUBRIDClient.closeRequest(returnedQueryHandle, cb); + }, + function (cb) { + CUBRIDClient.close(cb); + } + ], + function (err) { + if (err == null) { + Helpers.logInfo('Test passed.'); + } else { + throw 'Error executing test!'; + } + } +); diff --git a/test/test_BasicSelect.js b/test/test_BasicSelect.js new file mode 100644 index 0000000..e8323b2 --- /dev/null +++ b/test/test_BasicSelect.js @@ -0,0 +1,51 @@ +var CUBRIDConnection = require('../src/CUBRIDConnection'), + Helpers = require('../src/utils/Helpers'), + Result2Array = require('../src/resultset/Result2Array'), + assert = require('assert'); + +var CUBRIDClient = new CUBRIDConnection('localhost', 33000, 'public', '', 'demodb'); + +function errorHandler(err) { + throw err.message; +} + +CUBRIDClient.connect(function (err) { + if (err) { + errorHandler(err); + } else { + Helpers.logInfo('Connected.'); + Helpers.logInfo('Querying: select * from nation'); + CUBRIDClient.query('select * from nation', function (err, result, queryHandle) { + if (err) { + errorHandler(err); + } else { + assert(Result2Array.GetResultsCount(result) === 215); + Helpers.logInfo('Query result rows count: ' + Result2Array.GetResultsCount(result)); + var arr = Result2Array.GetResultsArray(result); + assert(arr.length === 215); + assert(arr[0].toString() === 'SRB,Serbia,Europe,Beograd'); + assert(arr[arr.length - 1].toString() === 'AFG,Afghanistan,Asia,Kabul'); + for (var j = 0; j < arr.length; j++) { + Helpers.logInfo(arr[j].toString()); + } + CUBRIDClient.closeRequest(queryHandle, function (err) { + if (err) { + errorHandler(err); + } else { + Helpers.logInfo('Query closed.'); + CUBRIDClient.close(function (err) { + if (err) { + errorHandler(err); + } else { + Helpers.logInfo('Connection closed.'); + Helpers.logInfo('Test passed.'); + } + }) + } + }) + } + }) + } +}); + + diff --git a/test/test_BasicSelect_Cache.js b/test/test_BasicSelect_Cache.js new file mode 100644 index 0000000..af7e1ca --- /dev/null +++ b/test/test_BasicSelect_Cache.js @@ -0,0 +1,61 @@ +var CUBRIDConnection = require('../src/CUBRIDConnection'), + Helpers = require('../src/utils/Helpers'), + Result2Array = require('../src/resultset/Result2Array'), + assert = require('assert'); + +var CUBRIDClient = new CUBRIDConnection('localhost', 33000, 'public', '', 'demodb', 1000 * 30); //30 sec. cache lifetime + +function errorHandler(err) { + throw err.message; +} + +CUBRIDClient.connect(function (err) { + if (err) { + errorHandler(err); + } else { + Helpers.logInfo('Connected.'); + Helpers.logInfo('Querying: select * from game'); + var startTime1 = (new Date()).getTime(); + CUBRIDClient.query('select * from game', function (err, result, queryHandle) { + if (err) { + errorHandler(err); + } else { + var endTime1 = (new Date()).getTime(); + Helpers.logInfo('[First] query execution time (ms): ' + (endTime1 - startTime1).toString()); + assert(Result2Array.GetResultsCount(result) === 8653); + + //Repeat query - results expected to come from cache this time + var startTime2 = (new Date()).getTime(); + CUBRIDClient.query('select * from game', function (err, result, queryHandle) { + if (err) { + errorHandler(err); + } else { + var endTime2 = (new Date()).getTime(); + Helpers.logInfo('[Second] query execution time (ms): ' + (endTime1 - startTime1).toString()); + assert(endTime2 - startTime2 < 50); + + assert(Result2Array.GetResultsCount(result) === 8653); + + CUBRIDClient.closeRequest(queryHandle, function (err) { + if (err) { + errorHandler(err); + } else { + Helpers.logInfo('Query closed.'); + CUBRIDClient.close(function (err) { + if (err) { + errorHandler(err); + } else { + Helpers.logInfo('Connection closed.'); + Helpers.logInfo('Test passed.'); + } + }) + } + }) + } + }) + } + }) + } +}); + + diff --git a/test/test_BasicSelect_ConnectImplicit.js b/test/test_BasicSelect_ConnectImplicit.js new file mode 100644 index 0000000..556cc97 --- /dev/null +++ b/test/test_BasicSelect_ConnectImplicit.js @@ -0,0 +1,46 @@ +var CUBRIDConnection = require('../src/CUBRIDConnection'), + Helpers = require('../src/utils/Helpers'), + Result2Array = require('../src/resultset/Result2Array'), + assert = require('assert'); + +var CUBRIDClient = new CUBRIDConnection('localhost', 33000, 'public', '', 'demodb'); + +function errorHandler(err) { + throw err.message; +} + +Helpers.logInfo('Connected.'); +Helpers.logInfo('Querying: select * from nation'); +CUBRIDClient.query('select * from nation', function (err, result, queryHandle) { + if (err) { + errorHandler(err); + } else { + assert(Result2Array.GetResultsCount(result) === 215); + Helpers.logInfo('Query result rows count: ' + Result2Array.GetResultsCount(result)); + var arr = Result2Array.GetResultsArray(result); + assert(arr.length === 215); + assert(arr[0].toString() === 'SRB,Serbia,Europe,Beograd'); + assert(arr[arr.length - 1].toString() === 'AFG,Afghanistan,Asia,Kabul'); + for (var j = 0; j < arr.length; j++) { + Helpers.logInfo(arr[j].toString()); + } + CUBRIDClient.closeRequest(queryHandle, function (err) { + if (err) { + errorHandler(err); + } else { + Helpers.logInfo('Query closed.'); + CUBRIDClient.close(function (err) { + if (err) { + errorHandler(err); + } else { + Helpers.logInfo('Connection closed.'); + Helpers.logInfo('Test passed.'); + } + }) + } + }) + } +}); + + + diff --git a/test/test_BatchExecuteVariant.js b/test/test_BatchExecuteVariant.js new file mode 100644 index 0000000..c29b02a --- /dev/null +++ b/test/test_BatchExecuteVariant.js @@ -0,0 +1,51 @@ +var CUBRIDConnection = require('../src/CUBRIDConnection'), + Helpers = require('../src/utils/Helpers'), + assert = require('assert'); + +var CUBRIDClient = new CUBRIDConnection('localhost', 33000, 'public', '', 'demodb'); + +function errorHandler(err) { + throw err.message; +} + +CUBRIDClient.connect(function (err) { + if (err) { + errorHandler(err); + } else { + Helpers.logInfo('Connected.'); + CUBRIDClient.batchExecuteNoQuery('drop table if exists node_test', function (err) { + if (err) { + errorHandler(err); + } else { + CUBRIDClient.batchExecuteNoQuery('create table node_test(id int)', function (err) { + if (err) { + errorHandler(err); + } else { + CUBRIDClient.batchExecuteNoQuery('insert into node_test values(1)', function (err) { + if (err) { + errorHandler(err); + } else { + CUBRIDClient.batchExecuteNoQuery('drop table node_test', function (err) { + if (err) { + errorHandler(err); + } else { + CUBRIDClient.close(function (err) { + if (err) { + errorHandler(err); + } else { + Helpers.logInfo('Connection closed.'); + Helpers.logInfo('Test passed.'); + } + }) + } + }) + } + }) + } + }) + } + }) + } +}); + + diff --git a/test/test_Connect.js b/test/test_Connect.js new file mode 100644 index 0000000..57faf0c --- /dev/null +++ b/test/test_Connect.js @@ -0,0 +1,34 @@ +var CUBRIDConnection = require('../src/CUBRIDConnection'), + Helpers = require('../src/utils/Helpers'), + assert = require('assert'); + +var CUBRIDClient = new CUBRIDConnection('localhost', 33000, 'public', '', 'demodb'); + +function errorHandler(err) { + throw err.message; +} + +CUBRIDClient.connect(function (err) { + if (err) { + errorHandler(err); + } else { + Helpers.logInfo('Connected OK.'); + CUBRIDClient.getEngineVersion(function (err, result) { + if (err) { + errorHandler(err); + } else { + Helpers.logInfo('CUBRID engine version: ' + result); + CUBRIDClient.close(function (err) { + if (err) { + errorHandler(err); + } else { + Helpers.logInfo('Connection closed.'); + Helpers.logInfo('Test passed.'); + } + }) + } + }) + } +}); + + diff --git a/test/test_Connect_Sequence.js b/test/test_Connect_Sequence.js new file mode 100644 index 0000000..0fe0a41 --- /dev/null +++ b/test/test_Connect_Sequence.js @@ -0,0 +1,28 @@ +var CUBRIDConnection = require('../src/CUBRIDConnection'), + ActionQueue = require('../src/utils/ActionQueue'), + Helpers = require('../src/utils/Helpers'); + +var CUBRIDClient = new CUBRIDConnection('localhost', 33000, 'public', '', 'demodb'); + +ActionQueue.enqueue( + [ + function (callback) { + CUBRIDClient.connect(callback); + }, + function (callback) { + CUBRIDClient.getEngineVersion(callback); + }, + function (version, callback) { + Helpers.logInfo('Engine version: ' + version); + CUBRIDClient.close(callback); + } + ], + function (err) { + if (err) { + throw err.message; + } else { + Helpers.logInfo('Test passed.'); + } + } +); + diff --git a/test/test_GetEngineVersion_ConnectImplicit.js b/test/test_GetEngineVersion_ConnectImplicit.js new file mode 100644 index 0000000..06853ef --- /dev/null +++ b/test/test_GetEngineVersion_ConnectImplicit.js @@ -0,0 +1,28 @@ +var CUBRIDConnection = require('../src/CUBRIDConnection'), + Helpers = require('../src/utils/Helpers'), + assert = require('assert'); + +var CUBRIDClient = new CUBRIDConnection('localhost', 33000, 'public', '', 'demodb'); + +function errorHandler(err) { + throw err.message; +} + +CUBRIDClient.getEngineVersion(function (err, result) { + if (err) { + errorHandler(err); + } else { + Helpers.logInfo('CUBRID engine version: ' + result); + CUBRIDClient.close(function (err) { + if (err) { + errorHandler(err); + } else { + assert(result.startsWith('8.4.1.') == true); + Helpers.logInfo('Connection closed.'); + Helpers.logInfo('Test passed.'); + } + }); + } +}); + + diff --git a/test/test_SelectConstant_01.js b/test/test_SelectConstant_01.js new file mode 100644 index 0000000..383ba62 --- /dev/null +++ b/test/test_SelectConstant_01.js @@ -0,0 +1,51 @@ +var CUBRIDConnection = require('../src/CUBRIDConnection'), + Helpers = require('../src/utils/Helpers'), + Result2Array = require('../src/resultset/Result2Array'), + assert = require('assert'); + +var CUBRIDClient = new CUBRIDConnection('localhost', 33000, 'public', '', 'demodb'); + +function errorHandler(err) { + throw err.message; +} + +CUBRIDClient.connect(function (err) { + if (err) { + errorHandler(err); + } else { + Helpers.logInfo('Connected.'); + Helpers.logInfo('Querying: select 1'); + CUBRIDClient.query('select 1', function (err, result, queryHandle) { + if (err) { + errorHandler(err); + } else { + assert(Result2Array.GetResultsCount(result) === 1); + Helpers.logInfo('Query result rows count: ' + Result2Array.GetResultsCount(result)); + Helpers.logInfo('Query results:'); + var arr = Result2Array.GetResultsArray(result); + assert(arr.length === 1); + assert(arr[0].toString() === '1'); + for (var j = 0; j < arr.length; j++) { + Helpers.logInfo(arr[j].toString()); + } + } + CUBRIDClient.closeRequest(queryHandle, function (err) { + if (err) { + errorHandler(err); + } else { + Helpers.logInfo('Query closed.'); + CUBRIDClient.close(function (err) { + if (err) { + errorHandler(err); + } else { + Helpers.logInfo('Connection closed.'); + Helpers.logInfo('Test passed.'); + } + }) + } + }) + }) + } +}); + + diff --git a/test/test_SelectConstant_02.js b/test/test_SelectConstant_02.js new file mode 100644 index 0000000..a6ee888 --- /dev/null +++ b/test/test_SelectConstant_02.js @@ -0,0 +1,52 @@ +var CUBRIDConnection = require('../src/CUBRIDConnection'), + Helpers = require('../src/utils/Helpers'), + Result2Array = require('../src/resultset/Result2Array'), + assert = require('assert'); + +var CUBRIDClient = new CUBRIDConnection('localhost', 33000, 'public', '', 'demodb'); + +function errorHandler(err) { + throw err.message; +} + +CUBRIDClient.connect(function (err) { + if (err) { + errorHandler(err); + } else { + Helpers.logInfo('Connected.'); + Helpers.logInfo('Querying: select null from nation where rownum < 3'); + CUBRIDClient.query('select null from nation where rownum < 3', function (err, result, queryHandle) { + if (err) { + errorHandler(err); + } else { + assert(Result2Array.GetResultsCount(result) === 2); + Helpers.logInfo('Query result rows count: ' + Result2Array.GetResultsCount(result)); + Helpers.logInfo('Query results:'); + var arr = Result2Array.GetResultsArray(result); + assert(arr.length === 2); + assert(arr[0].toString() === ''); + assert(arr[1].toString() === ''); + for (var j = 0; j < arr.length; j++) { + Helpers.logInfo(arr[j].toString()); + } + CUBRIDClient.closeRequest(queryHandle, function (err) { + if (err) { + errorHandler(err); + } else { + Helpers.logInfo('Query closed.'); + CUBRIDClient.close(function (err) { + if (err) { + errorHandler(err); + } else { + Helpers.logInfo('Connection closed.'); + Helpers.logInfo('Test passed.'); + } + }) + } + }) + } + }) + } +}); + + diff --git a/test/test_SocketError.js b/test/test_SocketError.js new file mode 100644 index 0000000..17d9b8c --- /dev/null +++ b/test/test_SocketError.js @@ -0,0 +1,30 @@ +var CUBRIDConnection = require('../src/CUBRIDConnection'), + Helpers = require('../src/utils/Helpers'), + assert = require('assert'); + +var CUBRIDClient = new CUBRIDConnection('localhost', 33000, 'public', '', 'demodb'); + +function errorHandler(err) { + Helpers.logInfo(err.message); + assert(err.message == 'This socket is closed.'); + Helpers.logInfo('Test passed.'); +} + +CUBRIDClient.connect(function (err) { + if (err) { + errorHandler(err); + } else { + Helpers.logInfo('Connected.'); + Helpers.logInfo('Querying: select * from nation'); + CUBRIDClient.socket.destroy(); + CUBRIDClient.query('select * from nation', function (err) { + if (err) { + errorHandler(err); + } else { + Helpers.logInfo('We should not get here!'); + } + }) + } +}); + +