diff --git a/.gitignore b/.gitignore index 331ea9c7..22c414e5 100644 --- a/.gitignore +++ b/.gitignore @@ -3,8 +3,14 @@ build node_modules deps .idea +<<<<<<< HEAD sandbox core oldtest .env -.vscode/ \ No newline at end of file +.vscode/ +======= +core +.env +.vscode +>>>>>>> master diff --git a/.npmignore b/.npmignore new file mode 100644 index 00000000..a318c48e --- /dev/null +++ b/.npmignore @@ -0,0 +1,11 @@ +a.out +build +node_modules +deps +.idea +sandbox +core +oldtest +.env +.vscode/ +test/ \ No newline at end of file diff --git a/LICENSE b/LICENSE index a7afdd01..9bd70f13 100644 --- a/LICENSE +++ b/LICENSE @@ -1,3 +1,4 @@ + Copyright (c) 2019, IBM Copyright (c) 2010 Lee Smith Permission is hereby granted, free of charge, to any person diff --git a/README.md b/README.md index 8e8133c9..775069ef 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,24 @@ -# node-odbc +node-odbc +--------- + +An asynchronous/synchronous interface for node.js to unixODBC and its supported +drivers. + +requirements +------------ + +* unixODBC binaries and development libraries for module compilation + * on Ubuntu/Debian `sudo apt-get install unixodbc unixodbc-dev` + * on RedHat/CentOS `sudo yum install unixODBC unixODBC-devel` + * on OSX + * using macports.org `sudo port unixODBC` + * using brew `brew install unixODBC` + * on IBM i `yum install unixODBC unixODBC-devel` (requires [yum](http://ibm.biz/ibmi-rpms)) +* odbc drivers for target database +* properly configured odbc.ini and odbcinst.ini. + +install +------- `node-odbc` is an ODBC database interface for Node.js. It allows connecting to any database management system if the system has been correctly configured, including installing of unixODBC and unixODBC-devel packages, installing an ODBC driver for your desired database, and configuring your odbc.ini and odbcinst.ini files. By using an ODBC, and it makes remote development a breeze through the use of ODBC data sources, and switching between DBMS systems is as easy as modifying your queries, as all your code can stay the same. @@ -42,7 +62,7 @@ npm install odbc ## API * [Connection](#Connection) - * [constructor (new Connection())](#constructor-new-connectionconnectionstring) + * [constructor: odbc.connect()](#constructor-odbcconnectconnectionstring) * [.query()](#querysql-parameters-callback) * [.callProcedure()](#callprocedurecatalog-schema-name-parameters-callback) * [.createStatement()](#createstatementcallback) @@ -53,8 +73,7 @@ npm install odbc * [.rollback()](#rollbackcallback) * [.close()](#closecallback) * [Pool](#Pool) - * [constructor (new Pool())](#constructor-new-poolconnectionstring) - * [.init()](#initcallback) + * [constructor: odbc.pool()](#constructor-odbcpoolconnectionstring) * [.connect()](#connectcallback) * [.query()](#querysql-parameters-callback-1) * [.close()](#closecallback-1) @@ -132,30 +151,77 @@ With this result structure, users can iterate over the result set like any old a ## **Connection** -Connection has the following functions: +A Connection is your means of connecting to the database through ODBC. + +### `constructor: odbc.connect(connectionString)` + +In order to get a connection, you must use the `.connect` function exported from the module. This asynchronously creates a Connection and gives it back to you. Like all asynchronous functions, this can be done either with callback functions or Promises. + +#### Parameters: +* **connectionString**: The connection string to connect to the database, usually by naming a DSN. Can also be a configuration object with the following properties: + * `connectionString` **REQUIRED**: The connection string to connect to the database + * `connectionTimeout`: How long before an idle connection will close, in seconds + * `loginTimeout`: How long before the connection process will attempt to connect before timing out, in seconds. +* **{OPTIONAL} callback**: The function called when `.connect` has finished connecting. If no callback function is given, `.connect` will return a native JavaScript `Promise`. Callback signature is: + * error: The error that occured in execution, or `null` if no error + * connection: The Connection object if a successful connection was made + +#### Examples: + +**Promises** + +```javascript +const odbc = require('odbc'); + +async function connectToDatabase() { + const connection1 = await odbc.connect('DSN=MYDSN'); + // connection1 is now an open Connection + + // or using a configuration object + const connectionConfig = { + connectionString: 'DSN=MYDSN', + connectionTimeout: 10, + loginTimeout: 10, + } + const connection2 = await odbc.connect(connectionConfig); + // connection2 is now an open Connection +} -### `constructor (new Connection(connectionString))` +connectToDatabase(); +``` -Create a Connection object, which is opened (synchronously!) +**Callbacks** ```javascript -const { Connection } = require('odbc'); -const connection = new Connection(connectionString); +const odbc = require('odbc'); +odbc.connect(connectionString, (error, connection) => { + // connection is now an open Connection +}); ``` +Once a Connection has been created with `odbc.connect`, you can use the following functions on the connection: + --- ### `.query(sql, parameters?, callback?)` -Run a query on the database. Can be passed an SQL string with parameter markers `?` and an array of parameters to bind to those markers. +Run a query on the database. Can be passed an SQL string with parameter markers `?` and an array of parameters to bind to those markers. Returns a [result array](#result-array). + +#### Parameters: +* **sql**: The SQL string to execute +* **parameters?**: An array of parameters to be bound the parameter markers (`?`) +* **{OPTIONAL} callback**: The function called when `.query` has finished execution. If no callback function is given, `.query` will return a native JavaScript `Promise`. Callback signature is: + * error: The error that occured in execution, or `null` if no error + * result: The result object from execution ```JavaScript -const { Connection } = require('odbc'); -const connection = new Connection(connectionString); -connection.query('SELECT * FROM QIWS.QCUSTCDT', (error, result) => { - if (error) { console.error(error) } - console.log(result); -}) +const odbc = require('odbc'); +const connection = odbc.connect(connectionString (error, connection) => { + connection.query('SELECT * FROM QIWS.QCUSTCDT', (error, result) => { + if (error) { console.error(error) } + console.log(result); + }); +}); ``` --- @@ -178,11 +244,11 @@ Calls a database procedure, returning the results in a [result array](#result-ar **Promises** ```javascript -const { Connection } = require('odbc'); -const connection = new Connection(`${process.env.CONNECTION_STRING}`); +const odbc = require('odbc'); -// can only use await keywork in an async function +// can only use await keyword in an async function async function callProcedureExample() { + const connection = await odbc.connect(`${process.env.CONNECTION_STRING}`); const statement = await connection.createStatement(); // now have a statement where sql can be prepared, bound, and executed } @@ -193,13 +259,15 @@ callProcedureExample(); **Callbacks** ```javascript -const { Connection } = require('odbc'); -const connection = new Connection(connectionString); -connection.callProcedure(null, null, 'MY_PROC', [undefined], (error, result) => { - if (error) { console.error(error) } // handle - // result contains an array of results, and has a `parameters` property to access parameters returned by the procedure. - console.log(result); -}) +const odbc = require('odbc'); + +odbc.connect(`${process.env.CONNECTION_STRING}`, (error, connection) => { + connection.callProcedure(null, null, 'MY_PROC', [undefined], (error, result) => { + if (error) { console.error(error) } // handle + // result contains an array of results, and has a `parameters` property to access parameters returned by the procedure. + console.log(result); + }); +}); ``` --- @@ -218,11 +286,11 @@ Returns a [Statement](#Statement) object from the connection. **Promises** ```javascript -const { Connection } = require('odbc'); -const connection = new Connection(`${process.env.CONNECTION_STRING}`); +const odbc = require('odbc'); -// can only use await keywork in an async function +// can only use await keyword in an async function async function statementExample() { + const connection = await odbc.connect(`${process.env.CONNECTION_STRING}`); const statement = await connection.createStatement(); // now have a statement where sql can be prepared, bound, and executed } @@ -233,13 +301,14 @@ statementExample(); **Callbacks** ```javascript -const { Connection } = require('odbc'); -const connection = new Connection(`${process.env.CONNECTION_STRING}`); +const odbc = require('odbc'); // returns information about all tables in schema MY_SCHEMA -connection.createStatement((error, statement) => { - if (error) { return; } // handle - // now have a statement where sql can be prepared, bound, and executed +odbc.connect(`${process.env.CONNECTION_STRING}`, (error, connection) => { + connection.createStatement((error, statement) => { + if (error) { return; } // handle + // now have a statement where sql can be prepared, bound, and executed + }); }); ``` @@ -263,12 +332,12 @@ Returns information about the table specified in the parameters by calling the O **Promises** ```javascript -const { Connection } = require('odbc'); -const connection = new Connection(`${process.env.CONNECTION_STRING}`); +const odbc = require('odbc'); -// can only use await keywork in an async function +// can only use await keyword in an async function async function getTables() { // returns information about all tables in schema MY_SCHEMA + const connection = await odbc.connect(`${process.env.CONNECTION_STRING}`); const result = await connection.tables(null, 'MY_SCHEMA', null, null); console.log(result); } @@ -279,13 +348,14 @@ getTables(); **Callbacks** ```javascript -const { Connection } = require('odbc'); -const connection = new Connection(`${process.env.CONNECTION_STRING}`); +const odbc = require('odbc'); // returns information about all tables in schema MY_SCHEMA -connection.columns(null, "MY_SCHEMA", null, null, (error, result) => { - if (error) { return; } // handle - console.log(result); +odbc.connect(`${process.env.CONNECTION_STRING}`, (error, connection) => { + connection.columns(null, "MY_SCHEMA", null, null, (error, result) => { + if (error) { return; } // handle + console.log(result); + }); }); ``` @@ -309,12 +379,12 @@ Returns information about the columns specified in the parameters by calling the **Promises** ```javascript -const { Connection } = require('odbc'); -const connection = new Connection(`${process.env.CONNECTION_STRING}`); +const odbc = require('odbc'); -// can only use await keywork in an async function +// can only use await keyword in an async function async function getColumns() { // returns information about all columns in table MY_SCEHMA.MY_TABLE + const connection = await odbc.connect(`${process.env.CONNECTION_STRING}`); const result = await connection.columns(null, 'MY_SCHEMA', 'MY_TABLE', null); console.log(result); } @@ -325,13 +395,14 @@ getColumns(); **Callbacks** ```javascript -const { Connection } = require('odbc'); -const connection = new Connection(`${process.env.CONNECTION_STRING}`); +const odbc = require('odbc'); // returns information about all columns in table MY_SCEHMA.MY_TABLE -connection.columns(null, "MY_SCHEMA", "MY_TABLE", null, (error, result) => { - if (error) { return; } // handle - console.log(result); +odbc.connect(`${process.env.CONNECTION_STRING}`, (error, connection) => { + connection.columns(null, "MY_SCHEMA", "MY_TABLE", null, (error, result) => { + if (error) { return; } // handle + console.log(result); + }); }); ``` @@ -350,11 +421,11 @@ Begins a transaction on the connection. The transaction can be committed by call **Promises** ```javascript -const { Connection } = require('odbc'); -const connection = new Connection(`${process.env.CONNECTION_STRING}`); +const odbc = require('odbc'); -// can only use await keywork in an async function +// can only use await keyword in an async function async function transaction() { + const connection = await odbc.connect(`${process.env.CONNECTION_STRING}`); await connection.beginTransaction(); // transaction is now open } @@ -365,13 +436,14 @@ transaction(); **Callbacks** ```javascript -const { Connection } = require('odbc'); -const connection = new Connection(`${process.env.CONNECTION_STRING}`); +const odbc = require('odbc'); // returns information about all columns in table MY_SCEHMA.MY_TABLE -connection.beginTransaction((error) => { - if (error) { return; } // handle - // transaction is now open +odbc.connect(`${process.env.CONNECTION_STRING}`, (error, connection) => { + connection.beginTransaction((error) => { + if (error) { return; } // handle + // transaction is now open + }); }); ``` @@ -390,11 +462,11 @@ Commits an open transaction. If called on a connection that doesn't have an open **Promises** ```javascript -const { Connection } = require('odbc'); -const connection = new Connection(`${process.env.CONNECTION_STRING}`); +const odbc = require('odbc'); -// can only use await keywork in an async function +// can only use await keyword in an async function async function commitTransaction() { + const connection = await odbc.connect(`${process.env.CONNECTION_STRING}`); await connection.beginTransaction(); const insertResult = await connection.query('INSERT INTO MY_TABLE VALUES(1, \'Name\')'); await connection.commit(); @@ -407,18 +479,19 @@ commitTransaction(); **Callbacks** ```javascript -const { Connection } = require('odbc'); -const connection = new Connection(`${process.env.CONNECTION_STRING}`); +const odbc = require('odbc'); // returns information about all columns in table MY_SCEHMA.MY_TABLE -connection.beginTransaction((error1) => { - if (error1) { return; } // handle - connection.query('INSERT INTO MY_TABLE VALUES(1, \'Name\')', (error2, result) => { - if (error2) { return; } // handle - connection.commit((error3) => { - // INSERT query has now been committed +odbc.connect(`${process.env.CONNECTION_STRING}`, (error, connection) => { + connection.beginTransaction((error1) => { + if (error1) { return; } // handle + connection.query('INSERT INTO MY_TABLE VALUES(1, \'Name\')', (error2, result) => { + if (error2) { return; } // handle + connection.commit((error3) => { + // INSERT query has now been committed + }) }) - }) + }); }); ``` @@ -438,11 +511,11 @@ Rolls back an open transaction. If called on a connection that doesn't have an o **Promises** ```javascript -const { Connection } = require('odbc'); -const connection = new Connection(`${process.env.CONNECTION_STRING}`); +const odbc = require('odbc'); -// can only use await keywork in an async function +// can only use await keyword in an async function async function rollbackTransaction() { + const connection = await odbc.connect(`${process.env.CONNECTION_STRING}`); await connection.beginTransaction(); const insertResult = await connection.query('INSERT INTO MY_TABLE VALUES(1, \'Name\')'); await connection.rollback(); @@ -455,18 +528,19 @@ rollbackTransaction(); **Callbacks** ```javascript -const { Connection } = require('odbc'); -const connection = new Connection(`${process.env.CONNECTION_STRING}`); +const odbc = require('odbc'); // returns information about all columns in table MY_SCEHMA.MY_TABLE -connection.beginTransaction((error1) => { - if (error1) { return; } // handle - connection.query('INSERT INTO MY_TABLE VALUES(1, \'Name\')', (error2, result) => { - if (error2) { return; } // handle - connection.rollback((error3) => { - // INSERT query has now been rolled back +odbc.connect(`${process.env.CONNECTION_STRING}`, (error, connection) => { + connection.beginTransaction((error1) => { + if (error1) { return; } // handle + connection.query('INSERT INTO MY_TABLE VALUES(1, \'Name\')', (error2, result) => { + if (error2) { return; } // handle + connection.rollback((error3) => { + // INSERT query has now been rolled back + }) }) - }) + }); }); ``` @@ -474,58 +548,90 @@ connection.beginTransaction((error1) => { ### `.close(callback?)` -Closes and open connection. Any transactions on the connection that have not been committed or rolledback will be rolledback. +Closes and open connection. Any transactions on the connection that have not been ended will be rolledback. ---- ---- +#### Parameters: +* **{OPTIONAL} callback**: The function called when `.close` has finished clsoing the connection. If no callback function is given, `.close` will return a native JavaScript `Promise`. Callback signature is: + * error: The error that occured in execution, or `null` if no error +#### Examples: -### **Pool** +**Promises** -### `constructor (new Pool(connectionString))` +```javascript +const odbc = require('odbc'); -Creates a instance of the Pool class, storing information but not opening any connections. +// can only use await keyword in an async function +async function closeConnection() { + const connection = await odbc.connect(`${process.env.CONNECTION_STRING}`); + // do something with your connection here + await connection.close(); +} -```JavaScript -const { Pool } = require('odbc'); -const pool = new Pool(connectionString); +rollbackTransaction(); ``` -**PLEASE NOTE:** The pool will not have any open connections until you call pool.init(); +**Callbacks** + +```javascript +const odbc = require('odbc'); + +odbc.connect(`${process.env.CONNECTION_STRING}`, (error, connection) => { + // do something with your connection here + connection.close((error) => { + if (error) { return; } // handle + // connection is now closed + }) +}); +``` + +--- +--- + + +### **Pool** + +### `constructor: odbc.pool(connectionString)` -### `.init(callback?)` +In order to get a Pool, you must use the `.pool` function exported from the module. This asynchronously creates a Pool of a number of Connections and returns it to you. Like all asynchronous functions, this can be done either with callback functions or Promises. -Opens all the connections in the Pool asynchronously. Returns once all of the Connections have been opened. +Note that `odbc.pool` will return from callback or Promise as soon as it has created 1 connection. It will continue to spin up Connections and add them to the Pool in the background, but by returning early it will allow you to use the Pool as soon as possible. #### Parameters: -* **{OPTIONAL} callback**: The function called when `.init` has finished execution. If no callback function is given, `.init` will return a native JavaScript `Promise`. Callback signature is: +* **connectionString**: The connection string to connect to the database for all connections in the pool, usually by naming a DSN. Can also be a configuration object with the following properties: + * `connectionString` **REQUIRED**: The connection string to connect to the database + * `connectionTimeout`: How long before an idle connection will close, in seconds + * `loginTimeout`: How long before the connection process will attempt to connect before timing out, in seconds. + * `initialSize`: The initial number of Connections created in the Pool + * `incrementSzie`: How many additional Connections to create when all of the Pool's connections are taken + * `maxSize`: The maximum number of open Connections the Pool will create + * `shrink`: Whether or not the number of Connections should shrink to `initialSize` as they free up +* **{OPTIONAL} callback**: The function called when `.connect` has finished connecting. If no callback function is given, `.connect` will return a native JavaScript `Promise`. Callback signature is: * error: The error that occured in execution, or `null` if no error + * connection: The Connection object if a successful connection was made #### Examples: **Promises** -```javascript -const { Pool } = require('odbc'); +```JavaScript +const odbc = require('odbc'); -// can only use await keywork in an async function -async function connectExample() { - const pool = new Pool(`${process.env.CONNECTION_STRING}`); - await pool.init(); - // all Connections in the pool are now opened +// can only use await keyword in an async function +async function createPool() { + const pool = await odbc.pool(`${process.env.CONNECTION_STRING}`); + // can now do something with the Pool } -connectExample(); +createPool(); ``` **Callbacks** -```javascript -const { Pool } = require('odbc'); -const pool = new Pool(`${process.env.CONNECTION_STRING}`); -pool.init((error1) => { - if (error1) { return; } // handle - // all Connections in the pool are now opened +```JavaScript +const odbc = require('odbc'); +const pool = odbc.pool('DSN=MyDSN', (error, pool) => { + // pool now has open connections }); ``` @@ -543,12 +649,11 @@ Returns a [Connection](#connection) object for you to use from the Pool. Doesn't **Promises** ```javascript -const { Pool } = require('odbc'); +const odbc = require('odbc'); -// can only use await keywork in an async function +// can only use await keyword in an async function async function connectExample() { - const pool = new Pool(`${process.env.CONNECTION_STRING}`); - await pool.init(); + const pool = await odbc.pool(`${process.env.CONNECTION_STRING}`); const connection = await pool.connect(); // now have a Connection to do work with } @@ -559,9 +664,8 @@ connectExample(); **Callbacks** ```javascript -const { Pool } = require('odbc'); -const pool = new Pool(`${process.env.CONNECTION_STRING}`); -pool.init((error1) => { +const odbc = require('odbc'); +odbc.pool(`${process.env.CONNECTION_STRING}`, (error1, pool) => { if (error1) { return; } // handle pool.connect((error2, connection) => { if (error2) { return; } // handle @@ -588,12 +692,11 @@ Utility function to execute a query on any open connection in the pool. Will get **Promises** ```javascript -const { Pool } = require('odbc'); +const odbc = require('odbc'); -// can only use await keywork in an async function +// can only use await keyword in an async function async function queryExample() { - const pool = new Pool(`${process.env.CONNECTION_STRING}`); - await pool.init(); + const pool = await odbc.pool(`${process.env.CONNECTION_STRING}`); const result = await pool.query('SELECT * FROM MY_TABLE'); console.log(result); } @@ -604,9 +707,8 @@ queryExample(); **Callbacks** ```javascript -const { Pool } = require('odbc'); -const pool = new Pool(`${process.env.CONNECTION_STRING}`); -pool.init((error1) => { +const odbc = require('odbc'); +odbc.pool(`${process.env.CONNECTION_STRING}`, (error1, pool) => { if (error1) { return; } // handle pool.query('SELECT * FROM MY_TABLE', (error2, result) => { if (error2) { return; } // handle @@ -630,12 +732,11 @@ Closes the entire pool of currently unused connections. Will not close connectio **Promises** ```javascript -const { Pool } = require('odbc'); +const odbc = require('odbc'); -// can only use await keywork in an async function +// can only use await keyword in an async function async function closeExample() { - const pool = new Pool(`${process.env.CONNECTION_STRING}`); - await pool.init(); + const pool = await odbc.pool(`${process.env.CONNECTION_STRING}`); await pool.close(); // pool is now closed } @@ -646,10 +747,11 @@ closeExample(); **Callbacks** ```javascript -const { Pool } = require('odbc'); -const pool = new Pool(`${process.env.CONNECTION_STRING}`); -pool.init((error1) => { +const odbc = require('odbc'); + +odbc.pool(`${process.env.CONNECTION_STRING}`, (error1, pool) => { if (error1) { return; } // handle + // do something with your pool here pool.close((error2) => { if (error2) { return; } // handle // pool is now closed @@ -662,7 +764,7 @@ pool.init((error1) => { ## **Statement** -A statement object is created from a Connection, and cannot be created _ad hoc_ with a constructor. +A Statement object is created from a Connection, and cannot be created _ad hoc_ with a constructor. Statements allow you to prepare a commonly used statement, then bind parameters to it multiple times, executing in between. @@ -682,11 +784,11 @@ Prepares an SQL statement, with or without parameters (?) to bind to. **Promises** ```javascript -const { Connection } = require('odbc'); -const connection = new Connection(`${process.env.CONNECTION_STRING}`); +const odbc = require('odbc'); -// can only use await keywork in an async function +// can only use await keyword in an async function async function prepareExample() { + const connection = await odbc.connect(`${process.env.CONNECTION_STRING}`); const statement = await connection.createStatement(); await statement.prepare('INSTERT INTO MY_TABLE VALUES(?, ?)'); // statement has been prepared, can bind and execute @@ -698,14 +800,15 @@ prepareExample(); **Callbacks** ```javascript -const { Connection } = require('odbc'); -const connection = new Connection(`${process.env.CONNECTION_STRING}`); - -connection.createStatement((error1, statement) => { - if (error1) { return; } // handle - statement.prepare('INSTERT INTO MY_TABLE VALUES(?, ?)' (error2) => { - if (error2) { return; } // handle - // statement has been prepared, can bind and execute +const odbc = require('odbc'); + +odbc.connect(`${process.env.CONNECTION_STRING}`, (error, connection) => { + connection.createStatement((error1, statement) => { + if (error1) { return; } // handle + statement.prepare('INSTERT INTO MY_TABLE VALUES(?, ?)' (error2) => { + if (error2) { return; } // handle + // statement has been prepared, can bind and execute + }); }); }); ``` @@ -726,11 +829,11 @@ Binds an array of values to the parameters on the prepared SQL statement. Cannot **Promises** ```javascript -const { Connection } = require('odbc'); -const connection = new Connection(`${process.env.CONNECTION_STRING}`); +const odbc = require('odbc'); -// can only use await keywork in an async function +// can only use await keyword in an async function async function bindExample() { + const connection = await odbc.connect(`${process.env.CONNECTION_STRING}`); const statement = await connection.createStatement(); await statement.prepare('INSTERT INTO MY_TABLE VALUES(?, ?)'); // Assuming MY_TABLE has INTEGER and VARCHAR fields. @@ -744,17 +847,18 @@ bindExample(); **Callbacks** ```javascript -const { Connection } = require('odbc'); -const connection = new Connection(`${process.env.CONNECTION_STRING}`); - -connection.createStatement((error1, statement) => { - if (error1) { return; } // handle - statement.prepare('INSERT INTO MY_TABLE VALUES(?, ?)' (error2) => { - if (error2) { return; } // handle - // Assuming MY_TABLE has INTEGER and VARCHAR fields. - statement.bind([1, 'Name'], (error3) => { - if (error3) { return; } // handle - // statement has been prepared and values bound, can now execute +const odbc = require('odbc'); + +odbc.connect(`${process.env.CONNECTION_STRING}`, (error, connection) => { + connection.createStatement((error1, statement) => { + if (error1) { return; } // handle + statement.prepare('INSERT INTO MY_TABLE VALUES(?, ?)' (error2) => { + if (error2) { return; } // handle + // Assuming MY_TABLE has INTEGER and VARCHAR fields. + statement.bind([1, 'Name'], (error3) => { + if (error3) { return; } // handle + // statement has been prepared and values bound, can now execute + }); }); }); }); @@ -776,11 +880,11 @@ Executes the prepared and optionally bound SQL statement. **Promises** ```javascript -const { Connection } = require('odbc'); -const connection = new Connection(`${process.env.CONNECTION_STRING}`); +const odbc = require('odbc'); -// can only use await keywork in an async function +// can only use await keyword in an async function async function executeExample() { + const connection = await odbc.connect(`${process.env.CONNECTION_STRING}`); const statement = await connection.createStatement(); await statement.prepare('INSTERT INTO MY_TABLE VALUES(?, ?)'); // Assuming MY_TABLE has INTEGER and VARCHAR fields. @@ -796,20 +900,21 @@ executeExample(); **Callbacks** ```javascript -const { Connection } = require('odbc'); -const connection = new Connection(`${process.env.CONNECTION_STRING}`); - -connection.createStatement((error1, statement) => { - if (error1) { return; } // handle - statement.prepare('INSTERT INTO MY_TABLE VALUES(?, ?)' (error2) => { - if (error2) { return; } // handle - // Assuming MY_TABLE has INTEGER and VARCHAR fields. - statement.bind([1, 'Name'], (error3) => { - if (error3) { return; } // handle - statement.execute((error4, result) => { - if (error4) { return; } // handle - console.log(result); - }) +const odbc = require('odbc'); + +odbc.connect(`${process.env.CONNECTION_STRING}`, (error, connection) => { + connection.createStatement((error1, statement) => { + if (error1) { return; } // handle + statement.prepare('INSTERT INTO MY_TABLE VALUES(?, ?)' (error2) => { + if (error2) { return; } // handle + // Assuming MY_TABLE has INTEGER and VARCHAR fields. + statement.bind([1, 'Name'], (error3) => { + if (error3) { return; } // handle + statement.execute((error4, result) => { + if (error4) { return; } // handle + console.log(result); + }) + }); }); }); }); @@ -830,11 +935,11 @@ Closes the Statement, freeing the statement handle. Running functions on the sta **Promises** ```javascript -const { Connection } = require('odbc'); -const connection = new Connection(`${process.env.CONNECTION_STRING}`); +const odbc = require('odbc'); -// can only use await keywork in an async function +// can only use await keyword in an async function async function executeExample() { + const connection = await odbc.connect(`${process.env.CONNECTION_STRING}`); const statement = await connection.createStatement(); await statement.prepare('INSTERT INTO MY_TABLE VALUES(?, ?)'); // Assuming MY_TABLE has INTEGER and VARCHAR fields. @@ -850,24 +955,25 @@ executeExample(); **Callbacks** ```javascript -const { Connection } = require('odbc'); -const connection = new Connection(`${process.env.CONNECTION_STRING}`); - -connection.createStatement((error1, statement) => { - if (error1) { return; } // handle - statement.prepare('INSTERT INTO MY_TABLE VALUES(?, ?)' (error2) => { - if (error2) { return; } // handle - // Assuming MY_TABLE has INTEGER and VARCHAR fields. - statement.bind([1, 'Name'], (error3) => { - if (error3) { return; } // handle - statement.execute((error4, result) => { - if (error4) { return; } // handle - console.log(result); - statement.close((error5) => { - if (error5) { return; } // handle - // statement closed successfully +const odbc = require('odbc'); + +odbc.connect(`${process.env.CONNECTION_STRING}`, (error, connection) => { + connection.createStatement((error1, statement) => { + if (error1) { return; } // handle + statement.prepare('INSTERT INTO MY_TABLE VALUES(?, ?)' (error2) => { + if (error2) { return; } // handle + // Assuming MY_TABLE has INTEGER and VARCHAR fields. + statement.bind([1, 'Name'], (error3) => { + if (error3) { return; } // handle + statement.execute((error4, result) => { + if (error4) { return; } // handle + console.log(result); + statement.close((error5) => { + if (error5) { return; } // handle + // statement closed successfully + }) }) - }) + }); }); }); }); @@ -903,4 +1009,4 @@ Permission is hereby granted, free of charge, to any person obtaining a copy of The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file +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/lib/Connection.js b/lib/Connection.js index 59930ec4..64cc8c11 100644 --- a/lib/Connection.js +++ b/lib/Connection.js @@ -1,4 +1,3 @@ -const odbc = require('../build/Release/odbc.node'); const { Statement } = require('./Statement'); class Connection { @@ -8,13 +7,8 @@ class Connection { * @param {string|object} connectionString - The connection string to connect using the DSN * defined in odbc.ini, or an odbcConnection object */ - constructor(connectionString, debug = false) { - if (typeof connectionString === 'string') { - this.odbcConnection = odbc.connectSync(connectionString); - } else { - this.odbcConnection = connectionString; - } - this.debug = debug; + constructor(odbcConnection) { + this.odbcConnection = odbcConnection; } get connected() { @@ -51,6 +45,11 @@ class Connection { } } + // if explicitly passing undefined into parameters, need to change to null + if (typeof parameters === 'undefined') { + parameters = null; + } + if (typeof sql !== 'string' || (parameters !== null && !Array.isArray(parameters)) || (typeof callback !== 'function' && typeof callback !== 'undefined')) { @@ -96,6 +95,11 @@ class Connection { } } + // if explicitly passing undefined into parameters, need to change to null + if (typeof parameters === 'undefined') { + parameters = null; + } + if (typeof name !== 'string' || (parameters !== null && !Array.isArray(parameters)) || (typeof callback !== 'function' && typeof callback !== 'undefined')) { diff --git a/lib/Pool.js b/lib/Pool.js index 26c13c93..07edaca8 100644 --- a/lib/Pool.js +++ b/lib/Pool.js @@ -1,87 +1,70 @@ +const async = require('async'); const odbc = require('../build/Release/odbc.node'); const { Connection } = require('./Connection'); -// TODO: Have to add options: -// increase Pool size or no -// max pool size? -// starting pool size -// increment size +const INITIAL_SIZE_DEFAULT = 10; +const INCREMENT_SIZE_DEFAULT = 10; +const MAX_SIZE_DEFAULT = null; +const SHRINK_DEFAULT = true; +const CONNECTION_TIMEOUT_DEFAULT = 0; +const LOGIN_TIMEOUT_DEFAULT = 0; + class Pool { - constructor(connectionString, initialSize = 10) { - this.isInitialized = false; + constructor(connectionString) { + this.isOpen = false; this.freeConnections = []; - if (typeof connectionString === 'object') { + // connectionString is a... + if (typeof connectionString === 'string') { + this.connectionString = connectionString; + this.initialSize = INITIAL_SIZE_DEFAULT; + this.incrementSize = INCREMENT_SIZE_DEFAULT; + this.maxSize = MAX_SIZE_DEFAULT; + this.shrink = SHRINK_DEFAULT; + this.connectionTimeout = CONNECTION_TIMEOUT_DEFAULT; + this.loginTimeout = LOGIN_TIMEOUT_DEFAULT; + } else if (typeof connectionString === 'object') { const configObject = connectionString; - this.connectionString = configObject.connectionString; - if (Object.prototype.hasOwnProperty.call(configObject, 'connectionString')) { - this.connectionString = configObject.connectionString; - } else { - throw new Error('Pool configuration object must contain "connectionString" key'); + if (!Object.prototype.hasOwnProperty.call(configObject, 'connectionString')) { + throw new TypeError('Pool configuration object must contain "connectionString" key'); } - - this.initialSize = configObject.initialSize || 10; - this.incrementSize = configObject.incrementSize || configObject.initialSize || 10; - this.connectionTimeout = configObject.connectionTimeout || 1000; - this.idleTimeout = configObject.idleTimeout || 1000; - this.maxSize = configObject.maxSize || 64; - this.shrink = configObject.shrink || true; + this.connectionString = configObject.connectionString; + this.initialSize = configObject.initialSize || INITIAL_SIZE_DEFAULT; + this.incrementSize = configObject.incrementSize || INCREMENT_SIZE_DEFAULT; + this.maxSize = configObject.maxSize || MAX_SIZE_DEFAULT; + this.shrink = configObject.shrink || SHRINK_DEFAULT; + this.connectionTimeout = configObject.connectionTimeout || CONNECTION_TIMEOUT_DEFAULT; + this.loginTimeout = configObject.loginTimeout || LOGIN_TIMEOUT_DEFAULT; } else { - this.connectionString = connectionString; - this.initialSize = initialSize; - this.incrementSize = initialSize; - this.maxSize = 100; - this.shrink = true; + throw TypeError('Pool constructor must passed a connection string or a configuration object'); } } - async init(callback = undefined) { - if (!this.isInitialized) { - // promise... - if (typeof callback === 'undefined') { - return new Promise(async (resolve, reject) => { - try { - await this.increasePoolSize(this.initialSize); - this.isInitialized = true; - resolve(null); - } catch (error) { - reject(new Error(error)); - } - }); - } - - // ...or callback - try { - await this.increasePoolSize(this.initialSize); - this.isInitialized = true; - } catch (error) { - return callback(error); - } - return callback(null); - } - - console.log('.init() was called, but the Pool was already initialized.'); - return undefined; - } - // TODO: Documentation // TODO: Does this need to be async? // returns a open connection, ready to use. // should overwrite the 'close' function of the connection, and rename it is 'nativeClose', so // that that close can still be called. async connect(callback = undefined) { - if (this.freeConnections.length < this.poolSize) { + if (this.freeConnections.length < 2) { await this.increasePoolSize(this.incrementSize); } - const connection = this.freeConnections.pop(); + let connection; + + if (this.freeConnections.length > 0) { + connection = this.freeConnections.pop(); + } else { + await this.increasePoolSize(this.incrementSize); + connection = this.freeConnections.pop(); + } connection.nativeClose = connection.close; connection.close = async (closeCallback = undefined) => { if (typeof closeCallback === 'undefined') { return new Promise((resolve, reject) => { - connection.close((error, result) => { + connection.nativeClose((error, result) => { if (error) { reject(new Error(error)); } else { @@ -89,22 +72,12 @@ class Pool { } }); - if (this.shrink === false || this.freeConnections.length < this.initialSize) { - this.increasePoolSize(1); - } + this.increasePoolSize(1); }); } connection.nativeClose(closeCallback); - - if (this.shrink === false || this.freeConnections.length < this.initialSize) { - try { - this.increasePoolSize(1); - } catch (error1) { - console.error(error1); - } - } - + this.increasePoolSize(1); return undefined; }; @@ -123,7 +96,7 @@ class Pool { return callback(null, connection); } - query(sql, params, cb) { + async query(sql, params, cb) { // determine the parameters passed let callback = cb; let parameters = params; @@ -137,18 +110,22 @@ class Pool { } // else parameters = params, check type in this.ODBCconnection.query } - const connection = this.freeConnections.pop(); + let connection; + + if (this.freeConnections.length > 0) { + connection = this.freeConnections.pop(); + } else { + await this.increasePoolSize(this.incrementSize); + connection = this.freeConnections.pop(); + } // promise... if (typeof callback !== 'function') { return new Promise((resolve, reject) => { connection.query(sql, parameters, (error, result) => { // after running, close the connection whether error or not - connection.close((closeError) => { - this.freeConnections.push(new Connection(this.connectionString)); - if (closeError) { - // TODO:: throw and error - } + connection.close(() => { + this.increasePoolSize(1); }); if (error) { @@ -163,66 +140,99 @@ class Pool { // ...or callback return connection.query(sql, parameters, (error, result) => { // after running, close the connection whether error or not - connection.close((closeError) => { - this.freeConnections.push(new Connection(this.connectionString)); - if (closeError) { - // TODO:: throw an error - } + connection.close(() => { + this.increasePoolSize(1); }); callback(error, result); }); } - // These are PRIVATE (although can't be made private in ES6... So don't use these)! + // close the pool and all of the connections + async close(callback = undefined) { + const connections = [...this.freeConnections]; + this.freeConnections.length = 0; + this.isOpen = false; - // odbc.connect runs on an AsyncWorker, so this is truly non-blocking - async increasePoolSize(count) { - return new Promise((resolve, reject) => { - Pool.generateConnections(this.connectionString, count, (error, connections) => { - if (error) { - reject(new Error(error)); - } else { - this.freeConnections = [...this.freeConnections, ...connections]; - resolve(); - } - }); - }); - } - - static async generateConnections(connectionString, count, callback) { - // promise... if (typeof callback === 'undefined') { return new Promise((resolve, reject) => { - odbc.connect(connectionString, count, (error, odbcConnections) => { + async.each(connections, (connection, cb) => { + connection.close((error) => { + cb(error); + }); + }, (error) => { if (error) { - reject(new Error(error)); + reject(error); } else { - const connections = Pool.wrapConnections(odbcConnections); - resolve(connections); + resolve(error); } }); }); } - // or callback - return odbc.connect(connectionString, count, (error, odbcConnections) => { - if (error) { - callback(error, null); - return undefined; + async.each(this.freeConnections, (connection, cb) => { + connection.close((error) => { + cb(error); + }); + }, error => callback(error)); + } + + async init(callback = undefined) { + if (!this.isOpen) { + this.isOpen = true; + // promise... + if (typeof callback === 'undefined') { + return new Promise(async (resolve, reject) => { + try { + await this.increasePoolSize(this.initialSize); + resolve(null); + } catch (error) { + reject(new Error(error)); + } + }); + } + + // ...or callback + try { + await this.increasePoolSize(this.initialSize); + } catch (error) { + return callback(error); } + return callback(null); + } - const connections = Pool.wrapConnections(odbcConnections); - callback(null, connections); - return undefined; - }); + console.log('.init() was called, but the Pool was already initialized.'); + return undefined; } - static wrapConnections(odbcConnections) { - const connectionsArray = []; - odbcConnections.forEach((odbcConnection) => { - connectionsArray.push(new Connection(odbcConnection)); + // odbc.connect runs on an AsyncWorker, so this is truly non-blocking + async increasePoolSize(count) { + const connectionConfig = { + connectionString: this.connectionString, + connectionTimeout: this.connectionTimeout, + loginTimeout: this.loginTimeout, + }; + return new Promise((resolve, reject) => { + const connectArray = []; + for (let i = 0; i < count; i += 1) { + connectArray.push((callback) => { + odbc.connect(connectionConfig, (error, connection) => { + if (!error) { + if (this.isOpen) { + this.freeConnections.push(new Connection(connection)); + } + } + callback(error, connection); + }); + }); + } + async.race(connectArray, (error) => { + if (error) { + reject(error); + } else { + resolve(); + } + }); }); - return connectionsArray; } } diff --git a/lib/odbc.js b/lib/odbc.js index 693193e7..03ac2c6a 100644 --- a/lib/odbc.js +++ b/lib/odbc.js @@ -1,7 +1,50 @@ +const nativeOdbc = require('../build/Release/odbc.node'); const { Connection } = require('./Connection'); const { Pool } = require('./Pool'); +function connect(connectionString, callback) { + if (typeof callback !== 'function') { + return new Promise((resolve, reject) => { + nativeOdbc.connect(connectionString, (error, odbcConnection) => { + if (error) { + reject(error); + } else { + const connection = new Connection(odbcConnection); + resolve(connection); + } + }); + }); + } + + return nativeOdbc.connect(connectionString, (error, odbcConnection) => { + if (!error) { + return callback(error, new Connection(odbcConnection)); + } + return callback(error, null); + }); +} + +function pool(options, callback) { + const poolObj = new Pool(options); + + if (typeof callback !== 'function') { + return new Promise((resolve, reject) => { + poolObj.init((error) => { + if (error) { + reject(error); + } else { + resolve(poolObj); + } + }); + }); + } + + return poolObj.init((error) => { + callback(error, poolObj); + }); +} + module.exports = { - Pool, - Connection, + pool, + connect, }; diff --git a/package-lock.json b/package-lock.json index b4f5d3a4..3c932c2e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "odbc", - "version": "2.0.0-beta.0", + "version": "2.0.0-beta.1", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -84,6 +84,11 @@ "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", "dev": true }, + "async": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/async/-/async-3.0.1.tgz", + "integrity": "sha512-ZswD8vwPtmBZzbn9xyi8XBQWXH3AvOQ43Za1KWYq7JeycrZuUYzx01KvHcVbXltjqH4y0MWrQ33008uLTqXuDw==" + }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", diff --git a/package.json b/package.json index 666a2e63..a2a7045e 100644 --- a/package.json +++ b/package.json @@ -1,15 +1,15 @@ { "name": "odbc", "description": "unixodbc bindings for node", - "version": "2.0.0-beta.0", - "homepage": "http://github.com/wankdanker/node-odbc/", + "version": "2.0.0", + "homepage": "http://github.com/markdirish/node-odbc/", "main": "./lib/odbc.js", "repository": { "type": "git", - "url": "git://github.com/wankdanker/node-odbc.git" + "url": "git://github.com/markdirish/node-odbc.git" }, "bugs": { - "url": "https://github.com/w1nk/node-odbc/issues" + "url": "https://github.com/markdirish/node-odbc/issues" }, "contributors": [ { @@ -36,11 +36,12 @@ "test": "mocha --slow 5000 --timeout 10000" }, "dependencies": { - "dotenv": "^6.2.0", "node-addon-api": "^1.3.0" }, "gypfile": true, "devDependencies": { + "async": "^3.0.1", + "dotenv": "^6.2.0", "eslint": "^5.13.0", "eslint-config-airbnb-base": "^13.1.0", "eslint-plugin-import": "^2.16.0", diff --git a/src/odbc.cpp b/src/odbc.cpp index ce2c0d7d..200bf52c 100644 --- a/src/odbc.cpp +++ b/src/odbc.cpp @@ -1,4 +1,5 @@ /* + Copyright (c) 2019, IBM Copyright (c) 2013, Dan VerWeire Copyright (c) 2010, Lee Smith @@ -56,48 +57,9 @@ Napi::Value ODBC::Init(Napi::Env env, Napi::Object exports) { ODBC_CONSTANTS.push_back(Napi::PropertyDescriptor::Value("SQL_NULLABLE", Napi::Number::New(env, SQL_NULLABLE), napi_enumerable)); ODBC_CONSTANTS.push_back(Napi::PropertyDescriptor::Value("SQL_NULLABLE_UNKNOWN", Napi::Number::New(env, SQL_NULLABLE_UNKNOWN), napi_enumerable)); - // ODBC_CONSTANTS.push_back(Napi::PropertyDescriptor::Value("SQL_USER_NAME", Napi::Number::New(env, SQL_USER_NAME), napi_enumerable)); - - // // connection attributes - // ODBC_CONSTANTS.push_back(Napi::PropertyDescriptor::Value("SQL_ACCESS_MODE", Napi::Number::New(env, SQL_ACCESS_MODE), napi_enumerable)); - // ODBC_CONSTANTS.push_back(Napi::PropertyDescriptor::Value("SQL_AUTOCOMMIT", Napi::Number::New(env, SQL_AUTOCOMMIT), napi_enumerable)); - // ODBC_CONSTANTS.push_back(Napi::PropertyDescriptor::Value("SQL_LOGIN_TIMEOUT", Napi::Number::New(env, SQL_LOGIN_TIMEOUT), napi_enumerable)); - // ODBC_CONSTANTS.push_back(Napi::PropertyDescriptor::Value("SQL_OPT_TRACE", Napi::Number::New(env, SQL_OPT_TRACE), napi_enumerable)); - // ODBC_CONSTANTS.push_back(Napi::PropertyDescriptor::Value("SQL_OPT_TRACEFILE", Napi::Number::New(env, SQL_OPT_TRACEFILE), napi_enumerable)); - // ODBC_CONSTANTS.push_back(Napi::PropertyDescriptor::Value("SQL_TRANSLATE_DLL", Napi::Number::New(env, SQL_TRANSLATE_DLL), napi_enumerable)); - // ODBC_CONSTANTS.push_back(Napi::PropertyDescriptor::Value("SQL_TRANSLATE_OPTION", Napi::Number::New(env, SQL_TRANSLATE_OPTION), napi_enumerable)); - // ODBC_CONSTANTS.push_back(Napi::PropertyDescriptor::Value("SQL_TXN_ISOLATION", Napi::Number::New(env, SQL_TXN_ISOLATION), napi_enumerable)); - // ODBC_CONSTANTS.push_back(Napi::PropertyDescriptor::Value("SQL_CURRENT_QUALIFIER", Napi::Number::New(env, SQL_CURRENT_QUALIFIER), napi_enumerable)); - // ODBC_CONSTANTS.push_back(Napi::PropertyDescriptor::Value("SQL_ODBC_CURSORS", Napi::Number::New(env, SQL_ODBC_CURSORS), napi_enumerable)); - // ODBC_CONSTANTS.push_back(Napi::PropertyDescriptor::Value("SQL_QUIET_MODE", Napi::Number::New(env, SQL_QUIET_MODE), napi_enumerable)); - // ODBC_CONSTANTS.push_back(Napi::PropertyDescriptor::Value("SQL_PACKET_SIZE", Napi::Number::New(env, SQL_PACKET_SIZE), napi_enumerable)); - - // // connection attributes with new names - // #if (ODBCVER >= 0x0300) - // ODBC_CONSTANTS.push_back(Napi::PropertyDescriptor::Value("SQL_ATTR_ACCESS_MODE", Napi::Number::New(env, SQL_ATTR_ACCESS_MODE), napi_enumerable)); - // ODBC_CONSTANTS.push_back(Napi::PropertyDescriptor::Value("SQL_ATTR_AUTOCOMMIT", Napi::Number::New(env, SQL_ATTR_AUTOCOMMIT), napi_enumerable)); - // ODBC_CONSTANTS.push_back(Napi::PropertyDescriptor::Value("SQL_ATTR_CONNECTION_TIMEOUT", Napi::Number::New(env, SQL_ATTR_CONNECTION_TIMEOUT), napi_enumerable)); - // ODBC_CONSTANTS.push_back(Napi::PropertyDescriptor::Value("SQL_ATTR_CURRENT_CATALOG", Napi::Number::New(env, SQL_ATTR_CURRENT_CATALOG), napi_enumerable)); - // ODBC_CONSTANTS.push_back(Napi::PropertyDescriptor::Value("SQL_ATTR_DISCONNECT_BEHAVIOR", Napi::Number::New(env, SQL_ATTR_DISCONNECT_BEHAVIOR), napi_enumerable)); - // ODBC_CONSTANTS.push_back(Napi::PropertyDescriptor::Value("SQL_ATTR_ENLIST_IN_DTC", Napi::Number::New(env, SQL_ATTR_ENLIST_IN_DTC), napi_enumerable)); - // ODBC_CONSTANTS.push_back(Napi::PropertyDescriptor::Value("SQL_ATTR_ENLIST_IN_XA", Napi::Number::New(env, SQL_ATTR_ENLIST_IN_XA), napi_enumerable)); - // ODBC_CONSTANTS.push_back(Napi::PropertyDescriptor::Value("SQL_ATTR_LOGIN_TIMEOUT", Napi::Number::New(env, SQL_ATTR_LOGIN_TIMEOUT), napi_enumerable)); - // ODBC_CONSTANTS.push_back(Napi::PropertyDescriptor::Value("SQL_ATTR_ODBC_CURSORS", Napi::Number::New(env, SQL_ATTR_ODBC_CURSORS), napi_enumerable)); - // ODBC_CONSTANTS.push_back(Napi::PropertyDescriptor::Value("SQL_ATTR_PACKET_SIZE", Napi::Number::New(env, SQL_ATTR_PACKET_SIZE), napi_enumerable)); - // ODBC_CONSTANTS.push_back(Napi::PropertyDescriptor::Value("SQL_ATTR_QUIET_MODE", Napi::Number::New(env, SQL_ATTR_QUIET_MODE), napi_enumerable)); - // ODBC_CONSTANTS.push_back(Napi::PropertyDescriptor::Value("SQL_ATTR_TRACE", Napi::Number::New(env, SQL_ATTR_TRACE), napi_enumerable)); - // ODBC_CONSTANTS.push_back(Napi::PropertyDescriptor::Value("SQL_ATTR_TRACEFILE", Napi::Number::New(env, SQL_ATTR_TRACEFILE), napi_enumerable)); - // ODBC_CONSTANTS.push_back(Napi::PropertyDescriptor::Value("SQL_ATTR_TRANSLATE_LIB", Napi::Number::New(env, SQL_ATTR_TRANSLATE_LIB), napi_enumerable)); - // ODBC_CONSTANTS.push_back(Napi::PropertyDescriptor::Value("SQL_ATTR_TRANSLATE_OPTION", Napi::Number::New(env, SQL_ATTR_TRANSLATE_OPTION), napi_enumerable)); - // ODBC_CONSTANTS.push_back(Napi::PropertyDescriptor::Value("SQL_ATTR_TXN_ISOLATION", Napi::Number::New(env, SQL_ATTR_TXN_ISOLATION), napi_enumerable)); - // #endif - - // ODBC_CONSTANTS.push_back(Napi::PropertyDescriptor::Value("SQL_ATTR_CONNECTION_DEAD", Napi::Number::New(env, SQL_ATTR_CONNECTION_DEAD), napi_enumerable)); - exports.DefineProperties(ODBC_CONSTANTS); exports.Set("connect", Napi::Function::New(env, ODBC::Connect)); - exports.Set("connectSync", Napi::Function::New(env, ODBC::ConnectSync)); // Initialize the cross platform mutex provided by libuv uv_mutex_init(&ODBC::g_odbcMutex); @@ -138,21 +100,24 @@ ODBC::~ODBC() { class ConnectAsyncWorker : public Napi::AsyncWorker { public: - ConnectAsyncWorker(HENV hEnv, SQLTCHAR *connectionStringPtr, int count, Napi::Function& callback) : Napi::AsyncWorker(callback), + ConnectAsyncWorker(HENV hEnv, SQLTCHAR *connectionStringPtr, unsigned int connectionTimeout, unsigned int loginTimeout, Napi::Function& callback) : Napi::AsyncWorker(callback), connectionStringPtr(connectionStringPtr), - count(count), + connectionTimeout(connectionTimeout), + loginTimeout(loginTimeout), hEnv(hEnv) {} - ~ConnectAsyncWorker() {} + ~ConnectAsyncWorker() { + delete[] connectionStringPtr; + } private: SQLTCHAR *connectionStringPtr; - int count; + unsigned int connectionTimeout; + unsigned int loginTimeout; SQLHENV hEnv; + SQLHDBC hDBC; - std::vector hDBCs; - SQLUSMALLINT canHaveMoreResults; SQLRETURN sqlReturnCode; void Execute() { @@ -161,75 +126,40 @@ class ConnectAsyncWorker : public Napi::AsyncWorker { uv_mutex_lock(&ODBC::g_odbcMutex); - // when we pool, want to create more than one connection in the AsyncWoker - for (int i = 0; i < count; i++) { - - SQLHDBC hDBC; - - sqlReturnCode = SQLAllocHandle(SQL_HANDLE_DBC, hEnv, &hDBC); - - unsigned int connectTimeout = 5; - unsigned int loginTimeout = 5; - - if (connectTimeout > 0) { - sqlReturnCode = SQLSetConnectAttr( - hDBC, // ConnectionHandle - SQL_ATTR_CONNECTION_TIMEOUT, // Attribute - (SQLPOINTER) size_t(connectTimeout), // ValuePtr - SQL_IS_UINTEGER); // StringLength - } - - if (loginTimeout > 0) { - sqlReturnCode = SQLSetConnectAttr( - hDBC, // ConnectionHandle - SQL_ATTR_LOGIN_TIMEOUT, // Attribute - (SQLPOINTER) size_t(loginTimeout), // ValuePtr - SQL_IS_UINTEGER); // StringLength - } - - //Attempt to connect - sqlReturnCode = SQLDriverConnect( - hDBC, // ConnectionHandle - NULL, // WindowHandle - connectionStringPtr, // InConnectionString - SQL_NTS, // StringLength1 - NULL, // OutConnectionString - 0, // BufferLength - in characters - NULL, // StringLength2Ptr - SQL_DRIVER_NOPROMPT); // DriverCompletion - - if (SQL_SUCCEEDED(sqlReturnCode)) { - - // odbcConnectionObject->connected = true; - - HSTMT hStmt; - - //allocate a temporary statment - sqlReturnCode = SQLAllocHandle(SQL_HANDLE_STMT, hDBC, &hStmt); - - //try to determine if the driver can handle - //multiple recordsets - sqlReturnCode = SQLGetFunctions( - hDBC, - SQL_API_SQLMORERESULTS, - &canHaveMoreResults - ); - - if (!SQL_SUCCEEDED(sqlReturnCode)) { - canHaveMoreResults = 0; - } - - //free the handle - sqlReturnCode = SQLFreeHandle(SQL_HANDLE_STMT, hStmt); - - hDBCs.push_back(hDBC); - - } else { - SetError(ODBC::GetSQLError(SQL_HANDLE_DBC, hDBC, (char *) "[node-odbc] Error in ConnectAsyncWorker")); - } + sqlReturnCode = SQLAllocHandle( + SQL_HANDLE_DBC, + hEnv, + &hDBC); + + if (connectionTimeout > 0) { + sqlReturnCode = SQLSetConnectAttr( + hDBC, // ConnectionHandle + SQL_ATTR_CONNECTION_TIMEOUT, // Attribute + (SQLPOINTER) size_t(connectionTimeout), // ValuePtr + SQL_IS_UINTEGER); // StringLength + } + + if (loginTimeout > 0) { + sqlReturnCode = SQLSetConnectAttr( + hDBC, // ConnectionHandle + SQL_ATTR_LOGIN_TIMEOUT, // Attribute + (SQLPOINTER) size_t(loginTimeout), // ValuePtr + SQL_IS_UINTEGER); // StringLength } + //Attempt to connect + sqlReturnCode = SQLDriverConnect( + hDBC, // ConnectionHandle + NULL, // WindowHandle + connectionStringPtr, // InConnectionString + SQL_NTS, // StringLength1 + NULL, // OutConnectionString + 0, // BufferLength - in characters + NULL, // StringLength2Ptr + SQL_DRIVER_NOPROMPT); // DriverCompletion + uv_mutex_unlock(&ODBC::g_odbcMutex); + ASYNC_WORKER_CHECK_CODE_SET_ERROR_RETURN(sqlReturnCode, SQL_HANDLE_DBC, hDBC, "ConnectAsyncWorker::Execute", "SQLDriverConnect"); } void OnOK() { @@ -239,25 +169,19 @@ class ConnectAsyncWorker : public Napi::AsyncWorker { Napi::Env env = Env(); Napi::HandleScope scope(env); - Napi::Array connections = Napi::Array::New(env); - - for (unsigned int i = 0; i < hDBCs.size(); i++) { - // pass the HENV and HDBC values to the ODBCConnection constructor - std::vector connectionArguments; - connectionArguments.push_back(Napi::External::New(env, &hEnv)); // connectionArguments[0] - connectionArguments.push_back(Napi::External::New(env, &hDBCs[i])); // connectionArguments[1] - - // create a new ODBCConnection object as a Napi::Value - connections.Set(i, ODBCConnection::constructor.New(connectionArguments)); - } + // pass the HENV and HDBC values to the ODBCConnection constructor + std::vector connectionArguments; + connectionArguments.push_back(Napi::External::New(env, &hEnv)); // connectionArguments[0] + connectionArguments.push_back(Napi::External::New(env, &hDBC)); // connectionArguments[1] + + Napi::Value connection = ODBCConnection::constructor.New(connectionArguments); // pass the arguments to the callback function std::vector callbackArguments; - callbackArguments.push_back(env.Null()); // callbackArguments[0] - callbackArguments.push_back(connections); // callbackArguments[1] + callbackArguments.push_back(env.Null()); // callbackArguments[0] + callbackArguments.push_back(connection); // callbackArguments[1] Callback().Call(callbackArguments); - } }; @@ -271,178 +195,52 @@ Napi::Value ODBC::Connect(const Napi::CallbackInfo& info) { Napi::String connectionString; Napi::Function callback; - int count; SQLTCHAR *connectionStringPtr = nullptr; + unsigned int connectionTimeout = 0; + unsigned int loginTimeout = 0; - if(info.Length() != 3) { - Napi::TypeError::New(env, "connect(connectionString, count, callback) requires 2 parameters.").ThrowAsJavaScriptException(); + if(info.Length() != 2) { + Napi::TypeError::New(env, "connect(connectionString, callback) requires 2 parameters.").ThrowAsJavaScriptException(); return env.Null(); } if (info[0].IsString()) { connectionString = info[0].As(); connectionStringPtr = ODBC::NapiStringToSQLTCHAR(connectionString); + } else if (info[0].IsObject()) { + Napi::Object connectionObject = info[0].As(); + if (connectionObject.Has("connectionString") && connectionObject.Get("connectionString").IsString()) { + connectionString = connectionObject.Get("connectionString").As(); + connectionStringPtr = ODBC::NapiStringToSQLTCHAR(connectionString); + } else { + Napi::TypeError::New(env, "connect: A configuration object must have a 'connectionString' property that is a string.").ThrowAsJavaScriptException(); + return env.Null(); + } + if (connectionObject.Has("connectionTimeout") && connectionObject.Get("connectionTimeout").IsNumber()) { + connectionTimeout = connectionObject.Get("connectionTimeout").As().Int32Value(); + } + if (connectionObject.Has("loginTimeout") && connectionObject.Get("loginTimeout").IsNumber()) { + loginTimeout = connectionObject.Get("loginTimeout").As().Int32Value(); + } } else { - Napi::TypeError::New(env, "connect: first parameter must be a string.").ThrowAsJavaScriptException(); - return env.Null(); - } - - if (info[1].IsNumber()) { - count = info[1].ToNumber().Int32Value(); - } else { - Napi::TypeError::New(env, "connect: second parameter must be a number.").ThrowAsJavaScriptException(); + Napi::TypeError::New(env, "connect: first parameter must be a string or an object.").ThrowAsJavaScriptException(); return env.Null(); } - if (info[2].IsFunction()) { - callback = info[2].As(); + if (info[1].IsFunction()) { + callback = info[1].As(); } else { - Napi::TypeError::New(env, "connect: third parameter must be a function.").ThrowAsJavaScriptException(); + Napi::TypeError::New(env, "connect: second parameter must be a function.").ThrowAsJavaScriptException(); return env.Null(); } - ConnectAsyncWorker *worker = new ConnectAsyncWorker(hEnv, connectionStringPtr, count, callback); + ConnectAsyncWorker *worker = new ConnectAsyncWorker(hEnv, connectionStringPtr, connectionTimeout, loginTimeout, callback); worker->Queue(); return env.Undefined(); } -Napi::Value ODBC::ConnectSync(const Napi::CallbackInfo& info) { - - DEBUG_PRINTF("ODBC::CreateConnection\n"); - - Napi::Env env = info.Env(); - Napi::HandleScope scope(env); - - Napi::Value error; - Napi::Value returnValue; - - SQLUSMALLINT canHaveMoreResults; - SQLRETURN sqlReturnCode; - SQLHDBC hDBC; - - if(info.Length() != 1) { - Napi::TypeError::New(env, "connectSync(connectionString) requires 1 parameter.").ThrowAsJavaScriptException(); - return env.Null(); - } - - if (!info[0].IsString()) { - Napi::TypeError::New(env, "connectSync: first parameter must be a string.").ThrowAsJavaScriptException(); - return env.Null(); - } - - Napi::String connectionString = info[0].As(); - SQLTCHAR *connectionStringPtr = ODBC::NapiStringToSQLTCHAR(connectionString); - - uv_mutex_lock(&ODBC::g_odbcMutex); - sqlReturnCode = SQLAllocHandle(SQL_HANDLE_DBC, hEnv, &hDBC); - - if (!SQL_SUCCEEDED(sqlReturnCode)) { - Napi::Error::New(env, ODBC::GetSQLError(SQL_HANDLE_DBC, hDBC)).ThrowAsJavaScriptException(); - uv_mutex_unlock(&ODBC::g_odbcMutex); - return env.Null(); - } - - unsigned int connectTimeout = 5; - unsigned int loginTimeout = 5; - - if (connectTimeout > 0) { - sqlReturnCode = SQLSetConnectAttr( - hDBC, // ConnectionHandle - SQL_ATTR_CONNECTION_TIMEOUT, // Attribute - (SQLPOINTER) size_t(connectTimeout), // ValuePtr - SQL_IS_UINTEGER); // StringLength - } - - if (!SQL_SUCCEEDED(sqlReturnCode)) { - Napi::Error::New(env, ODBC::GetSQLError(SQL_HANDLE_DBC, hDBC)).ThrowAsJavaScriptException(); - uv_mutex_unlock(&ODBC::g_odbcMutex); - return env.Null(); - } - - if (loginTimeout > 0) { - sqlReturnCode = SQLSetConnectAttr( - hDBC, // ConnectionHandle - SQL_ATTR_LOGIN_TIMEOUT, // Attribute - (SQLPOINTER) size_t(loginTimeout), // ValuePtr - SQL_IS_UINTEGER); // StringLength - } - - if (!SQL_SUCCEEDED(sqlReturnCode)) { - Napi::Error::New(env, ODBC::GetSQLError(SQL_HANDLE_DBC, hDBC)).ThrowAsJavaScriptException(); - uv_mutex_unlock(&ODBC::g_odbcMutex); - return env.Null(); - } - - //Attempt to connect - //NOTE: SQLDriverConnect requires the thread to be locked - sqlReturnCode = SQLDriverConnect( - hDBC, // ConnectionHandle - NULL, // WindowHandle - connectionStringPtr, // InConnectionString - SQL_NTS, // StringLength1 - NULL, // OutConnectionString - 0, // BufferLength - in characters - NULL, // StringLength2Ptr - SQL_DRIVER_NOPROMPT // DriverCompletion - ); - - - if (!SQL_SUCCEEDED(sqlReturnCode)) { - Napi::Error::New(env, ODBC::GetSQLError(SQL_HANDLE_DBC, hDBC)).ThrowAsJavaScriptException(); - uv_mutex_unlock(&ODBC::g_odbcMutex); - return env.Null(); - } - - HSTMT hStmt; - - //allocate a temporary statment - sqlReturnCode = SQLAllocHandle(SQL_HANDLE_STMT, hDBC, &hStmt); - - if (!SQL_SUCCEEDED(sqlReturnCode)) { - Napi::Error::New(env, ODBC::GetSQLError(SQL_HANDLE_ENV, hEnv)).ThrowAsJavaScriptException(); - uv_mutex_unlock(&ODBC::g_odbcMutex); - return env.Null(); - } - - //try to determine if the driver can handle - //multiple recordsets - sqlReturnCode = SQLGetFunctions( - hDBC, - SQL_API_SQLMORERESULTS, - &canHaveMoreResults); - - if (!SQL_SUCCEEDED(sqlReturnCode)) { - canHaveMoreResults = 0; - } - - //free the handle - sqlReturnCode = SQLFreeHandle(SQL_HANDLE_STMT, hStmt); - - if (!SQL_SUCCEEDED(sqlReturnCode)) { - Napi::Error::New(env, ODBC::GetSQLError(SQL_HANDLE_ENV, hEnv)).ThrowAsJavaScriptException(); - uv_mutex_unlock(&ODBC::g_odbcMutex); - return env.Null(); - } - - uv_mutex_unlock(&ODBC::g_odbcMutex); - - // return the SQLError - if (!SQL_SUCCEEDED(sqlReturnCode)) { - Napi::Error::New(env, ODBC::GetSQLError(SQL_HANDLE_ENV, hEnv)).ThrowAsJavaScriptException(); - return env.Null(); - } - - // return the Connection - // pass the HENV and HDBC values to the ODBCConnection constructor - std::vector connectionArguments; - connectionArguments.push_back(Napi::External::New(env, &hEnv)); // connectionArguments[0] - connectionArguments.push_back(Napi::External::New(env, &hDBC)); // connectionArguments[1] - // create a new ODBCConnection object as a Napi::Value - return ODBCConnection::constructor.New(connectionArguments); -} - /////////////////////////////////////////////////////////////////////////////// ///////////////////////////// UTILITY FUNCTIONS /////////////////////////////// /////////////////////////////////////////////////////////////////////////////// @@ -452,15 +250,19 @@ Napi::Value ODBC::ConnectSync(const Napi::CallbackInfo& info) { // no UNICODE : SQLCHAR* SQLTCHAR* ODBC::NapiStringToSQLTCHAR(Napi::String string) { + size_t byteCount = 0; + #ifdef UNICODE std::u16string tempString = string.Utf16Value(); + byteCount = (tempString.length() * 2) + 1; #else std::string tempString = string.Utf8Value(); + byteCount = tempString.length() + 1; #endif - std::vector *stringVector = new std::vector(tempString.begin(), tempString.end()); - stringVector->push_back('\0'); - return &(*stringVector)[0]; + SQLTCHAR *sqlString = new SQLTCHAR[tempString.length() + 1]; + std::memcpy(sqlString, tempString.c_str(), byteCount); + return sqlString; } // Encapsulates the workflow after a result set is returned (many paths require this workflow). @@ -524,8 +326,8 @@ SQLRETURN ODBC::BindColumns(QueryData *data) { } // create Columns for the column data to go into - data->columns = new Column*[data->columnCount]; - data->boundRow = new SQLTCHAR*[data->columnCount](); + data->columns = new Column*[data->columnCount](); + data->boundRow = new SQLCHAR*[data->columnCount](); for (int i = 0; i < data->columnCount; i++) { @@ -558,29 +360,25 @@ SQLRETURN ODBC::BindColumns(QueryData *data) { // bind depending on the column switch(column->DataType) { + case SQL_REAL: case SQL_DECIMAL : case SQL_NUMERIC : - - maxColumnLength = column->ColumnSize + column->DecimalDigits + 1; + maxColumnLength = (column->ColumnSize + 1) * sizeof(SQLCHAR); targetType = SQL_C_CHAR; - break; case SQL_DOUBLE : - maxColumnLength = column->ColumnSize; targetType = SQL_C_DOUBLE; break; case SQL_INTEGER: case SQL_SMALLINT: - maxColumnLength = column->ColumnSize; targetType = SQL_C_SLONG; break; case SQL_BIGINT : - maxColumnLength = column->ColumnSize; targetType = SQL_C_SBIGINT; break; @@ -588,7 +386,6 @@ SQLRETURN ODBC::BindColumns(QueryData *data) { case SQL_BINARY: case SQL_VARBINARY: case SQL_LONGVARBINARY: - maxColumnLength = column->ColumnSize; targetType = SQL_C_BINARY; break; @@ -596,8 +393,7 @@ SQLRETURN ODBC::BindColumns(QueryData *data) { case SQL_WCHAR: case SQL_WVARCHAR: case SQL_WLONGVARCHAR: - - maxColumnLength = (column->ColumnSize + 1) * sizeof(SQL_C_WCHAR); + maxColumnLength = (column->ColumnSize + 1) * sizeof(SQLWCHAR); targetType = SQL_C_WCHAR; break; @@ -605,8 +401,7 @@ SQLRETURN ODBC::BindColumns(QueryData *data) { case SQL_VARCHAR: case SQL_LONGVARCHAR: default: - - maxColumnLength = (column->ColumnSize + 1) * sizeof(SQL_C_CHAR); + maxColumnLength = (column->ColumnSize + 1) * sizeof(SQLCHAR); targetType = SQL_C_CHAR; break; } @@ -726,7 +521,6 @@ Napi::Array ODBC::ParametersToArray(Napi::Env env, QueryData *data) { // Node.js runtime. Napi::Array ODBC::ProcessDataForNapi(Napi::Env env, QueryData *data) { - std::vector *storedRows = &data->storedRows; Column **columns = data->columns; SQLSMALLINT columnCount = data->columnCount; @@ -765,7 +559,7 @@ Napi::Array ODBC::ProcessDataForNapi(Napi::Env env, QueryData *data) { // set the 'parameters' property Napi::Array params = ODBC::ParametersToArray(env, data); - rows.Set(Napi::String::New(env, PARAMETERS), ODBC::ParametersToArray(env, data)); + rows.Set(Napi::String::New(env, PARAMETERS), params); // set the 'return' property rows.Set(Napi::String::New(env, RETURN), env.Undefined()); // TODO: This doesn't exist on my DBMS of choice, need to test on MSSQL Server or similar @@ -785,11 +579,11 @@ Napi::Array ODBC::ProcessDataForNapi(Napi::Env env, QueryData *data) { rows.Set(Napi::String::New(env, COLUMNS), napiColumns); // iterate over all of the stored rows, - for (size_t i = 0; i < storedRows->size(); i++) { + for (size_t i = 0; i < data->storedRows.size(); i++) { Napi::Object row = Napi::Object::New(env); - ColumnData *storedRow = (*storedRows)[i]; + ColumnData *storedRow = data->storedRows[i]; // Iterate over each column, putting the data in the row object for (SQLSMALLINT j = 0; j < columnCount; j++) { @@ -805,18 +599,18 @@ Napi::Array ODBC::ProcessDataForNapi(Napi::Env env, QueryData *data) { switch(columns[j]->DataType) { case SQL_REAL: - case SQL_NUMERIC : + case SQL_DECIMAL: + case SQL_NUMERIC: value = Napi::Number::New(env, atof((const char*)storedRow[j].data)); break; // Napi::Number - case SQL_DECIMAL : - case SQL_FLOAT : - case SQL_DOUBLE : + case SQL_FLOAT: + case SQL_DOUBLE: value = Napi::Number::New(env, *(double*)storedRow[j].data); break; - case SQL_INTEGER : - case SQL_SMALLINT : - case SQL_BIGINT : + case SQL_INTEGER: + case SQL_SMALLINT: + case SQL_BIGINT: value = Napi::Number::New(env, *(int*)storedRow[j].data); break; // Napi::ArrayBuffer @@ -840,15 +634,11 @@ Napi::Array ODBC::ProcessDataForNapi(Napi::Env env, QueryData *data) { break; } } - row.Set(Napi::String::New(env, (const char*)columns[j]->ColumnName), value); - - delete storedRow[j].data; } rows.Set(i, row); } - storedRows->clear(); return rows; } @@ -861,7 +651,7 @@ Napi::Array ODBC::ProcessDataForNapi(Napi::Env env, QueryData *data) { * Array of parameters can hold either/and: * Value: * One value to bind, In/Out defaults to SQL_PARAM_INPUT, dataType defaults based on the value - * Arrays: + * Arrays:ns when you elect n * between 1 and 3 entries in lenth, with the following signfigance and default values: * 1. Value (REQUIRED): The value to bind * 2. In/Out (Optional): Defaults to SQL_PARAM_INPUT @@ -1007,7 +797,7 @@ std::string ODBC::GetSQLError(SQLSMALLINT handleType, SQLHANDLE handle, const ch SQLINTEGER native; SQLSMALLINT len; - SQLINTEGER statusRecCount; + SQLINTEGER statusRecCount = 0; SQLRETURN ret; char errorSQLState[14]; char errorMessage[ERROR_MESSAGE_BUFFER_BYTES]; diff --git a/src/odbc.h b/src/odbc.h index 3af875ff..37ed4ecb 100644 --- a/src/odbc.h +++ b/src/odbc.h @@ -1,4 +1,5 @@ /* + Copyright (c) 2019, IBM Copyright (c) 2013, Dan VerWeire Copyright (c) 2010, Lee Smith @@ -83,11 +84,11 @@ typedef struct Parameter { } Parameter; typedef struct ColumnData { - SQLTCHAR *data; + SQLCHAR *data; SQLLEN size; ~ColumnData() { - delete this->data; + delete[] this->data; } } ColumnData; @@ -105,7 +106,7 @@ typedef struct QueryData { // columns and rows Column **columns = NULL; SQLSMALLINT columnCount; - SQLTCHAR **boundRow = NULL; + SQLCHAR **boundRow = NULL; std::vector storedRows; SQLLEN rowCount; @@ -132,19 +133,24 @@ typedef struct QueryData { } } - storedRows.clear(); - delete columns; columns = NULL; delete boundRow; boundRow = NULL; delete sql; sql = NULL; } void clear() { - if (this->bindValueCount > 0 || this->parameterCount > 0) { + + for (size_t h = 0; h < this->storedRows.size(); h++) { + delete[] storedRows[h]; + }; + + int numParameters = std::max(this->bindValueCount, this->parameterCount); + + if (numParameters > 0) { Parameter* parameter; - for (int i = 0; i < this->bindValueCount; i++) { + for (int i = 0; i < numParameters; i++) { if (parameter = this->parameters[i], parameter->ParameterValuePtr != NULL) { switch (parameter->ValueType) { case SQL_C_SBIGINT: @@ -158,34 +164,36 @@ typedef struct QueryData { break; case SQL_C_TCHAR: default: - delete (SQLTCHAR*)parameter->ParameterValuePtr; + delete[] (SQLTCHAR*)parameter->ParameterValuePtr; break; } } parameter->ParameterValuePtr = NULL; + delete parameter; } - delete this->parameters; this->parameters = NULL; + delete[] this->parameters; this->parameters = NULL; this->bindValueCount = 0; this->parameterCount = 0; } if (this->columnCount > 0) { for (int i = 0; i < this->columnCount; i++) { - delete this->columns[i]->ColumnName; + delete[] this->boundRow[i]; + delete[] this->columns[i]->ColumnName; delete this->columns[i]; } } - delete columns; columns = NULL; - delete boundRow; boundRow = NULL; + delete[] columns; columns = NULL; + delete[] boundRow; boundRow = NULL; - delete this->sql; this->sql = NULL; - delete this->catalog; this->catalog = NULL; - delete this->schema; this->schema = NULL; - delete this->table; this->table = NULL; - delete this->type; this->type = NULL; - delete this->column; this->column = NULL; + delete[] this->sql; this->sql = NULL; + delete[] this->catalog; this->catalog = NULL; + delete[] this->schema; this->schema = NULL; + delete[] this->table; this->table = NULL; + delete[] this->type; this->type = NULL; + delete[] this->column; this->column = NULL; } } QueryData; @@ -219,8 +227,6 @@ class ODBC { ~ODBC(); static Napi::Value Connect(const Napi::CallbackInfo& info); - static Napi::Value ConnectSync(const Napi::CallbackInfo& info); - static Napi::Value ConnectMany(const Napi::CallbackInfo& info); #ifdef dynodbc static Napi::Value LoadODBCLibrary(const Napi::CallbackInfo& info); diff --git a/src/odbc_connection.cpp b/src/odbc_connection.cpp index 70d4c5ed..cc9bc1af 100644 --- a/src/odbc_connection.cpp +++ b/src/odbc_connection.cpp @@ -1,4 +1,5 @@ /* + Copyright (c) 2019, IBM Copyright (c) 2013, Dan VerWeire Copyright (c) 2010, Lee Smith @@ -103,14 +104,12 @@ SQLRETURN ODBCConnection::Free() { if (this->hDBC) { returnCode = SQLDisconnect(this->hDBC); if (!SQL_SUCCEEDED(returnCode)) { - printf("\nSQLDisconnect"); uv_mutex_unlock(&ODBC::g_odbcMutex); return returnCode; } returnCode = SQLFreeHandle(SQL_HANDLE_DBC, this->hDBC); if (!SQL_SUCCEEDED(returnCode)) { - printf("\nSQLFreeHandle"); uv_mutex_unlock(&ODBC::g_odbcMutex); return returnCode; } @@ -126,7 +125,7 @@ Napi::Value ODBCConnection::ConnectedGetter(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); Napi::HandleScope scope(env); - SQLINTEGER connection; + SQLINTEGER connection = SQL_CD_TRUE; SQLGetConnectAttr( this->hDBC, // ConnectionHandle @@ -136,9 +135,7 @@ Napi::Value ODBCConnection::ConnectedGetter(const Napi::CallbackInfo& info) { NULL // StringLengthPtr ); - if (connection == SQL_CD_TRUE) { - return Napi::Boolean::New(env, false); - } else if (connection == SQL_CD_FALSE) { + if (connection == SQL_CD_FALSE) { // connection dead is false return Napi::Boolean::New(env, true); } @@ -258,10 +255,6 @@ Napi::Value ODBCConnection::Close(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); Napi::HandleScope scope(env); - if (!info[0].IsFunction()) { - - } - Napi::Function callback = info[0].As(); CloseAsyncWorker *worker = new CloseAsyncWorker(this, callback); @@ -385,9 +378,9 @@ class QueryAsyncWorker : public Napi::AsyncWorker { DEBUG_PRINTF("\nODBCConnection::QueryAsyncWorke::Execute"); - // DEBUG_PRINTF("ODBCConnection::Query : sqlLen=%i, sqlSize=%i, sql=%s\n", - // data->sqlLen, data->sqlSize, (char*)data->sql); - + DEBUG_PRINTF("ODBCConnection::Query :sql=%s\n", + (char*)data->sql); + // allocate a new statement handle uv_mutex_lock(&ODBC::g_odbcMutex); data->sqlReturnCode = SQLAllocHandle( @@ -468,7 +461,11 @@ class QueryAsyncWorker : public Napi::AsyncWorker { data(data) {} ~QueryAsyncWorker() { - //delete data; + uv_mutex_lock(&ODBC::g_odbcMutex); + this->data->sqlReturnCode = SQLFreeHandle(SQL_HANDLE_STMT, this->data->hSTMT); + this->data->hSTMT = SQL_NULL_HANDLE; + uv_mutex_unlock(&ODBC::g_odbcMutex); + delete data; } }; @@ -918,8 +915,8 @@ class TablesAsyncWorker : public Napi::AsyncWorker { void OnOK() { - DEBUG_PRINTF("ODBCConnection::QueryAsyncWorker::OnOk : data->sqlReturnCode=%i, \n", data->sqlReturnCode ); - + DEBUG_PRINTF("ODBCConnection::QueryAsyncWorker::OnOk : data->sqlReturnCode=%i\n", data->sqlReturnCode); + Napi::Env env = Env(); Napi::HandleScope scope(env); @@ -1039,16 +1036,6 @@ Napi::Value ODBCConnection::Tables(const Napi::CallbackInfo& info) { // ColumnsAsyncWorker, used by Columns function (see below) class ColumnsAsyncWorker : public Napi::AsyncWorker { - public: - - ColumnsAsyncWorker(ODBCConnection *odbcConnectionObject, QueryData *data, Napi::Function& callback) : Napi::AsyncWorker(callback), - odbcConnectionObject(odbcConnectionObject), - data(data) {} - - ~ColumnsAsyncWorker() { - delete data; - } - private: ODBCConnection *odbcConnectionObject; @@ -1094,6 +1081,16 @@ class ColumnsAsyncWorker : public Napi::AsyncWorker { callbackArguments.push_back(rows); Callback().Call(callbackArguments); } + + public: + + ColumnsAsyncWorker(ODBCConnection *odbcConnectionObject, QueryData *data, Napi::Function& callback) : Napi::AsyncWorker(callback), + odbcConnectionObject(odbcConnectionObject), + data(data) {} + + ~ColumnsAsyncWorker() { + delete data; + } }; /* diff --git a/src/odbc_connection.h b/src/odbc_connection.h index e06d70ba..117fbb58 100644 --- a/src/odbc_connection.h +++ b/src/odbc_connection.h @@ -1,4 +1,5 @@ /* + Copyright (c) 2019, IBM Copyright (c) 2013, Dan VerWeire Copyright (c) 2010, Lee Smith @@ -19,7 +20,6 @@ #define _SRC_ODBC_CONNECTION_H #include -// #include class ODBCConnection : public Napi::ObjectWrap { diff --git a/src/odbc_statement.cpp b/src/odbc_statement.cpp index 61317a23..de1364de 100644 --- a/src/odbc_statement.cpp +++ b/src/odbc_statement.cpp @@ -1,4 +1,5 @@ /* + Copyright (c) 2019, IBM Copyright (c) 2013, Dan VerWeire Permission to use, copy, modify, and/or distribute this software for any @@ -65,7 +66,7 @@ ODBCStatement::~ODBCStatement() { SQLRETURN ODBCStatement::Free() { DEBUG_PRINTF("ODBCStatement::Free\n"); - if (this->data && this->data->hSTMT) { + if (this->data && this->data->hSTMT && this->data->hSTMT != SQL_NULL_HANDLE) { uv_mutex_lock(&ODBC::g_odbcMutex); this->data->sqlReturnCode = SQLFreeHandle(SQL_HANDLE_STMT, this->data->hSTMT); this->data->hSTMT = SQL_NULL_HANDLE; @@ -96,9 +97,7 @@ class PrepareAsyncWorker : public Napi::AsyncWorker { ~PrepareAsyncWorker() {} void Execute() { - DEBUG_PRINTF("ODBCStatement::PrepareAsyncWorker in Execute()\n"); - DEBUG_PRINTF("ODBCStatement::PrepareAsyncWorker hDBC=%p hDBC=%p hSTMT=%p\n", odbcStatementObject->hENV, odbcStatementObject->hDBC, diff --git a/src/odbc_statement.h b/src/odbc_statement.h index 56b05a8a..e35de812 100644 --- a/src/odbc_statement.h +++ b/src/odbc_statement.h @@ -1,4 +1,5 @@ /* + Copyright (c) 2019, IBM Copyright (c) 2013, Dan VerWeire Permission to use, copy, modify, and/or distribute this software for any @@ -18,7 +19,6 @@ #define _SRC_ODBC_STATEMENT_H #include -// #include class ODBCStatement : public Napi::ObjectWrap { public: diff --git a/test/connection/beginTransaction.js b/test/connection/beginTransaction.js index 9c076bef..f27624a3 100644 --- a/test/connection/beginTransaction.js +++ b/test/connection/beginTransaction.js @@ -2,13 +2,13 @@ require('dotenv').config(); const assert = require('assert'); -const { Connection } = require('../../'); +const odbc = require('../../'); describe('.beginTransaction([callback])...', () => { let connection = null; - beforeEach(() => { - connection = new Connection(`${process.env.CONNECTION_STRING}`); + beforeEach(async () => { + connection = await odbc.connect(`${process.env.CONNECTION_STRING}`); }); afterEach(async () => { @@ -104,38 +104,44 @@ describe('.beginTransaction([callback])...', () => { }); }); it('...should make transactional queries visible only to the connection that the transaction was started on until commit() is called (at transactional isolation level \'read committed\').', (done) => { - const connection1 = new Connection(`${process.env.CONNECTION_STRING};CMT=1`); // set commitment level to 1 (Read committed) - const connection2 = new Connection(`${process.env.CONNECTION_STRING};CMT=1;CONCURRENTACCESSRESOLUTION=1`); // set commitment level to 1 (Read committed) - connection1.beginTransaction((error0) => { - assert.deepEqual(error0, null); - connection1.query(`INSERT INTO ${process.env.DB_SCHEMA}.${process.env.DB_TABLE} VALUES(1, 'committed', 10)`, (error2, result2) => { + // set commitment level to 1 (Read committed) + odbc.connect(`${process.env.CONNECTION_STRING};CMT=1`, (error1, connection1) => { + assert.deepEqual(error1, null); + // set commitment level to 1 (Read committed) + odbc.connect(`${process.env.CONNECTION_STRING};CMT=1;CONCURRENTACCESSRESOLUTION=1`, (error2, connection2) => { assert.deepEqual(error2, null); - assert.notDeepEqual(result2, null); - assert.deepEqual(result2.count, 1); - connection1.query(`SELECT * FROM ${process.env.DB_SCHEMA}.${process.env.DB_TABLE} FETCH1`, (error3, result3) => { + connection1.beginTransaction((error3) => { assert.deepEqual(error3, null); - assert.notDeepEqual(result3, null); - assert.deepEqual(result3.length, 1); - assert.deepEqual(result3[0], { ID: 1, NAME: 'committed', AGE: 10 }); - assert.deepEqual(result3.count, -1); - connection2.query(`SELECT * FROM ${process.env.DB_SCHEMA}.${process.env.DB_TABLE} FETCH2`, (error4, result4) => { + connection1.query(`INSERT INTO ${process.env.DB_SCHEMA}.${process.env.DB_TABLE} VALUES(1, 'committed', 10)`, (error4, result4) => { assert.deepEqual(error4, null); assert.notDeepEqual(result4, null); - assert.deepEqual(result4.length, 0); - assert.deepEqual(result4.count, -1); - connection1.commit((error5) => { + assert.deepEqual(result4.count, 1); + connection1.query(`SELECT * FROM ${process.env.DB_SCHEMA}.${process.env.DB_TABLE} FETCH1`, (error5, result5) => { assert.deepEqual(error5, null); - connection2.query(`SELECT * FROM ${process.env.DB_SCHEMA}.${process.env.DB_TABLE}`, (error6, result6) => { + assert.notDeepEqual(result5, null); + assert.deepEqual(result5.length, 1); + assert.deepEqual(result5[0], { ID: 1, NAME: 'committed', AGE: 10 }); + assert.deepEqual(result5.count, -1); + connection2.query(`SELECT * FROM ${process.env.DB_SCHEMA}.${process.env.DB_TABLE} FETCH2`, (error6, result6) => { assert.deepEqual(error6, null); assert.notDeepEqual(result6, null); - assert.deepEqual(result6.length, 1); + assert.deepEqual(result6.length, 0); assert.deepEqual(result6.count, -1); - assert.deepEqual(result6[0], { ID: 1, NAME: 'committed', AGE: 10 }); - connection1.close((error7) => { + connection1.commit((error7) => { assert.deepEqual(error7, null); - connection2.close((error8) => { + connection2.query(`SELECT * FROM ${process.env.DB_SCHEMA}.${process.env.DB_TABLE}`, (error8, result8) => { assert.deepEqual(error8, null); - done(); + assert.notDeepEqual(result8, null); + assert.deepEqual(result8.length, 1); + assert.deepEqual(result8.count, -1); + assert.deepEqual(result8[0], { ID: 1, NAME: 'committed', AGE: 10 }); + connection1.close((error9) => { + assert.deepEqual(error9, null); + connection2.close((error10) => { + assert.deepEqual(error10, null); + done(); + }); + }); }); }); }); @@ -146,25 +152,29 @@ describe('.beginTransaction([callback])...', () => { }); }); it('...shouldn\'t hide queries that occur on other connections.', (done) => { - const connection1 = new Connection(`${process.env.CONNECTION_STRING}`); - const connection2 = new Connection(`${process.env.CONNECTION_STRING}`); - connection1.beginTransaction((error1) => { + odbc.connect(`${process.env.CONNECTION_STRING}`, (error1, connection1) => { assert.deepEqual(error1, null); - connection2.query(`INSERT INTO ${process.env.DB_SCHEMA}.${process.env.DB_TABLE} VALUES(1, 'committed', 10)`, (error2, result2) => { + odbc.connect(`${process.env.CONNECTION_STRING}`, (error2, connection2) => { assert.deepEqual(error2, null); - assert.notDeepEqual(result2, null); - assert.deepEqual(result2.count, 1); - connection1.query(`SELECT * FROM ${process.env.DB_SCHEMA}.${process.env.DB_TABLE}`, (error3, result3) => { + connection1.beginTransaction((error3) => { assert.deepEqual(error3, null); - assert.notDeepEqual(result3, null); - assert.deepEqual(result3.length, 1); - assert.deepEqual(result3[0], { ID: 1, NAME: 'committed', AGE: 10 }); - assert.deepEqual(result3.count, -1); - connection1.close((error4) => { + connection2.query(`INSERT INTO ${process.env.DB_SCHEMA}.${process.env.DB_TABLE} VALUES(1, 'committed', 10)`, (error4, result4) => { assert.deepEqual(error4, null); - connection2.close((error5) => { + assert.notDeepEqual(result4, null); + assert.deepEqual(result4.count, 1); + connection1.query(`SELECT * FROM ${process.env.DB_SCHEMA}.${process.env.DB_TABLE}`, (error5, result5) => { assert.deepEqual(error5, null); - done(); + assert.notDeepEqual(result5, null); + assert.deepEqual(result5.length, 1); + assert.deepEqual(result5[0], { ID: 1, NAME: 'committed', AGE: 10 }); + assert.deepEqual(result5.count, -1); + connection1.close((error6) => { + assert.deepEqual(error6, null); + connection2.close((error7) => { + assert.deepEqual(error7, null); + done(); + }); + }); }); }); }); diff --git a/test/connection/callProcedure.js b/test/connection/callProcedure.js index 06b6380e..faba107c 100644 --- a/test/connection/callProcedure.js +++ b/test/connection/callProcedure.js @@ -2,17 +2,19 @@ require('dotenv').config(); const assert = require('assert'); -const { Connection } = require('../../'); +const odbc = require('../../'); -describe('.callProcedure(procedureName, parameters, [callback])...', () => { +describe.skip('.callProcedure(procedureName, parameters, [callback])...', () => { describe('...with callbacks...', () => { - it('...should place correct result in and out parameter.', (done) => { + it('...should place correct result in an out parameter.', (done) => { const array = [undefined]; - const connection = new Connection(`${process.env.CONNECTION_STRING}`); - connection.callProcedure(null, `${process.env.DB_SCHEMA}`, `${process.env.DB_STOREDPROCEDURE}`, array, (error1, result1) => { + odbc.connect(`${process.env.CONNECTION_STRING}`, (error1, connection) => { assert.deepEqual(error1, null); - assert.notDeepEqual(result1, null); - done(); + connection.callProcedure(null, `${process.env.DB_SCHEMA}`, `${process.env.DB_STOREDPROCEDURE}`, array, (error2, result2) => { + assert.deepEqual(error2, null); + assert.notDeepEqual(result2, null); + done(); + }); }); }); }); diff --git a/test/connection/close.js b/test/connection/close.js index 101624d1..0b44b2d0 100644 --- a/test/connection/close.js +++ b/test/connection/close.js @@ -2,7 +2,7 @@ require('dotenv').config(); const assert = require('assert'); -const { Connection } = require('../../'); +const odbc = require('../../'); describe('.close([callback])...', () => { it('...should throw a TypeError if function signature doesn\'t match accepted signatures.', async () => { @@ -13,7 +13,7 @@ describe('.close([callback])...', () => { const CLOSE_CALLBACK = () => {}; - const connection = new Connection(`${process.env.CONNECTION_STRING}`); + const connection = await odbc.connect(`${process.env.CONNECTION_STRING}`); assert.throws(() => { connection.close(1, []); }, CLOSE_TYPE_ERROR); @@ -39,36 +39,39 @@ describe('.close([callback])...', () => { }); describe('...with callbacks...', () => { it('...should set .connected property to false.', (done) => { - const connection = new Connection(`${process.env.CONNECTION_STRING}`); - assert.deepEqual(connection.connected, true); - connection.close((error1) => { - assert.deepEqual(error1, null); - assert.deepEqual(connection.connected, false); - done(); + odbc.connect(`${process.env.CONNECTION_STRING}`, (error1, connection) => { + assert.deepEqual(connection.connected, true); + connection.close((error2) => { + assert.deepEqual(error2, null); + assert.deepEqual(connection.connected, false); + done(); + }); }); }); it('...shouldn\'t allow queries after close() is called.', (done) => { - const connection = new Connection(`${process.env.CONNECTION_STRING}`); - connection.close((error1) => { - assert.deepEqual(error1, null); - connection.query(`SELECT * FROM ${process.env.DB_SCHEMA}.${process.env.DB_TABLE}`, (error2, result2) => { - assert.notDeepEqual(error2, null); - assert.deepEqual(error2 instanceof Error, true); - assert.deepEqual(result2); - done(); + odbc.connect(`${process.env.CONNECTION_STRING}`, (error1, connection) => { + assert.deepEqual(error1); + connection.close((error2) => { + assert.deepEqual(error2, null); + connection.query(`SELECT * FROM ${process.env.DB_SCHEMA}.${process.env.DB_TABLE}`, (error3, result3) => { + assert.notDeepEqual(error3, null); + assert.deepEqual(error3 instanceof Error, true); + assert.deepEqual(result3); + done(); + }); }); }); }); }); // ...with callbacks... describe('...with promises...', () => { it('...should set .connected property to false.', async () => { - const connection = new Connection(`${process.env.CONNECTION_STRING}`); + const connection = await odbc.connect(`${process.env.CONNECTION_STRING}`); assert.deepEqual(connection.connected, true); await connection.close(); assert.deepEqual(connection.connected, false); }); it('...shouldn\'t allow queries after close() is called.', async () => { - const connection = new Connection(`${process.env.CONNECTION_STRING}`); + const connection = await odbc.connect(`${process.env.CONNECTION_STRING}`); await connection.close(); assert.rejects(async () => { await connection.query(`SELECT * FROM ${process.env.DB_SCHEMA}.${process.env.DB_TABLE}`); diff --git a/test/connection/columns.js b/test/connection/columns.js index 1e20f3a9..8b0b2328 100644 --- a/test/connection/columns.js +++ b/test/connection/columns.js @@ -2,13 +2,13 @@ require('dotenv').config(); const assert = require('assert'); -const odbc = require('../../build/Release/odbc.node'); -const { Connection } = require('../../'); +const odbc = require('../../'); +const nativeOdbc = require('../../build/Release/odbc.node'); describe('.columns(catalog, schema, table, column, callback)...', () => { let connection = null; - beforeEach(() => { - connection = new Connection(`${process.env.CONNECTION_STRING}`); + beforeEach(async () => { + connection = await odbc.connect(`${process.env.CONNECTION_STRING}`); }); afterEach(async () => { @@ -17,75 +17,73 @@ describe('.columns(catalog, schema, table, column, callback)...', () => { }); describe('...with callbacks...', () => { it('...should return information about all columns of a table.', (done) => { - // const connection = new Connection(`${process.env.CONNECTION_STRING}`); connection.columns(null, `${process.env.DB_SCHEMA}`, `${process.env.DB_TABLE}`, null, (error, results) => { assert.strictEqual(error, null); assert.strictEqual(results.length, 3); assert.strictEqual(results.count, 3); assert.deepStrictEqual(results.columns, [ - { name: 'TABLE_CAT', dataType: odbc.SQL_VARCHAR }, - { name: 'TABLE_SCHEM', dataType: odbc.SQL_VARCHAR }, - { name: 'TABLE_NAME', dataType: odbc.SQL_VARCHAR }, - { name: 'COLUMN_NAME', dataType: odbc.SQL_VARCHAR }, - { name: 'DATA_TYPE', dataType: odbc.SQL_SMALLINT }, - { name: 'TYPE_NAME', dataType: odbc.SQL_VARCHAR }, - { name: 'COLUMN_SIZE', dataType: odbc.SQL_INTEGER }, - { name: 'BUFFER_LENGTH', dataType: odbc.SQL_INTEGER }, - { name: 'DECIMAL_DIGITS', dataType: odbc.SQL_SMALLINT }, - { name: 'NUM_PREC_RADIX', dataType: odbc.SQL_SMALLINT }, - { name: 'NULLABLE', dataType: odbc.SQL_SMALLINT }, - { name: 'REMARKS', dataType: odbc.SQL_VARCHAR }, - { name: 'COLUMN_DEF', dataType: odbc.SQL_VARCHAR }, - { name: 'SQL_DATA_TYPE', dataType: odbc.SQL_SMALLINT }, - { name: 'SQL_DATETIME_SUB', dataType: odbc.SQL_SMALLINT }, - { name: 'CHAR_OCTET_LENGTH', dataType: odbc.SQL_INTEGER }, - { name: 'ORDINAL_POSITION', dataType: odbc.SQL_INTEGER }, - { name: 'IS_NULLABLE', dataType: odbc.SQL_VARCHAR }, + { name: 'TABLE_CAT', dataType: nativeOdbc.SQL_VARCHAR }, + { name: 'TABLE_SCHEM', dataType: nativeOdbc.SQL_VARCHAR }, + { name: 'TABLE_NAME', dataType: nativeOdbc.SQL_VARCHAR }, + { name: 'COLUMN_NAME', dataType: nativeOdbc.SQL_VARCHAR }, + { name: 'DATA_TYPE', dataType: nativeOdbc.SQL_SMALLINT }, + { name: 'TYPE_NAME', dataType: nativeOdbc.SQL_VARCHAR }, + { name: 'COLUMN_SIZE', dataType: nativeOdbc.SQL_INTEGER }, + { name: 'BUFFER_LENGTH', dataType: nativeOdbc.SQL_INTEGER }, + { name: 'DECIMAL_DIGITS', dataType: nativeOdbc.SQL_SMALLINT }, + { name: 'NUM_PREC_RADIX', dataType: nativeOdbc.SQL_SMALLINT }, + { name: 'NULLABLE', dataType: nativeOdbc.SQL_SMALLINT }, + { name: 'REMARKS', dataType: nativeOdbc.SQL_VARCHAR }, + { name: 'COLUMN_DEF', dataType: nativeOdbc.SQL_VARCHAR }, + { name: 'SQL_DATA_TYPE', dataType: nativeOdbc.SQL_SMALLINT }, + { name: 'SQL_DATETIME_SUB', dataType: nativeOdbc.SQL_SMALLINT }, + { name: 'CHAR_OCTET_LENGTH', dataType: nativeOdbc.SQL_INTEGER }, + { name: 'ORDINAL_POSITION', dataType: nativeOdbc.SQL_INTEGER }, + { name: 'IS_NULLABLE', dataType: nativeOdbc.SQL_VARCHAR }, ]); const idColumn = results[0]; assert.deepEqual(idColumn.COLUMN_NAME, 'ID'); - assert.deepEqual(idColumn.DATA_TYPE, odbc.SQL_INTEGER); - assert.deepEqual(idColumn.NULLABLE, odbc.SQL_NULLABLE); + assert.deepEqual(idColumn.DATA_TYPE, nativeOdbc.SQL_INTEGER); + assert.deepEqual(idColumn.NULLABLE, nativeOdbc.SQL_NULLABLE); const nameColumn = results[1]; assert.deepEqual(nameColumn.COLUMN_NAME, 'NAME'); - assert.deepEqual(nameColumn.DATA_TYPE, odbc.SQL_VARCHAR); - assert.deepEqual(nameColumn.NULLABLE, odbc.SQL_NULLABLE); + assert.deepEqual(nameColumn.DATA_TYPE, nativeOdbc.SQL_VARCHAR); + assert.deepEqual(nameColumn.NULLABLE, nativeOdbc.SQL_NULLABLE); const ageColumn = results[2]; assert.deepEqual(ageColumn.COLUMN_NAME, 'AGE'); - assert.deepEqual(ageColumn.DATA_TYPE, odbc.SQL_INTEGER); - assert.deepEqual(ageColumn.NULLABLE, odbc.SQL_NULLABLE); + assert.deepEqual(ageColumn.DATA_TYPE, nativeOdbc.SQL_INTEGER); + assert.deepEqual(ageColumn.NULLABLE, nativeOdbc.SQL_NULLABLE); done(); }); }); it('...should return empty with bad parameters.', (done) => { - // const connection = new Connection(`${process.env.CONNECTION_STRING}`); connection.columns(null, 'bad schema name', 'bad table name', null, (error, results) => { assert.strictEqual(error, null); assert.strictEqual(results.length, 0); assert.strictEqual(results.count, 0); assert.deepStrictEqual(results.columns, [ - { name: 'TABLE_CAT', dataType: odbc.SQL_VARCHAR }, - { name: 'TABLE_SCHEM', dataType: odbc.SQL_VARCHAR }, - { name: 'TABLE_NAME', dataType: odbc.SQL_VARCHAR }, - { name: 'COLUMN_NAME', dataType: odbc.SQL_VARCHAR }, - { name: 'DATA_TYPE', dataType: odbc.SQL_SMALLINT }, - { name: 'TYPE_NAME', dataType: odbc.SQL_VARCHAR }, - { name: 'COLUMN_SIZE', dataType: odbc.SQL_INTEGER }, - { name: 'BUFFER_LENGTH', dataType: odbc.SQL_INTEGER }, - { name: 'DECIMAL_DIGITS', dataType: odbc.SQL_SMALLINT }, - { name: 'NUM_PREC_RADIX', dataType: odbc.SQL_SMALLINT }, - { name: 'NULLABLE', dataType: odbc.SQL_SMALLINT }, - { name: 'REMARKS', dataType: odbc.SQL_VARCHAR }, - { name: 'COLUMN_DEF', dataType: odbc.SQL_VARCHAR }, - { name: 'SQL_DATA_TYPE', dataType: odbc.SQL_SMALLINT }, - { name: 'SQL_DATETIME_SUB', dataType: odbc.SQL_SMALLINT }, - { name: 'CHAR_OCTET_LENGTH', dataType: odbc.SQL_INTEGER }, - { name: 'ORDINAL_POSITION', dataType: odbc.SQL_INTEGER }, - { name: 'IS_NULLABLE', dataType: odbc.SQL_VARCHAR }, + { name: 'TABLE_CAT', dataType: nativeOdbc.SQL_VARCHAR }, + { name: 'TABLE_SCHEM', dataType: nativeOdbc.SQL_VARCHAR }, + { name: 'TABLE_NAME', dataType: nativeOdbc.SQL_VARCHAR }, + { name: 'COLUMN_NAME', dataType: nativeOdbc.SQL_VARCHAR }, + { name: 'DATA_TYPE', dataType: nativeOdbc.SQL_SMALLINT }, + { name: 'TYPE_NAME', dataType: nativeOdbc.SQL_VARCHAR }, + { name: 'COLUMN_SIZE', dataType: nativeOdbc.SQL_INTEGER }, + { name: 'BUFFER_LENGTH', dataType: nativeOdbc.SQL_INTEGER }, + { name: 'DECIMAL_DIGITS', dataType: nativeOdbc.SQL_SMALLINT }, + { name: 'NUM_PREC_RADIX', dataType: nativeOdbc.SQL_SMALLINT }, + { name: 'NULLABLE', dataType: nativeOdbc.SQL_SMALLINT }, + { name: 'REMARKS', dataType: nativeOdbc.SQL_VARCHAR }, + { name: 'COLUMN_DEF', dataType: nativeOdbc.SQL_VARCHAR }, + { name: 'SQL_DATA_TYPE', dataType: nativeOdbc.SQL_SMALLINT }, + { name: 'SQL_DATETIME_SUB', dataType: nativeOdbc.SQL_SMALLINT }, + { name: 'CHAR_OCTET_LENGTH', dataType: nativeOdbc.SQL_INTEGER }, + { name: 'ORDINAL_POSITION', dataType: nativeOdbc.SQL_INTEGER }, + { name: 'IS_NULLABLE', dataType: nativeOdbc.SQL_VARCHAR }, ]); done(); }); @@ -93,71 +91,69 @@ describe('.columns(catalog, schema, table, column, callback)...', () => { }); // ...with callbacks... describe('...with promises...', () => { it('...should return information about all columns of a table.', async () => { - // const connection = new Connection(`${process.env.CONNECTION_STRING}`); const results = await connection.columns(null, `${process.env.DB_SCHEMA}`, `${process.env.DB_TABLE}`, null); assert.strictEqual(results.length, 3); assert.strictEqual(results.count, 3); assert.deepStrictEqual(results.columns, [ - { name: 'TABLE_CAT', dataType: odbc.SQL_VARCHAR }, - { name: 'TABLE_SCHEM', dataType: odbc.SQL_VARCHAR }, - { name: 'TABLE_NAME', dataType: odbc.SQL_VARCHAR }, - { name: 'COLUMN_NAME', dataType: odbc.SQL_VARCHAR }, - { name: 'DATA_TYPE', dataType: odbc.SQL_SMALLINT }, - { name: 'TYPE_NAME', dataType: odbc.SQL_VARCHAR }, - { name: 'COLUMN_SIZE', dataType: odbc.SQL_INTEGER }, - { name: 'BUFFER_LENGTH', dataType: odbc.SQL_INTEGER }, - { name: 'DECIMAL_DIGITS', dataType: odbc.SQL_SMALLINT }, - { name: 'NUM_PREC_RADIX', dataType: odbc.SQL_SMALLINT }, - { name: 'NULLABLE', dataType: odbc.SQL_SMALLINT }, - { name: 'REMARKS', dataType: odbc.SQL_VARCHAR }, - { name: 'COLUMN_DEF', dataType: odbc.SQL_VARCHAR }, - { name: 'SQL_DATA_TYPE', dataType: odbc.SQL_SMALLINT }, - { name: 'SQL_DATETIME_SUB', dataType: odbc.SQL_SMALLINT }, - { name: 'CHAR_OCTET_LENGTH', dataType: odbc.SQL_INTEGER }, - { name: 'ORDINAL_POSITION', dataType: odbc.SQL_INTEGER }, - { name: 'IS_NULLABLE', dataType: odbc.SQL_VARCHAR }, + { name: 'TABLE_CAT', dataType: nativeOdbc.SQL_VARCHAR }, + { name: 'TABLE_SCHEM', dataType: nativeOdbc.SQL_VARCHAR }, + { name: 'TABLE_NAME', dataType: nativeOdbc.SQL_VARCHAR }, + { name: 'COLUMN_NAME', dataType: nativeOdbc.SQL_VARCHAR }, + { name: 'DATA_TYPE', dataType: nativeOdbc.SQL_SMALLINT }, + { name: 'TYPE_NAME', dataType: nativeOdbc.SQL_VARCHAR }, + { name: 'COLUMN_SIZE', dataType: nativeOdbc.SQL_INTEGER }, + { name: 'BUFFER_LENGTH', dataType: nativeOdbc.SQL_INTEGER }, + { name: 'DECIMAL_DIGITS', dataType: nativeOdbc.SQL_SMALLINT }, + { name: 'NUM_PREC_RADIX', dataType: nativeOdbc.SQL_SMALLINT }, + { name: 'NULLABLE', dataType: nativeOdbc.SQL_SMALLINT }, + { name: 'REMARKS', dataType: nativeOdbc.SQL_VARCHAR }, + { name: 'COLUMN_DEF', dataType: nativeOdbc.SQL_VARCHAR }, + { name: 'SQL_DATA_TYPE', dataType: nativeOdbc.SQL_SMALLINT }, + { name: 'SQL_DATETIME_SUB', dataType: nativeOdbc.SQL_SMALLINT }, + { name: 'CHAR_OCTET_LENGTH', dataType: nativeOdbc.SQL_INTEGER }, + { name: 'ORDINAL_POSITION', dataType: nativeOdbc.SQL_INTEGER }, + { name: 'IS_NULLABLE', dataType: nativeOdbc.SQL_VARCHAR }, ]); const idColumn = results[0]; assert.deepEqual(idColumn.COLUMN_NAME, 'ID'); - assert.deepEqual(idColumn.DATA_TYPE, odbc.SQL_INTEGER); - assert.deepEqual(idColumn.NULLABLE, odbc.SQL_NULLABLE); + assert.deepEqual(idColumn.DATA_TYPE, nativeOdbc.SQL_INTEGER); + assert.deepEqual(idColumn.NULLABLE, nativeOdbc.SQL_NULLABLE); const nameColumn = results[1]; assert.deepEqual(nameColumn.COLUMN_NAME, 'NAME'); - assert.deepEqual(nameColumn.DATA_TYPE, odbc.SQL_VARCHAR); - assert.deepEqual(nameColumn.NULLABLE, odbc.SQL_NULLABLE); + assert.deepEqual(nameColumn.DATA_TYPE, nativeOdbc.SQL_VARCHAR); + assert.deepEqual(nameColumn.NULLABLE, nativeOdbc.SQL_NULLABLE); const ageColumn = results[2]; assert.deepEqual(ageColumn.COLUMN_NAME, 'AGE'); - assert.deepEqual(ageColumn.DATA_TYPE, odbc.SQL_INTEGER); - assert.deepEqual(ageColumn.NULLABLE, odbc.SQL_NULLABLE); + assert.deepEqual(ageColumn.DATA_TYPE, nativeOdbc.SQL_INTEGER); + assert.deepEqual(ageColumn.NULLABLE, nativeOdbc.SQL_NULLABLE); }); it('...should return empty with bad parameters.', async () => { - // const connection = new Connection(`${process.env.CONNECTION_STRING}`); const results = await connection.columns(null, 'bad schema name', 'bad table name', null); assert.strictEqual(results.length, 0); assert.strictEqual(results.count, 0); assert.deepStrictEqual(results.columns, [ - { name: 'TABLE_CAT', dataType: odbc.SQL_VARCHAR }, - { name: 'TABLE_SCHEM', dataType: odbc.SQL_VARCHAR }, - { name: 'TABLE_NAME', dataType: odbc.SQL_VARCHAR }, - { name: 'COLUMN_NAME', dataType: odbc.SQL_VARCHAR }, - { name: 'DATA_TYPE', dataType: odbc.SQL_SMALLINT }, - { name: 'TYPE_NAME', dataType: odbc.SQL_VARCHAR }, - { name: 'COLUMN_SIZE', dataType: odbc.SQL_INTEGER }, - { name: 'BUFFER_LENGTH', dataType: odbc.SQL_INTEGER }, - { name: 'DECIMAL_DIGITS', dataType: odbc.SQL_SMALLINT }, - { name: 'NUM_PREC_RADIX', dataType: odbc.SQL_SMALLINT }, - { name: 'NULLABLE', dataType: odbc.SQL_SMALLINT }, - { name: 'REMARKS', dataType: odbc.SQL_VARCHAR }, - { name: 'COLUMN_DEF', dataType: odbc.SQL_VARCHAR }, - { name: 'SQL_DATA_TYPE', dataType: odbc.SQL_SMALLINT }, - { name: 'SQL_DATETIME_SUB', dataType: odbc.SQL_SMALLINT }, - { name: 'CHAR_OCTET_LENGTH', dataType: odbc.SQL_INTEGER }, - { name: 'ORDINAL_POSITION', dataType: odbc.SQL_INTEGER }, - { name: 'IS_NULLABLE', dataType: odbc.SQL_VARCHAR }, + { name: 'TABLE_CAT', dataType: nativeOdbc.SQL_VARCHAR }, + { name: 'TABLE_SCHEM', dataType: nativeOdbc.SQL_VARCHAR }, + { name: 'TABLE_NAME', dataType: nativeOdbc.SQL_VARCHAR }, + { name: 'COLUMN_NAME', dataType: nativeOdbc.SQL_VARCHAR }, + { name: 'DATA_TYPE', dataType: nativeOdbc.SQL_SMALLINT }, + { name: 'TYPE_NAME', dataType: nativeOdbc.SQL_VARCHAR }, + { name: 'COLUMN_SIZE', dataType: nativeOdbc.SQL_INTEGER }, + { name: 'BUFFER_LENGTH', dataType: nativeOdbc.SQL_INTEGER }, + { name: 'DECIMAL_DIGITS', dataType: nativeOdbc.SQL_SMALLINT }, + { name: 'NUM_PREC_RADIX', dataType: nativeOdbc.SQL_SMALLINT }, + { name: 'NULLABLE', dataType: nativeOdbc.SQL_SMALLINT }, + { name: 'REMARKS', dataType: nativeOdbc.SQL_VARCHAR }, + { name: 'COLUMN_DEF', dataType: nativeOdbc.SQL_VARCHAR }, + { name: 'SQL_DATA_TYPE', dataType: nativeOdbc.SQL_SMALLINT }, + { name: 'SQL_DATETIME_SUB', dataType: nativeOdbc.SQL_SMALLINT }, + { name: 'CHAR_OCTET_LENGTH', dataType: nativeOdbc.SQL_INTEGER }, + { name: 'ORDINAL_POSITION', dataType: nativeOdbc.SQL_INTEGER }, + { name: 'IS_NULLABLE', dataType: nativeOdbc.SQL_VARCHAR }, ]); }); }); // ...with promises... diff --git a/test/connection/commit.js b/test/connection/commit.js index 8c2c82a2..511b1e30 100644 --- a/test/connection/commit.js +++ b/test/connection/commit.js @@ -2,35 +2,71 @@ require('dotenv').config(); const assert = require('assert'); -const { Connection } = require('../../'); +const odbc = require('../../'); describe('.commit([callback])...', () => { describe('...with callbacks...', () => { it('...should commit all queries from after beginTransaction() was called.', (done) => { try { - const connection = new Connection(`${process.env.CONNECTION_STRING}`); - connection.beginTransaction((error1) => { + odbc.connect(`${process.env.CONNECTION_STRING}`, (error, connection) => { + assert.deepEqual(error, null); + connection.beginTransaction((error1) => { + assert.deepEqual(error1, null); + connection.query(`INSERT INTO ${process.env.DB_SCHEMA}.${process.env.DB_TABLE} VALUES(2, 'rolledback', 20)`, (error2, result2) => { + assert.deepEqual(error2, null); + assert.notDeepEqual(result2, null); + assert.deepEqual(result2.count, 1); + connection.query(`SELECT * FROM ${process.env.DB_SCHEMA}.${process.env.DB_TABLE}`, (error3, result3) => { + assert.deepEqual(error3, null); + assert.notDeepEqual(result3, null); + assert.deepEqual(result3.length, 1); + assert.deepEqual(result3.count, -1); + assert.deepEqual(result3[0], { ID: 2, NAME: 'rolledback', AGE: 20 }); + connection.commit((error4) => { + assert.deepEqual(error4, null); + connection.query(`SELECT * FROM ${process.env.DB_SCHEMA}.${process.env.DB_TABLE}`, (error5, result5) => { + assert.deepEqual(error5, null); + assert.notDeepEqual(result5, null); + assert.deepEqual(result5.length, 1); + assert.deepEqual(result5.count, -1); + assert.deepEqual(result5[0], { ID: 2, NAME: 'rolledback', AGE: 20 }); + connection.close((error6) => { + assert.deepEqual(error6); + done(); + }); + }); + }); + }); + }); + }); + }); + } catch (error) { + done(error); + } + }); + it('...shouldn\'t have adverse effects if called outside beginTransaction().', (done) => { + odbc.connect(`${process.env.CONNECTION_STRING}`, (error, connection) => { + assert.deepEqual(error, null); + connection.query(`INSERT INTO ${process.env.DB_SCHEMA}.${process.env.DB_TABLE} VALUES(1, 'committed', 10)`, (error1, result1) => { assert.deepEqual(error1, null); - connection.query(`INSERT INTO ${process.env.DB_SCHEMA}.${process.env.DB_TABLE} VALUES(2, 'rolledback', 20)`, (error2, result2) => { + assert.notDeepEqual(result1, null); + assert.deepEqual(result1.count, 1); + connection.commit((error2) => { assert.deepEqual(error2, null); - assert.notDeepEqual(result2, null); - assert.deepEqual(result2.count, 1); connection.query(`SELECT * FROM ${process.env.DB_SCHEMA}.${process.env.DB_TABLE}`, (error3, result3) => { assert.deepEqual(error3, null); - assert.notDeepEqual(result3, null); assert.deepEqual(result3.length, 1); + assert.deepEqual(result3[0], { ID: 1, NAME: 'committed', AGE: 10 }); assert.deepEqual(result3.count, -1); - assert.deepEqual(result3[0], { ID: 2, NAME: 'rolledback', AGE: 20 }); connection.commit((error4) => { assert.deepEqual(error4, null); connection.query(`SELECT * FROM ${process.env.DB_SCHEMA}.${process.env.DB_TABLE}`, (error5, result5) => { assert.deepEqual(error5, null); - assert.notDeepEqual(result5, null); assert.deepEqual(result5.length, 1); + assert.deepEqual(result5[0], { ID: 1, NAME: 'committed', AGE: 10 }); assert.deepEqual(result5.count, -1); - assert.deepEqual(result5[0], { ID: 2, NAME: 'rolledback', AGE: 20 }); connection.close((error6) => { - assert.deepEqual(error6); + assert.deepEqual(error6, null); done(); }); }); @@ -38,65 +74,34 @@ describe('.commit([callback])...', () => { }); }); }); - } catch (error) { - done(error); - } - }); - it('...shouldn\'t have adverse effects if called outside beginTransaction().', (done) => { - const connection = new Connection(`${process.env.CONNECTION_STRING}`); - connection.query(`INSERT INTO ${process.env.DB_SCHEMA}.${process.env.DB_TABLE} VALUES(1, 'committed', 10)`, (error1, result1) => { - assert.deepEqual(error1, null); - assert.notDeepEqual(result1, null); - assert.deepEqual(result1.count, 1); - connection.commit((error2) => { - assert.deepEqual(error2, null); - connection.query(`SELECT * FROM ${process.env.DB_SCHEMA}.${process.env.DB_TABLE}`, (error3, result3) => { - assert.deepEqual(error3, null); - assert.deepEqual(result3.length, 1); - assert.deepEqual(result3[0], { ID: 1, NAME: 'committed', AGE: 10 }); - assert.deepEqual(result3.count, -1); - connection.commit((error4) => { - assert.deepEqual(error4, null); - connection.query(`SELECT * FROM ${process.env.DB_SCHEMA}.${process.env.DB_TABLE}`, (error5, result5) => { - assert.deepEqual(error5, null); - assert.deepEqual(result5.length, 1); - assert.deepEqual(result5[0], { ID: 1, NAME: 'committed', AGE: 10 }); - assert.deepEqual(result5.count, -1); - connection.close((error6) => { - assert.deepEqual(error6, null); - done(); - }); - }); - }); - }); - }); }); }); it('...shouldn\'t commit if called after a transaction is already ended with rollback().', (done) => { - const connection = new Connection(`${process.env.CONNECTION_STRING}`); - connection.beginTransaction((error1) => { - assert.deepEqual(error1, null); - connection.query(`INSERT INTO ${process.env.DB_SCHEMA}.${process.env.DB_TABLE} VALUES(1, 'committed', 10)`, (error2, result2) => { - assert.deepEqual(error2, null); - assert.notDeepEqual(result2, null); - assert.deepEqual(result2.count, 1); - connection.rollback((error3) => { - assert.deepEqual(error3, null); - connection.query(`SELECT * FROM ${process.env.DB_SCHEMA}.${process.env.DB_TABLE}`, (error4, result4) => { - assert.deepEqual(error4, null); - assert.notDeepEqual(result4, null); - assert.deepEqual(result4.length, 0); - assert.deepEqual(result4.count, -1); - connection.commit((error5) => { - assert.deepEqual(error5, null); - connection.query(`SELECT * FROM ${process.env.DB_SCHEMA}.${process.env.DB_TABLE}`, (error6, result6) => { - assert.deepEqual(error6, null); - assert.notDeepEqual(result6, null); - assert.deepEqual(result6.length, 0); - assert.deepEqual(result6.count, -1); - connection.close((error7) => { - assert.deepEqual(error7, null); - done(); + odbc.connect(`${process.env.CONNECTION_STRING}`, (error, connection) => { + connection.beginTransaction((error1) => { + assert.deepEqual(error1, null); + connection.query(`INSERT INTO ${process.env.DB_SCHEMA}.${process.env.DB_TABLE} VALUES(1, 'committed', 10)`, (error2, result2) => { + assert.deepEqual(error2, null); + assert.notDeepEqual(result2, null); + assert.deepEqual(result2.count, 1); + connection.rollback((error3) => { + assert.deepEqual(error3, null); + connection.query(`SELECT * FROM ${process.env.DB_SCHEMA}.${process.env.DB_TABLE}`, (error4, result4) => { + assert.deepEqual(error4, null); + assert.notDeepEqual(result4, null); + assert.deepEqual(result4.length, 0); + assert.deepEqual(result4.count, -1); + connection.commit((error5) => { + assert.deepEqual(error5, null); + connection.query(`SELECT * FROM ${process.env.DB_SCHEMA}.${process.env.DB_TABLE}`, (error6, result6) => { + assert.deepEqual(error6, null); + assert.notDeepEqual(result6, null); + assert.deepEqual(result6.length, 0); + assert.deepEqual(result6.count, -1); + connection.close((error7) => { + assert.deepEqual(error7, null); + done(); + }); }); }); }); @@ -108,7 +113,7 @@ describe('.commit([callback])...', () => { }); // '...with callbacks...' describe('...with promises...', () => { it('...should commit all queries from after beginTransaction() was called.', async () => { - const connection = new Connection(`${process.env.CONNECTION_STRING}`); + const connection = await odbc.connect(`${process.env.CONNECTION_STRING}`); await connection.beginTransaction(); const result1 = await connection.query(`INSERT INTO ${process.env.DB_SCHEMA}.${process.env.DB_TABLE} VALUES(1, 'comitted', 10)`); assert.notDeepEqual(result1, null); @@ -127,7 +132,7 @@ describe('.commit([callback])...', () => { await connection.close(); }); it('...shouldn\'t have adverse effects if called outside beginTransaction().', async () => { - const connection = new Connection(`${process.env.CONNECTION_STRING}`); + const connection = await odbc.connect(`${process.env.CONNECTION_STRING}`); const result1 = await connection.query(`INSERT INTO ${process.env.DB_SCHEMA}.${process.env.DB_TABLE} VALUES(1, 'committed', 10)`); assert.notDeepEqual(result1, null); assert.deepEqual(result1.count, 1); @@ -146,7 +151,7 @@ describe('.commit([callback])...', () => { await connection.close(); }); it('...shouldn\'t commit if called after a transaction is already ended with rollback().', async () => { - const connection = new Connection(`${process.env.CONNECTION_STRING}`); + const connection = await odbc.connect(`${process.env.CONNECTION_STRING}`); await connection.beginTransaction(); const result1 = await connection.query(`INSERT INTO ${process.env.DB_SCHEMA}.${process.env.DB_TABLE} VALUES(1, 'committed', 10)`); assert.notDeepEqual(result1, null); diff --git a/test/connection/constructor.js b/test/connection/constructor.js index a182dde8..409c2894 100644 --- a/test/connection/constructor.js +++ b/test/connection/constructor.js @@ -2,20 +2,44 @@ require('dotenv').config(); const assert = require('assert'); -const { Connection } = require('../../'); +const odbc = require('../../'); +const { Connection } = require('../../lib/Connection'); -describe('new Connection(connectionString)...', () => { - it('...should return an open Connection when passed a valid connection string.', async () => { - const connection = new Connection(`${process.env.CONNECTION_STRING}`); - assert.notDeepEqual(connection, null); - assert.deepEqual(connection instanceof Connection, true); - assert.deepEqual(connection.connected, true); - assert.deepEqual(connection.autocommit, true); - await connection.close(); +describe('odbc.connect...', () => { + describe('...with callbacks...', () => { + it('...should return an open Connection when passed a valid connection string.', (done) => { + odbc.connect(`${process.env.CONNECTION_STRING}`, (error1, connection) => { + assert.deepEqual(error1, null); + assert.deepEqual(connection instanceof Connection, true); + assert.deepEqual(connection.connected, true); + assert.deepEqual(connection.autocommit, true); + connection.close((error2) => { + assert.deepEqual(error2, null); + done(); + }); + }); + }); + it('...should throw an Error when passed an invalid connection string.', (done) => { + odbc.connect('abc123!@#', (error, connection) => { + assert.notDeepEqual(error, null); + assert.deepEqual(connection, null); + done(); + }); + }); }); - it('...should throw an Error when passed an invalid connection string.', async () => { - assert.throws(() => { - const connection = new Connection('abc123!@#'); // eslint-disable-line no-unused-vars + describe('...with promises...', () => { + it('...should return an open Connection when passed a valid connection string.', async () => { + const connection = await odbc.connect(`${process.env.CONNECTION_STRING}`); + assert.notDeepEqual(connection, null); + // assert.deepEqual(connection instanceof odbc.Connection, true); + assert.deepEqual(connection.connected, true); + assert.deepEqual(connection.autocommit, true); + await connection.close(); + }); + it('...should throw an Error when passed an invalid connection string.', async () => { + assert.rejects(async () => { + const connection = await odbc.connect('abc123!@#'); // eslint-disable-line no-unused-vars + }); }); }); }); diff --git a/test/connection/query.js b/test/connection/query.js index 9fd4e42f..2a05bff6 100644 --- a/test/connection/query.js +++ b/test/connection/query.js @@ -2,7 +2,7 @@ require('dotenv').config(); const assert = require('assert'); -const { Connection } = require('../../'); +const odbc = require('../../'); describe('.query(sql, [parameters], [callback])...', () => { it('...should throw a TypeError if function signature doesn\'t match accepted signatures.', async () => { @@ -13,7 +13,7 @@ describe('.query(sql, [parameters], [callback])...', () => { const QUERY_CALLBACK = () => {}; const QUERY_STRING = `SELECT * FROM ${process.env.DB_SCHEMA}.${process.env.DB_TABLE}`; - const connection = new Connection(`${process.env.CONNECTION_STRING}`); + const connection = await odbc.connect(`${process.env.CONNECTION_STRING}`); assert.throws(() => { connection.query(); }, QUERY_TYPE_ERROR); @@ -42,41 +42,44 @@ describe('.query(sql, [parameters], [callback])...', () => { }); describe('...with callbacks...', () => { it('...should correctly identify function signature with .query({string}, {function}).', (done) => { - const connection = new Connection(`${process.env.CONNECTION_STRING}`); - connection.query(`INSERT INTO ${process.env.DB_SCHEMA}.${process.env.DB_TABLE} VALUES(1, 'committed', 10)`, (error1, result1) => { - assert.deepEqual(error1, null); - assert.notDeepEqual(result1, null); - assert.deepEqual(result1.length, 0); - assert.deepEqual(result1.count, 1); - connection.query(`SELECT * FROM ${process.env.DB_SCHEMA}.${process.env.DB_TABLE}`, (error2, result2) => { - assert.deepEqual(error2, null); - assert.notDeepEqual(result2, null); - assert.deepEqual(result2.length, 1); - assert.deepEqual(result2.count, -1); - assert.deepEqual(result2[0], { ID: 1, NAME: 'committed', AGE: 10 }); - connection.close((error3) => { - assert.deepEqual(error3, null); - done(); + odbc.connect(`${process.env.CONNECTION_STRING}`, (error, connection) => { + assert.deepEqual(error, null); + connection.query(`INSERT INTO ${process.env.DB_SCHEMA}.${process.env.DB_TABLE} VALUES(1, 'committed', 10)`, (error1, result1) => { + assert.deepEqual(error1, null); + assert.notDeepEqual(result1, null); + assert.deepEqual(result1.length, 0); + assert.deepEqual(result1.count, 1); + connection.query(`SELECT * FROM ${process.env.DB_SCHEMA}.${process.env.DB_TABLE}`, (error2, result2) => { + assert.deepEqual(error2, null); + assert.notDeepEqual(result2, null); + assert.deepEqual(result2.length, 1); + assert.deepEqual(result2.count, -1); + assert.deepEqual(result2[0], { ID: 1, NAME: 'committed', AGE: 10 }); + connection.close((error3) => { + assert.deepEqual(error3, null); + done(); + }); }); }); }); }); it('...should correctly identify function signature with .query({string}, {array}, {function})', (done) => { - const connection = new Connection(`${process.env.CONNECTION_STRING}`); - connection.query(`INSERT INTO ${process.env.DB_SCHEMA}.${process.env.DB_TABLE} VALUES(?, ?, ?)`, [1, 'committed', 10], (error1, result1) => { - assert.deepEqual(error1, null); - assert.notDeepEqual(result1, null); - assert.deepEqual(result1.length, 0); - assert.deepEqual(result1.count, 1); - connection.query(`SELECT * FROM ${process.env.DB_SCHEMA}.${process.env.DB_TABLE}`, (error2, result2) => { - assert.deepEqual(error2, null); - assert.notDeepEqual(result2, null); - assert.deepEqual(result2.length, 1); - assert.deepEqual(result2.count, -1); - assert.deepEqual(result2[0], { ID: 1, NAME: 'committed', AGE: 10 }); - connection.close((error3) => { - assert.deepEqual(error3, null); - done(); + odbc.connect(`${process.env.CONNECTION_STRING}`, (error, connection) => { + connection.query(`INSERT INTO ${process.env.DB_SCHEMA}.${process.env.DB_TABLE} VALUES(?, ?, ?)`, [1, 'committed', 10], (error1, result1) => { + assert.deepEqual(error1, null); + assert.notDeepEqual(result1, null); + assert.deepEqual(result1.length, 0); + assert.deepEqual(result1.count, 1); + connection.query(`SELECT * FROM ${process.env.DB_SCHEMA}.${process.env.DB_TABLE}`, (error2, result2) => { + assert.deepEqual(error2, null); + assert.notDeepEqual(result2, null); + assert.deepEqual(result2.length, 1); + assert.deepEqual(result2.count, -1); + assert.deepEqual(result2[0], { ID: 1, NAME: 'committed', AGE: 10 }); + connection.close((error3) => { + assert.deepEqual(error3, null); + done(); + }); }); }); }); @@ -84,7 +87,7 @@ describe('.query(sql, [parameters], [callback])...', () => { }); // ...with callbacks... describe('...with promises...', () => { it('...should correctly identify function signature with .query({string}).', async () => { - const connection = new Connection(`${process.env.CONNECTION_STRING}`); + const connection = await odbc.connect(`${process.env.CONNECTION_STRING}`); const result1 = await connection.query(`INSERT INTO ${process.env.DB_SCHEMA}.${process.env.DB_TABLE} VALUES(1, 'committed', 10)`); assert.notDeepEqual(result1, null); assert.deepEqual(result1.length, 0); @@ -97,7 +100,7 @@ describe('.query(sql, [parameters], [callback])...', () => { await connection.close(); }); it('...should correctly identify function signature with .query({string}, {array})', async () => { - const connection = new Connection(`${process.env.CONNECTION_STRING}`); + const connection = await odbc.connect(`${process.env.CONNECTION_STRING}`); const result1 = await connection.query(`INSERT INTO ${process.env.DB_SCHEMA}.${process.env.DB_TABLE} VALUES(?, ?, ?)`, [1, 'committed', 10]); assert.notDeepEqual(result1, null); assert.deepEqual(result1.length, 0); diff --git a/test/connection/rollback.js b/test/connection/rollback.js index ed98c833..54b5e7a1 100644 --- a/test/connection/rollback.js +++ b/test/connection/rollback.js @@ -2,32 +2,34 @@ require('dotenv').config(); const assert = require('assert'); -const { Connection } = require('../../'); +const odbc = require('../../'); describe('.rollback(callback)...', () => { describe('...with callbacks...', () => { it('...should rollback all queries from after beginTransaction() was called.', (done) => { - const connection = new Connection(`${process.env.CONNECTION_STRING}`); - connection.beginTransaction((error1) => { - assert.deepEqual(error1, null); - connection.query(`INSERT INTO ${process.env.DB_SCHEMA}.${process.env.DB_TABLE} VALUES(2, 'rolledback', 20)`, (error2, result2) => { - assert.deepEqual(error2, null); - assert.notDeepEqual(result2, null); - assert.deepEqual(result2.count, 1); - connection.query(`SELECT * FROM ${process.env.DB_SCHEMA}.${process.env.DB_TABLE}`, (error3, result3) => { - assert.deepEqual(error3, null); - assert.deepEqual(result3.length, 1); - assert.deepEqual(result3.count, -1); - assert.deepEqual(result3[0], { ID: 2, NAME: 'rolledback', AGE: 20 }); - connection.rollback((error4) => { - assert.deepEqual(error4, null); - connection.query(`SELECT * FROM ${process.env.DB_SCHEMA}.${process.env.DB_TABLE}`, (error5, result5) => { - assert.deepEqual(error5, null); - assert.deepEqual(result5.length, 0); - assert.deepEqual(result5.count, -1); - connection.close((error6) => { - assert.deepEqual(error6); - done(); + odbc.connect(`${process.env.CONNECTION_STRING}`, (error, connection) => { + assert.deepEqual(error, null); + connection.beginTransaction((error1) => { + assert.deepEqual(error1, null); + connection.query(`INSERT INTO ${process.env.DB_SCHEMA}.${process.env.DB_TABLE} VALUES(2, 'rolledback', 20)`, (error2, result2) => { + assert.deepEqual(error2, null); + assert.notDeepEqual(result2, null); + assert.deepEqual(result2.count, 1); + connection.query(`SELECT * FROM ${process.env.DB_SCHEMA}.${process.env.DB_TABLE}`, (error3, result3) => { + assert.deepEqual(error3, null); + assert.deepEqual(result3.length, 1); + assert.deepEqual(result3.count, -1); + assert.deepEqual(result3[0], { ID: 2, NAME: 'rolledback', AGE: 20 }); + connection.rollback((error4) => { + assert.deepEqual(error4, null); + connection.query(`SELECT * FROM ${process.env.DB_SCHEMA}.${process.env.DB_TABLE}`, (error5, result5) => { + assert.deepEqual(error5, null); + assert.deepEqual(result5.length, 0); + assert.deepEqual(result5.count, -1); + connection.close((error6) => { + assert.deepEqual(error6); + done(); + }); }); }); }); @@ -36,37 +38,38 @@ describe('.rollback(callback)...', () => { }); }); it('...shouldn\'t rollback commits from before beginTransaction() was called.', (done) => { - const connection = new Connection(`${process.env.CONNECTION_STRING}`); - connection.query(`INSERT INTO ${process.env.DB_SCHEMA}.${process.env.DB_TABLE} VALUES(1, 'committed', 10)`, (error1, result1) => { - assert.deepEqual(error1, null); - assert.notDeepEqual(result1, null); - assert.deepEqual(result1.count, 1); - connection.query(`SELECT * FROM ${process.env.DB_SCHEMA}.${process.env.DB_TABLE}`, (error2, result2) => { - assert.deepEqual(error2, null); - assert.deepEqual(result2.length, 1); - assert.deepEqual(result2[0], { ID: 1, NAME: 'committed', AGE: 10 }); - assert.deepEqual(result2.count, -1); - connection.beginTransaction((error3) => { - assert.deepEqual(error3, null); - connection.query(`INSERT INTO ${process.env.DB_SCHEMA}.${process.env.DB_TABLE} VALUES(2, 'rolledback', 20)`, (error4, result4) => { - assert.deepEqual(error4, null); - assert.notDeepEqual(result4, null); - assert.deepEqual(result4.count, 1); - connection.query(`SELECT * FROM ${process.env.DB_SCHEMA}.${process.env.DB_TABLE}`, (error5, result5) => { - assert.deepEqual(error5, null); - assert.deepEqual(result5.length, 2); - assert.deepEqual(result5[1], { ID: 2, NAME: 'rolledback', AGE: 20 }); - assert.deepEqual(result5.count, -1); - connection.rollback((error6) => { - assert.deepEqual(error6, null); - connection.query(`SELECT * FROM ${process.env.DB_SCHEMA}.${process.env.DB_TABLE}`, (error7, result7) => { - assert.deepEqual(error7, null); - assert.deepEqual(result7.length, 1); - assert.deepEqual(result7[0], { ID: 1, NAME: 'committed', AGE: 10 }); - assert.deepEqual(result7.count, -1); - connection.close((error8) => { - assert.deepEqual(error8, null); - done(); + odbc.connect(`${process.env.CONNECTION_STRING}`, (error, connection) => { + connection.query(`INSERT INTO ${process.env.DB_SCHEMA}.${process.env.DB_TABLE} VALUES(1, 'committed', 10)`, (error1, result1) => { + assert.deepEqual(error1, null); + assert.notDeepEqual(result1, null); + assert.deepEqual(result1.count, 1); + connection.query(`SELECT * FROM ${process.env.DB_SCHEMA}.${process.env.DB_TABLE}`, (error2, result2) => { + assert.deepEqual(error2, null); + assert.deepEqual(result2.length, 1); + assert.deepEqual(result2[0], { ID: 1, NAME: 'committed', AGE: 10 }); + assert.deepEqual(result2.count, -1); + connection.beginTransaction((error3) => { + assert.deepEqual(error3, null); + connection.query(`INSERT INTO ${process.env.DB_SCHEMA}.${process.env.DB_TABLE} VALUES(2, 'rolledback', 20)`, (error4, result4) => { + assert.deepEqual(error4, null); + assert.notDeepEqual(result4, null); + assert.deepEqual(result4.count, 1); + connection.query(`SELECT * FROM ${process.env.DB_SCHEMA}.${process.env.DB_TABLE}`, (error5, result5) => { + assert.deepEqual(error5, null); + assert.deepEqual(result5.length, 2); + assert.deepEqual(result5[1], { ID: 2, NAME: 'rolledback', AGE: 20 }); + assert.deepEqual(result5.count, -1); + connection.rollback((error6) => { + assert.deepEqual(error6, null); + connection.query(`SELECT * FROM ${process.env.DB_SCHEMA}.${process.env.DB_TABLE}`, (error7, result7) => { + assert.deepEqual(error7, null); + assert.deepEqual(result7.length, 1); + assert.deepEqual(result7[0], { ID: 1, NAME: 'committed', AGE: 10 }); + assert.deepEqual(result7.count, -1); + connection.close((error8) => { + assert.deepEqual(error8, null); + done(); + }); }); }); }); @@ -77,26 +80,27 @@ describe('.rollback(callback)...', () => { }); }); it('...shouldn\'t affect queries when beginTransaction() hasn\'t been called.', (done) => { - const connection = new Connection(`${process.env.CONNECTION_STRING}`); - connection.query(`INSERT INTO ${process.env.DB_SCHEMA}.${process.env.DB_TABLE} VALUES(1, 'committed', 10)`, (error1, result1) => { - assert.deepEqual(error1, null); - assert.notDeepEqual(result1, null); - assert.deepEqual(result1.count, 1); - connection.query(`SELECT * FROM ${process.env.DB_SCHEMA}.${process.env.DB_TABLE}`, (error2, result2) => { - assert.deepEqual(error2, null); - assert.deepEqual(result2.length, 1); - assert.deepEqual(result2[0], { ID: 1, NAME: 'committed', AGE: 10 }); - assert.deepEqual(result2.count, -1); - connection.rollback((error3) => { - assert.deepEqual(error3, null); - connection.query(`SELECT * FROM ${process.env.DB_SCHEMA}.${process.env.DB_TABLE}`, (error4, result7) => { - assert.deepEqual(error4, null); - assert.deepEqual(result7.length, 1); - assert.deepEqual(result7[0], { ID: 1, NAME: 'committed', AGE: 10 }); - assert.deepEqual(result7.count, -1); - connection.close((error5) => { - assert.deepEqual(error5, null); - done(); + odbc.connect(`${process.env.CONNECTION_STRING}`, (error, connection) => { + connection.query(`INSERT INTO ${process.env.DB_SCHEMA}.${process.env.DB_TABLE} VALUES(1, 'committed', 10)`, (error1, result1) => { + assert.deepEqual(error1, null); + assert.notDeepEqual(result1, null); + assert.deepEqual(result1.count, 1); + connection.query(`SELECT * FROM ${process.env.DB_SCHEMA}.${process.env.DB_TABLE}`, (error2, result2) => { + assert.deepEqual(error2, null); + assert.deepEqual(result2.length, 1); + assert.deepEqual(result2[0], { ID: 1, NAME: 'committed', AGE: 10 }); + assert.deepEqual(result2.count, -1); + connection.rollback((error3) => { + assert.deepEqual(error3, null); + connection.query(`SELECT * FROM ${process.env.DB_SCHEMA}.${process.env.DB_TABLE}`, (error4, result7) => { + assert.deepEqual(error4, null); + assert.deepEqual(result7.length, 1); + assert.deepEqual(result7[0], { ID: 1, NAME: 'committed', AGE: 10 }); + assert.deepEqual(result7.count, -1); + connection.close((error5) => { + assert.deepEqual(error5, null); + done(); + }); }); }); }); @@ -104,30 +108,31 @@ describe('.rollback(callback)...', () => { }); }); it('...shouldn\'t rollback if called after a transaction is already ended with commit().', (done) => { - const connection = new Connection(`${process.env.CONNECTION_STRING}`); - connection.beginTransaction((error1) => { - assert.deepEqual(error1, null); - connection.query(`INSERT INTO ${process.env.DB_SCHEMA}.${process.env.DB_TABLE} VALUES(1, 'committed', 10)`, (error2, result2) => { - assert.deepEqual(error2, null); - assert.notDeepEqual(result2, null); - assert.deepEqual(result2.count, 1); - connection.commit((error3) => { - assert.deepEqual(error3, null); - connection.query(`SELECT * FROM ${process.env.DB_SCHEMA}.${process.env.DB_TABLE}`, (error4, result4) => { - assert.deepEqual(error4, null); - assert.deepEqual(result4.length, 1); - assert.deepEqual(result4[0], { ID: 1, NAME: 'committed', AGE: 10 }); - assert.deepEqual(result4.count, -1); - connection.rollback((error5) => { - assert.deepEqual(error5, null); - connection.query(`SELECT * FROM ${process.env.DB_SCHEMA}.${process.env.DB_TABLE}`, (error6, result6) => { - assert.deepEqual(error6, null); - assert.deepEqual(result6.length, 1); - assert.deepEqual(result6[0], { ID: 1, NAME: 'committed', AGE: 10 }); - assert.deepEqual(result6.count, -1); - connection.close((error7) => { - assert.deepEqual(error7, null); - done(); + odbc.connect(`${process.env.CONNECTION_STRING}`, (error, connection) => { + connection.beginTransaction((error1) => { + assert.deepEqual(error1, null); + connection.query(`INSERT INTO ${process.env.DB_SCHEMA}.${process.env.DB_TABLE} VALUES(1, 'committed', 10)`, (error2, result2) => { + assert.deepEqual(error2, null); + assert.notDeepEqual(result2, null); + assert.deepEqual(result2.count, 1); + connection.commit((error3) => { + assert.deepEqual(error3, null); + connection.query(`SELECT * FROM ${process.env.DB_SCHEMA}.${process.env.DB_TABLE}`, (error4, result4) => { + assert.deepEqual(error4, null); + assert.deepEqual(result4.length, 1); + assert.deepEqual(result4[0], { ID: 1, NAME: 'committed', AGE: 10 }); + assert.deepEqual(result4.count, -1); + connection.rollback((error5) => { + assert.deepEqual(error5, null); + connection.query(`SELECT * FROM ${process.env.DB_SCHEMA}.${process.env.DB_TABLE}`, (error6, result6) => { + assert.deepEqual(error6, null); + assert.deepEqual(result6.length, 1); + assert.deepEqual(result6[0], { ID: 1, NAME: 'committed', AGE: 10 }); + assert.deepEqual(result6.count, -1); + connection.close((error7) => { + assert.deepEqual(error7, null); + done(); + }); }); }); }); @@ -137,34 +142,35 @@ describe('.rollback(callback)...', () => { }); }); it('...shouldn\'t rollback if called after a transaction is already ended with rollback().', (done) => { - const connection = new Connection(`${process.env.CONNECTION_STRING}`); - connection.beginTransaction((error1) => { - assert.deepEqual(error1, null); - connection.query(`INSERT INTO ${process.env.DB_SCHEMA}.${process.env.DB_TABLE} VALUES(1, 'committed', 10)`, (error2, result2) => { - assert.deepEqual(error2, null); - assert.notDeepEqual(result2, null); - assert.deepEqual(result2.count, 1); - connection.rollback((error3) => { - assert.deepEqual(error3, null); - connection.query(`INSERT INTO ${process.env.DB_SCHEMA}.${process.env.DB_TABLE} VALUES(1, 'committed', 10)`, (error4, result4) => { - assert.deepEqual(error4, null); - assert.notDeepEqual(result4, null); - assert.deepEqual(result4.count, 1); - connection.query(`SELECT * FROM ${process.env.DB_SCHEMA}.${process.env.DB_TABLE}`, (error5, result5) => { - assert.deepEqual(error5, null); - assert.deepEqual(result5.length, 1); - assert.deepEqual(result5[0], { ID: 1, NAME: 'committed', AGE: 10 }); - assert.deepEqual(result5.count, -1); - connection.rollback((error6) => { - assert.deepEqual(error6, null); - connection.query(`SELECT * FROM ${process.env.DB_SCHEMA}.${process.env.DB_TABLE}`, (error7, result7) => { - assert.deepEqual(error7, null); - assert.deepEqual(result7.length, 1); - assert.deepEqual(result7[0], { ID: 1, NAME: 'committed', AGE: 10 }); - assert.deepEqual(result7.count, -1); - connection.close((error8) => { - assert.deepEqual(error8, null); - done(); + odbc.connect(`${process.env.CONNECTION_STRING}`, (error, connection) => { + connection.beginTransaction((error1) => { + assert.deepEqual(error1, null); + connection.query(`INSERT INTO ${process.env.DB_SCHEMA}.${process.env.DB_TABLE} VALUES(1, 'committed', 10)`, (error2, result2) => { + assert.deepEqual(error2, null); + assert.notDeepEqual(result2, null); + assert.deepEqual(result2.count, 1); + connection.rollback((error3) => { + assert.deepEqual(error3, null); + connection.query(`INSERT INTO ${process.env.DB_SCHEMA}.${process.env.DB_TABLE} VALUES(1, 'committed', 10)`, (error4, result4) => { + assert.deepEqual(error4, null); + assert.notDeepEqual(result4, null); + assert.deepEqual(result4.count, 1); + connection.query(`SELECT * FROM ${process.env.DB_SCHEMA}.${process.env.DB_TABLE}`, (error5, result5) => { + assert.deepEqual(error5, null); + assert.deepEqual(result5.length, 1); + assert.deepEqual(result5[0], { ID: 1, NAME: 'committed', AGE: 10 }); + assert.deepEqual(result5.count, -1); + connection.rollback((error6) => { + assert.deepEqual(error6, null); + connection.query(`SELECT * FROM ${process.env.DB_SCHEMA}.${process.env.DB_TABLE}`, (error7, result7) => { + assert.deepEqual(error7, null); + assert.deepEqual(result7.length, 1); + assert.deepEqual(result7[0], { ID: 1, NAME: 'committed', AGE: 10 }); + assert.deepEqual(result7.count, -1); + connection.close((error8) => { + assert.deepEqual(error8, null); + done(); + }); }); }); }); @@ -177,7 +183,7 @@ describe('.rollback(callback)...', () => { }); // '...with callbacks...' describe('...with promises...', () => { it('...should rollback all queries from after beginTransaction() was called.', async () => { - const connection = new Connection(`${process.env.CONNECTION_STRING}`); + const connection = await odbc.connect(`${process.env.CONNECTION_STRING}`); await connection.beginTransaction(); const result1 = await connection.query(`INSERT INTO ${process.env.DB_SCHEMA}.${process.env.DB_TABLE} VALUES(2, 'rolledback', 20)`); assert.notDeepEqual(result1, null); @@ -193,7 +199,7 @@ describe('.rollback(callback)...', () => { await connection.close(); }); it('...shouldn\'t rollback commits from before beginTransaction() was called.', async () => { - const connection = new Connection(`${process.env.CONNECTION_STRING}`); + const connection = await odbc.connect(`${process.env.CONNECTION_STRING}`); const result1 = await connection.query(`INSERT INTO ${process.env.DB_SCHEMA}.${process.env.DB_TABLE} VALUES(1, 'committed', 10)`); assert.notDeepEqual(result1, null); assert.deepEqual(result1.count, 1); @@ -217,7 +223,7 @@ describe('.rollback(callback)...', () => { await connection.close(); }); it('...shouldn\'t affect queries when beginTransaction() hasn\'t been called.', async () => { - const connection = new Connection(`${process.env.CONNECTION_STRING}`); + const connection = await odbc.connect(`${process.env.CONNECTION_STRING}`); const result1 = await connection.query(`INSERT INTO ${process.env.DB_SCHEMA}.${process.env.DB_TABLE} VALUES(1, 'committed', 10)`); assert.notDeepEqual(result1, null); assert.deepEqual(result1.count, 1); @@ -233,7 +239,7 @@ describe('.rollback(callback)...', () => { await connection.close(); }); it('...shouldn\'t rollback if called after a transaction is already ended with a commit().', async () => { - const connection = new Connection(`${process.env.CONNECTION_STRING}`); + const connection = await odbc.connect(`${process.env.CONNECTION_STRING}`); await connection.beginTransaction(); const result1 = await connection.query(`INSERT INTO ${process.env.DB_SCHEMA}.${process.env.DB_TABLE} VALUES(1, 'committed', 10)`); assert.notDeepEqual(result1, null); @@ -251,7 +257,7 @@ describe('.rollback(callback)...', () => { await connection.close(); }); it('...shouldn\'t rollback if called after a transaction is already ended with a rollback().', async () => { - const connection = new Connection(`${process.env.CONNECTION_STRING}`); + const connection = await odbc.connect(`${process.env.CONNECTION_STRING}`); await connection.beginTransaction(); const result1 = await connection.query(`INSERT INTO ${process.env.DB_SCHEMA}.${process.env.DB_TABLE} VALUES(1, 'committed', 10)`); assert.notDeepEqual(result1, null); diff --git a/test/connection/tables.js b/test/connection/tables.js index 43ace392..d6bd499a 100644 --- a/test/connection/tables.js +++ b/test/connection/tables.js @@ -2,65 +2,69 @@ require('dotenv').config(); const assert = require('assert'); -const odbc = require('../../build/Release/odbc.node'); -const { Connection } = require('../../'); +const nativeOdbc = require('../../build/Release/odbc.node'); +const odbc = require('../../'); describe('.tables(catalog, schema, table, type, callback)...', () => { describe('...with callbacks...', () => { it('...should return information about a table.', (done) => { - const connection = new Connection(`${process.env.CONNECTION_STRING}`); - connection.tables(null, `${process.env.DB_SCHEMA}`, `${process.env.DB_TABLE}`, null, (error, results) => { - assert.strictEqual(error, null); - assert.strictEqual(results.length, 1); - assert.strictEqual(results.count, 1); - assert.deepStrictEqual(results.columns, - [ - { name: 'TABLE_CAT', dataType: odbc.SQL_VARCHAR }, - { name: 'TABLE_SCHEM', dataType: odbc.SQL_VARCHAR }, - { name: 'TABLE_NAME', dataType: odbc.SQL_VARCHAR }, - { name: 'TABLE_TYPE', dataType: odbc.SQL_VARCHAR }, - { name: 'REMARKS', dataType: odbc.SQL_VARCHAR }, - ]); - const result = results[0]; - // not testing for TABLE_CAT, dependent on the system - assert.strictEqual(result.TABLE_SCHEM, `${process.env.DB_SCHEMA}`); - assert.strictEqual(result.TABLE_NAME, `${process.env.DB_TABLE}`); - assert.strictEqual(result.TABLE_TYPE, 'TABLE'); - // not testing for REMARKS, dependent on the system - done(); + odbc.connect(`${process.env.CONNECTION_STRING}`, (error, connection) => { + assert.deepEqual(error, null); + connection.tables(null, `${process.env.DB_SCHEMA}`, `${process.env.DB_TABLE}`, null, (error1, results) => { + assert.strictEqual(error1, null); + assert.strictEqual(results.length, 1); + assert.strictEqual(results.count, 1); + assert.deepStrictEqual(results.columns, + [ + { name: 'TABLE_CAT', dataType: nativeOdbc.SQL_VARCHAR }, + { name: 'TABLE_SCHEM', dataType: nativeOdbc.SQL_VARCHAR }, + { name: 'TABLE_NAME', dataType: nativeOdbc.SQL_VARCHAR }, + { name: 'TABLE_TYPE', dataType: nativeOdbc.SQL_VARCHAR }, + { name: 'REMARKS', dataType: nativeOdbc.SQL_VARCHAR }, + ]); + const result = results[0]; + // not testing for TABLE_CAT, dependent on the system + assert.strictEqual(result.TABLE_SCHEM, `${process.env.DB_SCHEMA}`); + assert.strictEqual(result.TABLE_NAME, `${process.env.DB_TABLE}`); + assert.strictEqual(result.TABLE_TYPE, 'TABLE'); + // not testing for REMARKS, dependent on the system + done(); + }); }); }); it('...should return empty with bad parameters.', (done) => { - const connection = new Connection(`${process.env.CONNECTION_STRING}`); - connection.tables(null, 'bad schema name', 'bad table name', null, (error, results) => { - assert.strictEqual(error, null); - assert.strictEqual(results.length, 0); - assert.strictEqual(results.count, 0); - assert.deepStrictEqual(results.columns, - [ - { name: 'TABLE_CAT', dataType: odbc.SQL_VARCHAR }, - { name: 'TABLE_SCHEM', dataType: odbc.SQL_VARCHAR }, - { name: 'TABLE_NAME', dataType: odbc.SQL_VARCHAR }, - { name: 'TABLE_TYPE', dataType: odbc.SQL_VARCHAR }, - { name: 'REMARKS', dataType: odbc.SQL_VARCHAR }, - ]); - done(); + odbc.connect(`${process.env.CONNECTION_STRING}`, (error, connection) => { + assert.deepEqual(error, null); + connection.tables(null, 'bad schema name', 'bad table name', null, (error1, results) => { + assert.strictEqual(error1, null); + assert.strictEqual(results.length, 0); + assert.strictEqual(results.count, 0); + assert.deepStrictEqual(results.columns, + [ + { name: 'TABLE_CAT', dataType: nativeOdbc.SQL_VARCHAR }, + { name: 'TABLE_SCHEM', dataType: nativeOdbc.SQL_VARCHAR }, + { name: 'TABLE_NAME', dataType: nativeOdbc.SQL_VARCHAR }, + { name: 'TABLE_TYPE', dataType: nativeOdbc.SQL_VARCHAR }, + { name: 'REMARKS', dataType: nativeOdbc.SQL_VARCHAR }, + ]); + done(); + }); }); }); }); // ...with callbacks... describe('...with promises...', () => { it('...should return information about a table.', async () => { - const connection = new Connection(`${process.env.CONNECTION_STRING}`); + const connection = await odbc.connect(`${process.env.CONNECTION_STRING}`); const results = await connection.tables(null, `${process.env.DB_SCHEMA}`, `${process.env.DB_TABLE}`, null); assert.strictEqual(results.length, 1); assert.strictEqual(results.count, 1); assert.deepStrictEqual(results.columns, [ - { name: 'TABLE_CAT', dataType: odbc.SQL_VARCHAR }, - { name: 'TABLE_SCHEM', dataType: odbc.SQL_VARCHAR }, - { name: 'TABLE_NAME', dataType: odbc.SQL_VARCHAR }, - { name: 'TABLE_TYPE', dataType: odbc.SQL_VARCHAR }, - { name: 'REMARKS', dataType: odbc.SQL_VARCHAR }, + { name: 'TABLE_CAT', dataType: nativeOdbc.SQL_VARCHAR }, + { name: 'TABLE_SCHEM', dataType: nativeOdbc.SQL_VARCHAR }, + { name: 'TABLE_NAME', dataType: nativeOdbc.SQL_VARCHAR }, + { name: 'TABLE_TYPE', dataType: nativeOdbc.SQL_VARCHAR }, + { name: 'REMARKS', dataType: nativeOdbc.SQL_VARCHAR }, ]); const result = results[0]; // not testing for TABLE_CAT, dependent on the system @@ -70,17 +74,17 @@ describe('.tables(catalog, schema, table, type, callback)...', () => { // not testing for REMARKS, dependent on the system }); it('...should return empty with bad parameters.', async () => { - const connection = new Connection(`${process.env.CONNECTION_STRING}`); + const connection = await odbc.connect(`${process.env.CONNECTION_STRING}`); const results = await connection.tables(null, 'bad schema name', 'bad table name', null); assert.strictEqual(results.length, 0); assert.strictEqual(results.count, 0); assert.deepStrictEqual(results.columns, [ - { name: 'TABLE_CAT', dataType: odbc.SQL_VARCHAR }, - { name: 'TABLE_SCHEM', dataType: odbc.SQL_VARCHAR }, - { name: 'TABLE_NAME', dataType: odbc.SQL_VARCHAR }, - { name: 'TABLE_TYPE', dataType: odbc.SQL_VARCHAR }, - { name: 'REMARKS', dataType: odbc.SQL_VARCHAR }, + { name: 'TABLE_CAT', dataType: nativeOdbc.SQL_VARCHAR }, + { name: 'TABLE_SCHEM', dataType: nativeOdbc.SQL_VARCHAR }, + { name: 'TABLE_NAME', dataType: nativeOdbc.SQL_VARCHAR }, + { name: 'TABLE_TYPE', dataType: nativeOdbc.SQL_VARCHAR }, + { name: 'REMARKS', dataType: nativeOdbc.SQL_VARCHAR }, ]); }); }); // ...with promises... diff --git a/test/pool/close.js b/test/pool/close.js index e69de29b..fa729792 100644 --- a/test/pool/close.js +++ b/test/pool/close.js @@ -0,0 +1,31 @@ +/* eslint-env node, mocha */ + +require('dotenv').config(); +const assert = require('assert'); +const odbc = require('../../'); + +describe('close()...', () => { + describe('...with callbacks...', () => { + it('...should close all connections in the Pool.', (done) => { + odbc.pool(`${process.env.CONNECTION_STRING}`, (error1, pool) => { + assert.deepEqual(error1, null); + assert.notDeepEqual(pool, null); + setTimeout(() => { + assert.deepEqual(pool.freeConnections.length, 10); + pool.close((error2) => { + assert.deepEqual(error2, null); + assert.deepEqual(pool.freeConnections.length, 0); + done(); + }); + }, 5000); + }); + }); + }); // ...with callbacks... + describe('...with promises...', () => { + it('...should close all connections in the Pool.', async () => { + const pool = await odbc.pool(`${process.env.CONNECTION_STRING}`); + await pool.close(); + assert.deepEqual(pool.freeConnections.length, 0); + }); + }); // ...with promises... +}); diff --git a/test/pool/constructor.js b/test/pool/constructor.js index c58a30fb..ab56f845 100644 --- a/test/pool/constructor.js +++ b/test/pool/constructor.js @@ -2,10 +2,73 @@ require('dotenv').config(); const assert = require('assert'); -const { Pool } = require('../../'); +const odbc = require('../../'); +const { Connection } = require('../../lib/Connection'); -describe('constructor...', () => { - it('...should return the number of open Connections as was requested.', async () => { - const pool = new Pool(`${process.env.CONNECTION_STRING}`); - }); +describe('odbc.pool...', () => { + describe('...with callbacks...', () => { + it('...should return the default number of open connections when no config passed.', (done) => { + odbc.pool(`${process.env.CONNECTION_STRING}`, (error, pool) => { + assert.deepEqual(error, null); + assert.notDeepEqual(pool, null); + setTimeout(() => { + assert.deepEqual(pool.freeConnections.length, 10); + pool.close(); + done(); + }, 5000); + }); + }); + it('...should open as many connections as passed with `initialSize` key...', (done) => { + const poolConfig = { + connectionString: `${process.env.CONNECTION_STRING}`, + initialSize: 5, + }; + odbc.pool(poolConfig, (error, pool) => { + assert.deepEqual(error, null); + assert.notDeepEqual(pool, null); + setTimeout(() => { + assert.deepEqual(pool.freeConnections.length, 5); + pool.close(); + done(); + }, 3000); + }); + }); + it('...should have at least one free connection when .connect is called', (done) => { + odbc.pool(`${process.env.CONNECTION_STRING}`, (error1, pool) => { + assert.deepEqual(error1, null); + pool.connect((error2, connection) => { + assert.deepEqual(error2, null); + assert.deepEqual(connection instanceof Connection, true); + done(); + }); + }); + }); + }); // ...with callbacks... + describe('...with promises...', () => { + it('...should return the default number of open connections when no config passed.', async () => { + const pool = await odbc.pool(`${process.env.CONNECTION_STRING}`); + assert.notDeepEqual(pool, null); + setTimeout(() => { + assert.deepEqual(pool.freeConnections.length, 10); + pool.close(); + }, 5000); + }); + it('...should open as many connections as passed with `initialSize` key...', async () => { + const poolConfig = { + connectionString: `${process.env.CONNECTION_STRING}`, + initialSize: 5, + }; + const pool = await odbc.pool(poolConfig); + assert.notDeepEqual(pool, null); + setTimeout(() => { + assert.deepEqual(pool.freeConnections.length, 5); + pool.close(); + }, 3000); + }); + it('...should have at least one free connection when .connect is called', async () => { + const pool = await odbc.pool(`${process.env.CONNECTION_STRING}`); + const connection = await pool.connect(); + assert.deepEqual(connection instanceof Connection, true); + }); + }); // ...with promises... }); diff --git a/test/pool/init.js b/test/pool/init.js deleted file mode 100644 index 268e9411..00000000 --- a/test/pool/init.js +++ /dev/null @@ -1,28 +0,0 @@ -/* eslint-env node, mocha */ - -require('dotenv').config(); -const assert = require('assert'); -const { Pool } = require('../../'); - -describe('.init([callback])...', () => { - it('...with callbacks...', () => { - it('...should create the number of connections passed to the constructor.', () => { - - }); - it('...should create open connections.', () => { - - }); - it('...should not create connections with a bad connection string.', () => { - - }); - it('...should not create connections if a bad number of initial connections is created.', () => { - - }); - }); - it('...should t', async () => { - const pool = new Pool(`${process.env.CONNECTION_STRING}`); - pool.init((error1) => { - assert.deepEqual(error1, null); - }); - }); -}); diff --git a/test/pool/test.js b/test/pool/test.js index 06ec052f..ad8031b8 100644 --- a/test/pool/test.js +++ b/test/pool/test.js @@ -3,7 +3,6 @@ describe('Pool', () => { require('./constructor.js'); - require('./init.js'); require('./connect.js'); require('./query.js'); require('./close.js'); diff --git a/test/queries/test.js b/test/queries/test.js index 666cf26b..fc77ce39 100644 --- a/test/queries/test.js +++ b/test/queries/test.js @@ -2,13 +2,13 @@ require('dotenv').config(); const assert = require('assert'); -const { Connection } = require('../../'); +const odbc = require('../../'); describe('Queries...', () => { let connection = null; - beforeEach(() => { - connection = new Connection(`${process.env.CONNECTION_STRING}`); + beforeEach(async () => { + connection = await odbc.connect(`${process.env.CONNECTION_STRING}`); }); afterEach(async () => { diff --git a/test/statement/bind.js b/test/statement/bind.js index 2fcf97d5..fff7d735 100644 --- a/test/statement/bind.js +++ b/test/statement/bind.js @@ -2,13 +2,21 @@ require('dotenv').config(); const assert = require('assert'); -const { Connection } = require('../../'); - -const connection = new Connection(`${process.env.CONNECTION_STRING}`); +const odbc = require('../../'); describe('.bind(parameters, [calback])...', () => { + let connection = null; + + beforeEach(async () => { + connection = await odbc.connect(`${process.env.CONNECTION_STRING}`); + }); + + afterEach(async () => { + await connection.close(); + connection = null; + }); + it('...should throw a TypeError if function signature doesn\'t match accepted signatures.', async () => { - // const connection = new Connection(`${process.env.CONNECTION_STRING}`); const statement = await connection.createStatement(); const BIND_TYPE_ERROR = { @@ -59,7 +67,6 @@ describe('.bind(parameters, [calback])...', () => { }); describe('...with callbacks...', () => { it('...should bind if a valid SQL string has been prepared.', (done) => { - // const connection = new Connection(`${process.env.CONNECTION_STRING}`); connection.createStatement((error1, statement) => { assert.deepEqual(error1, null); assert.notDeepEqual(statement, null); @@ -73,7 +80,6 @@ describe('.bind(parameters, [calback])...', () => { }); }); it('...should bind even if an parameter types are invalid for the prepared SQL statement', (done) => { - // const connection = new Connection(`${process.env.CONNECTION_STRING}`); connection.createStatement((error1, statement) => { assert.deepEqual(error1, null); assert.notDeepEqual(statement, null); @@ -87,7 +93,6 @@ describe('.bind(parameters, [calback])...', () => { }); }); it('...should not bind if an invalid SQL statement has been prepared (> 0 parameters).', (done) => { - // const connection = new Connection(`${process.env.CONNECTION_STRING}`); connection.createStatement((error1, statement) => { assert.deepEqual(error1, null); assert.notDeepEqual(statement, null); @@ -103,7 +108,6 @@ describe('.bind(parameters, [calback])...', () => { }); }); it('...should not bind if an invalid SQL statement has been prepared (0 parameters).', (done) => { - // const connection = new Connection(`${process.env.CONNECTION_STRING}`); connection.createStatement((error1, statement) => { assert.deepEqual(error1, null); assert.notDeepEqual(statement, null); @@ -119,7 +123,6 @@ describe('.bind(parameters, [calback])...', () => { }); }); it('...should not bind if there is an incorrect number of parameters for the prepared SQL statement', (done) => { - // const connection = new Connection(`${process.env.CONNECTION_STRING}`); connection.createStatement((error1, statement) => { assert.deepEqual(error1, null); assert.notDeepEqual(statement, null); @@ -134,7 +137,6 @@ describe('.bind(parameters, [calback])...', () => { }); }); it('...should not bind if statement.prepare({string}, {function}[optional]) has not yet been called (> 0 parameters).', (done) => { - // const connection = new Connection(`${process.env.CONNECTION_STRING}`); connection.createStatement((error1, statement) => { assert.deepEqual(error1, null); assert.notDeepEqual(statement, null); @@ -146,7 +148,6 @@ describe('.bind(parameters, [calback])...', () => { }); }); it('...should not bind if statement.prepare({string}, {function}[optional]) has not yet been called (0 parameters).', (done) => { - // const connection = new Connection(`${process.env.CONNECTION_STRING}`); connection.createStatement((error1, statement) => { assert.deepEqual(error1, null); assert.notDeepEqual(statement, null); @@ -160,7 +161,7 @@ describe('.bind(parameters, [calback])...', () => { }); // '...with callbacks...' describe('...with promises...', () => { it('...should bind if a valid SQL string has been prepared.', async () => { - assert.doesNotReject(async () => { + await assert.doesNotReject(async () => { const statement = await connection.createStatement(); assert.notDeepEqual(statement, null); await statement.prepare(`INSERT INTO ${process.env.DB_SCHEMA}.${process.env.DB_TABLE} VALUES(?, ?, ?)`); @@ -168,7 +169,7 @@ describe('.bind(parameters, [calback])...', () => { }); }); it('...should bind even if an parameter types are invalid for the prepared SQL statement', async () => { - assert.doesNotReject(async () => { + await assert.doesNotReject(async () => { const statement = await connection.createStatement(); assert.notDeepEqual(statement, null); await statement.prepare(`INSERT INTO ${process.env.DB_SCHEMA}.${process.env.DB_TABLE} VALUES(?, ?, ?)`); @@ -203,21 +204,21 @@ describe('.bind(parameters, [calback])...', () => { const statement = await connection.createStatement(); assert.notDeepEqual(statement, null); await statement.prepare(`INSERT INTO ${process.env.DB_SCHEMA}.${process.env.DB_TABLE} VALUES(?, ?, ?)`); - assert.rejects(async () => { + await assert.rejects(async () => { await statement.bind([1, 'bound']); }); }); it('...should not bind if statement.prepare({string}, {function}[optional]) has not yet been called (> 0 parameters).', async () => { const statement = await connection.createStatement(); assert.notDeepEqual(statement, null); - assert.rejects(async () => { + await assert.rejects(async () => { await statement.bind([1, 'bound', 10]); }); }); it('...should not bind if statement.prepare({string}, {function}[optional]) has not yet been called (0 parameters).', async () => { const statement = await connection.createStatement(); assert.notDeepEqual(statement, null); - assert.rejects(async () => { + await assert.rejects(async () => { await statement.bind([]); }); }); diff --git a/test/statement/close.js b/test/statement/close.js index 544d05b7..5c3b9797 100644 --- a/test/statement/close.js +++ b/test/statement/close.js @@ -2,13 +2,13 @@ require('dotenv').config(); const assert = require('assert'); -const { Connection } = require('../../'); +const odbc = require('../../'); describe('.close([calback])...', () => { let connection = null; - beforeEach(() => { - connection = new Connection(`${process.env.CONNECTION_STRING}`); + beforeEach(async () => { + connection = await odbc.connect(`${process.env.CONNECTION_STRING}`); }); afterEach(async () => { diff --git a/test/statement/execute.js b/test/statement/execute.js index 7e2d0678..8714aa2d 100644 --- a/test/statement/execute.js +++ b/test/statement/execute.js @@ -2,13 +2,13 @@ require('dotenv').config(); const assert = require('assert'); -const { Connection } = require('../../'); +const odbc = require('../../'); describe('.execute([calback])...', () => { let connection = null; - beforeEach(() => { - connection = new Connection(`${process.env.CONNECTION_STRING}`); + beforeEach(async () => { + connection = await odbc.connect(`${process.env.CONNECTION_STRING}`); }); afterEach(async () => { diff --git a/test/statement/prepare.js b/test/statement/prepare.js index 382430da..cbb078a5 100644 --- a/test/statement/prepare.js +++ b/test/statement/prepare.js @@ -2,13 +2,11 @@ require('dotenv').config(); const assert = require('assert'); -const { Connection } = require('../../'); - -// const connection = new Connection(`${process.env.CONNECTION_STRING}`); +const odbc = require('../../'); describe('.prepare(sql, [calback])...', () => { it('...should throw a TypeError if function signature doesn\'t match accepted signatures.', async () => { - const connection = new Connection(`${process.env.CONNECTION_STRING}`); + const connection = await odbc.connect(`${process.env.CONNECTION_STRING}`); const statement = await connection.createStatement(); const PREPARE_TYPE_ERROR = { @@ -52,45 +50,51 @@ describe('.prepare(sql, [calback])...', () => { }); describe('...with callbacks...', () => { it('...should prepare a valid SQL string', (done) => { - const connection = new Connection(`${process.env.CONNECTION_STRING}`); - connection.createStatement((error1, statement) => { - assert.deepEqual(error1, null); - assert.notDeepEqual(statement, null); - statement.prepare(`INSERT INTO ${process.env.DB_SCHEMA}.${process.env.DB_TABLE} VALUES(?, ?, ?)`, (error2) => { - assert.deepEqual(error2, null); - connection.close((error3) => { - assert.deepEqual(error3, null); - done(); + odbc.connect(`${process.env.CONNECTION_STRING}`, (error, connection) => { + assert.deepEqual(error, null); + connection.createStatement((error1, statement) => { + assert.deepEqual(error1, null); + assert.notDeepEqual(statement, null); + statement.prepare(`INSERT INTO ${process.env.DB_SCHEMA}.${process.env.DB_TABLE} VALUES(?, ?, ?)`, (error2) => { + assert.deepEqual(error2, null); + connection.close((error3) => { + assert.deepEqual(error3, null); + done(); + }); }); }); }); }); it('...should return an error with an invalid SQL string', (done) => { - const connection = new Connection(`${process.env.CONNECTION_STRING}`); - connection.createStatement((error1, statement) => { - assert.deepEqual(error1, null); - assert.notDeepEqual(statement, null); - statement.prepare('INSERT INTO dummy123.table456 VALUES()', (error2) => { - assert.notDeepEqual(error2, null); - assert.deepEqual(error2 instanceof Error, true); - connection.close((error3) => { - assert.deepEqual(error3, null); - done(); + odbc.connect(`${process.env.CONNECTION_STRING}`, (error, connection) => { + assert.deepEqual(error, null); + connection.createStatement((error1, statement) => { + assert.deepEqual(error1, null); + assert.notDeepEqual(statement, null); + statement.prepare('INSERT INTO dummy123.table456 VALUES()', (error2) => { + assert.notDeepEqual(error2, null); + assert.deepEqual(error2 instanceof Error, true); + connection.close((error3) => { + assert.deepEqual(error3, null); + done(); + }); }); }); }); }); it('...should return an error with a blank SQL string', (done) => { - const connection = new Connection(`${process.env.CONNECTION_STRING}`); - connection.createStatement((error1, statement) => { - assert.deepEqual(error1, null); - assert.notDeepEqual(statement, null); - statement.prepare('', (error2) => { - assert.notDeepEqual(error2, null); - assert.deepEqual(error2 instanceof Error, true); - connection.close((error3) => { - assert.deepEqual(error3, null); - done(); + odbc.connect(`${process.env.CONNECTION_STRING}`, (error, connection) => { + assert.deepEqual(error, null); + connection.createStatement((error1, statement) => { + assert.deepEqual(error1, null); + assert.notDeepEqual(statement, null); + statement.prepare('', (error2) => { + assert.notDeepEqual(error2, null); + assert.deepEqual(error2 instanceof Error, true); + connection.close((error3) => { + assert.deepEqual(error3, null); + done(); + }); }); }); }); @@ -98,7 +102,7 @@ describe('.prepare(sql, [calback])...', () => { }); // '...with callbacks...' describe('...with promises...', () => { it('...should prepare a valid SQL string', async () => { - const connection = new Connection(`${process.env.CONNECTION_STRING}`); + const connection = await odbc.connect(`${process.env.CONNECTION_STRING}`); const statement = await connection.createStatement(); assert.doesNotReject(async () => { await statement.prepare(`INSERT INTO ${process.env.DB_SCHEMA}.${process.env.DB_TABLE} VALUES(?, ?, ?)`); @@ -106,7 +110,7 @@ describe('.prepare(sql, [calback])...', () => { }); }); it('...should return an error with an invalid SQL string', async () => { - const connection = new Connection(`${process.env.CONNECTION_STRING}`); + const connection = await odbc.connect(`${process.env.CONNECTION_STRING}`); const statement = await connection.createStatement(); assert.rejects(async () => { await statement.prepare('INSERT INTO dummy123.table456 VALUES()'); @@ -114,7 +118,7 @@ describe('.prepare(sql, [calback])...', () => { }); }); it('...should return an error with a blank SQL string', async () => { - const connection = new Connection(`${process.env.CONNECTION_STRING}`); + const connection = await odbc.connect(`${process.env.CONNECTION_STRING}`); const statement = await connection.createStatement(); assert.rejects(async () => { await statement.prepare(''); diff --git a/test/test.js b/test/test.js index 59e3c962..09a7ce1e 100644 --- a/test/test.js +++ b/test/test.js @@ -1,14 +1,15 @@ /* eslint-env node, mocha */ /* eslint-disable global-require */ -const { Connection } = require('../'); +const odbc = require('../'); const TABLE_EXISTS_STATE = '42S01'; describe('odbc', () => { before(async () => { - const connection = new Connection(`${process.env.CONNECTION_STRING}`); + let connection; try { + connection = await odbc.connect(`${process.env.CONNECTION_STRING}`); await connection.query(`CREATE TABLE ${process.env.DB_SCHEMA}.${process.env.DB_TABLE}(ID INTEGER, NAME VARCHAR(24), AGE INTEGER)`); } catch (error) { const errorJSON = JSON.parse(`{${error.message}}`); @@ -16,23 +17,24 @@ describe('odbc', () => { if (sqlState !== TABLE_EXISTS_STATE) { throw (error); } + } finally { + await connection.close(); } - await connection.close(); }); afterEach(async () => { - const connection = new Connection(`${process.env.CONNECTION_STRING}`); + const connection = await odbc.connect(`${process.env.CONNECTION_STRING}`); try { - await connection.query(`DELETE FROM ${process.env.DB_SCHEMA}.${process.env.DB_TABLE} WHERE 1=1`); + await connection.query(`DELETE FROM ${process.env.DB_SCHEMA}.${process.env.DB_TABLE}`); } catch (error) { - // console.log(error); + // There may be errors if deleting from the table when there are no rows in the table } finally { await connection.close(); } }); after(async () => { - const connection = new Connection(`${process.env.CONNECTION_STRING}`); + const connection = await odbc.connect(`${process.env.CONNECTION_STRING}`); await connection.query(`DROP TABLE ${process.env.DB_SCHEMA}.${process.env.DB_TABLE}`); await connection.close(); });