From 3460abdd967690cf44476d62646947774ba90f3f Mon Sep 17 00:00:00 2001 From: Timothy J Warren Date: Mon, 21 Nov 2016 19:48:00 -0500 Subject: [PATCH 1/3] Documentation updates --- lib/Adapter.js | 2 +- lib/QueryBuilder.js | 10 +++++----- lib/QueryParser.js | 2 +- lib/adapters/MSSQLServer/index.js | 5 +++++ lib/drivers/MSSQLDriver.js | 17 +++++++++++++++++ lib/helpers.js | 2 +- 6 files changed, 30 insertions(+), 8 deletions(-) create mode 100644 lib/drivers/MSSQLDriver.js diff --git a/lib/Adapter.js b/lib/Adapter.js index e0c36ed..772e341 100755 --- a/lib/Adapter.js +++ b/lib/Adapter.js @@ -22,7 +22,7 @@ class Adapter { * * @param {String} sql - The sql with placeholders * @param {Array} params - The values to insert into the query - * @return {Promise} - returns a promise if no callback is passed + * @return {Promise} - returns a promise resolving to the result of the database query */ execute (sql, params) { throw new Error('Correct adapter not defined for query execution'); diff --git a/lib/QueryBuilder.js b/lib/QueryBuilder.js index 268fd10..9460c1d 100755 --- a/lib/QueryBuilder.js +++ b/lib/QueryBuilder.js @@ -490,7 +490,7 @@ class QueryBuilder extends QueryBuilderBase { * @example query.get('table_name').then(promiseCallback); // Get all the rows in the table * @example query.get('table_name', 5); // Get 5 rows from the table * @example query.get(); // Get the results of a query generated with other methods - * @return {void|Promise} - If no callback is passed, a promise is returned + * @return {Promise} - Promise containing the result of the query */ get (table, limit, offset) { if (table) { @@ -510,7 +510,7 @@ class QueryBuilder extends QueryBuilderBase { * * @param {String} table - The table to insert into * @param {Object} [data] - Data to insert, if not already added with the 'set' method - * @return {Promise} - If no callback is passed, a promise is returned + * @return {Promise} - Promise containing the result of the query */ insert (table, data) { if (data) { @@ -528,7 +528,7 @@ class QueryBuilder extends QueryBuilderBase { * @param {Array} data - The array of objects containing data rows to insert * @example query.insertBatch('foo',[{id:1,val:'bar'},{id:2,val:'baz'}]) *.then(promiseCallback); - * @return {Promise} - If no callback is passed, a promise is returned + * @return {Promise} - Promise containing the result of the query */ insertBatch (table, data) { let batch = this.driver.insertBatch(table, data); @@ -542,7 +542,7 @@ class QueryBuilder extends QueryBuilderBase { * * @param {String} table - The table to insert into * @param {Object} [data] - Data to insert, if not already added with the 'set' method - * @return {Promise} - If no callback is passed, a promise is returned + * @return {Promise} - Promise containing the result of the query */ update (table, data) { if (data) { @@ -558,7 +558,7 @@ class QueryBuilder extends QueryBuilderBase { * * @param {String} table - The table to insert into * @param {Object} [where] - Where clause for delete statement - * @return {Promise} - If no callback is passed, a promise is returned + * @return {Promise} - Promise containing the result of the query */ delete (table, where) { if (where) { diff --git a/lib/QueryParser.js b/lib/QueryParser.js index e162385..27a140a 100644 --- a/lib/QueryParser.js +++ b/lib/QueryParser.js @@ -115,7 +115,7 @@ class QueryParser { /** * Return the output of the parsing of the join condition * - * @param {String} condition - The join condition to evalate + * @param {String} condition - The join condition to evaluate * @return {String} - The parsed/escaped join condition */ compileJoin (condition) { diff --git a/lib/adapters/MSSQLServer/index.js b/lib/adapters/MSSQLServer/index.js index e69de29..5c41b25 100644 --- a/lib/adapters/MSSQLServer/index.js +++ b/lib/adapters/MSSQLServer/index.js @@ -0,0 +1,5 @@ +'use strict'; + +module.exports = config => { + +}; diff --git a/lib/drivers/MSSQLDriver.js b/lib/drivers/MSSQLDriver.js new file mode 100644 index 0000000..0deab0f --- /dev/null +++ b/lib/drivers/MSSQLDriver.js @@ -0,0 +1,17 @@ +'use strict'; + +/** + * Driver for Microsoft SQL Server databases + * + * @module drivers/MSSQLDriver + */ +module.exports = (() => { + delete require.cache[require.resolve('../Driver')]; + const driver = require('../Driver'); + const helpers = require('../helpers'); + + driver.identifierStartChar = '['; + driver.identifierEndChar = ']'; + + return driver; +})(); diff --git a/lib/helpers.js b/lib/helpers.js index afb08d4..8735b80 100755 --- a/lib/helpers.js +++ b/lib/helpers.js @@ -5,7 +5,7 @@ * * @private */ -let helpers = { +const helpers = { /** * Wrap String.prototype.trim in a way that is easily mappable * From 7f22eee84db05944a29aff75b74b87e3641f1dbc Mon Sep 17 00:00:00 2001 From: Timothy Warren Date: Tue, 22 Nov 2016 16:03:46 -0500 Subject: [PATCH 2/3] Make helpers a class of static functions, add helper methods to run a full file of sql queries --- docker-compose.yml | 33 +++++++++ lib/{helpers.js => Helpers.js} | 67 +++++++++++++------ lib/QueryBuilder.js | 32 +++++++-- lib/adapters/Mysql/mysql2.js | 4 +- lib/adapters/Pg/Pg.js | 6 +- lib/adapters/Sqlite/dblite.js | 4 +- lib/adapters/Sqlite/sqlite3.js | 4 +- ...irebird_test.js => 00node-firebirdtest.js} | 0 test/adapters/mysql2_test.js | 6 ++ test/adapters/pg_test.js | 6 ++ test/helpers_test.js | 42 ++++++------ test/sql/mysql.sql | 18 ++--- test/sql/pgsql.sql | 2 +- 13 files changed, 156 insertions(+), 68 deletions(-) create mode 100644 docker-compose.yml rename lib/{helpers.js => Helpers.js} (73%) rename test/adapters/{00node-firebird_test.js => 00node-firebirdtest.js} (100%) diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..d1dfc15 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,33 @@ +mariadb: + image: mariadb:latest + environment: + - MYSQL_USER=test + - MYSQL_PASSWORD=test + - MYSQL_DATABASE=test + - MYSQL_RANDOM_ROOT_PASSWORD=yes + ports: + - 3306:3306 + +postgresql: + image: postgres:latest + environment: + - POSTGRES_USER=test + - POSTGRES_PASSWORD=test + - POSTGRES_DB=test + ports: + - 5432:5432 + +sqlserver: + image: microsoft/mssql-server-linux + environment: + - ACCEPT_EULA=Y + - SA_PASSWORD=t3571ng0n1y + ports: + - 1433:1433 + +firebird: + image: itherz/firebird3:latest + ports: + - 5040:5040 + volumes: + - ./test:/databases diff --git a/lib/helpers.js b/lib/Helpers.js similarity index 73% rename from lib/helpers.js rename to lib/Helpers.js index 8735b80..f5ff917 100755 --- a/lib/helpers.js +++ b/lib/Helpers.js @@ -1,18 +1,40 @@ 'use strict'; +const fs = require('fs'); + /** * Various internal helper functions * * @private */ -const helpers = { +class Helpers { + /** + * Get the contents of a file + * + * @param {string} file - The path to the file + * @return {Promise} - Promise resolving to the contents of the file + */ + static readFile (file) { + return new Promise((resolve, reject) => { + fs.readFile(file, (err, data) => { + if (err) { + return reject(err); + } + return resolve(Buffer.from(data).toString()); + }); + }); + } + /** * Wrap String.prototype.trim in a way that is easily mappable * * @param {String} str - The string to trim * @return {String} - The trimmed string */ - stringTrim: str => str.trim(), + static stringTrim (str) { + return str.trim(); + } + /** * Get the type of the variable passed * @@ -21,7 +43,7 @@ const helpers = { * @param {mixed} o - Object to type check * @return {String} - Type of the object */ - type: o => { + static type (o) { let type = Object.prototype.toString.call(o).slice(8, -1).toLowerCase(); // handle NaN and Infinity @@ -36,17 +58,19 @@ const helpers = { } return type; - }, + } + /** * Determine whether an object is scalar * * @param {mixed} obj - Object to test * @return {bool} - Is object scalar */ - isScalar: obj => { + static isScalar (obj) { let scalar = ['string', 'number', 'boolean']; - return scalar.indexOf(helpers.type(obj)) !== -1; - }, + return scalar.indexOf(Helpers.type(obj)) !== -1; + } + /** * Get a list of values with a common key from an array of objects * @@ -54,7 +78,7 @@ const helpers = { * @param {String} key - The key of the object to get * @return {Array} - The new array of plucked values */ - arrayPluck: (arr, key) => { + static arrayPluck (arr, key) { let output = []; // Empty case @@ -63,13 +87,14 @@ const helpers = { } arr.forEach(obj => { - if (!helpers.isUndefined(obj[key])) { + if (!Helpers.isUndefined(obj[key])) { output.push(obj[key]); } }); return output; - }, + } + /** * Determine if a value matching the passed regular expression is * in the passed array @@ -78,9 +103,9 @@ const helpers = { * @param {RegExp} pattern - The pattern to match * @return {Boolean} - If an array item matches the pattern */ - regexInArray: (arr, pattern) => { + static regexInArray (arr, pattern) { // Empty case(s) - if (!helpers.isArray(arr) || arr.length === 0) { + if (!Helpers.isArray(arr) || arr.length === 0) { return false; } @@ -93,19 +118,20 @@ const helpers = { } return false; - }, + } + /** * Make the first letter of the string uppercase * * @param {String} str - The string to modify * @return {String} - The modified string */ - upperCaseFirst: str => { + static upperCaseFirst (str) { str += ''; let first = str.charAt(0).toUpperCase(); return first + str.substr(1); } -}; +} // Define an 'is' method for each type let types = [ @@ -119,7 +145,8 @@ let types = [ 'Function', 'RegExp', 'NaN', - 'Infinite' + 'Infinite', + 'Promise' ]; types.forEach(t => { /** @@ -127,19 +154,19 @@ types.forEach(t => { * function name, eg isNumber * * Types available are Null, Undefined, Object, Array, String, Number, - * Boolean, Function, RegExp, NaN and Infinite + * Boolean, Function, RegExp, NaN, Infinite, Promise * * @private * @param {mixed} o - The object to check its type * @return {Boolean} - If the type matches */ - helpers[`is${t}`] = function (o) { + Helpers[`is${t}`] = function (o) { if (t.toLowerCase() === 'infinite') { t = 'infinity'; } - return helpers.type(o) === t.toLowerCase(); + return Helpers.type(o) === t.toLowerCase(); }; }); -module.exports = helpers; +module.exports = Helpers; diff --git a/lib/QueryBuilder.js b/lib/QueryBuilder.js index 9460c1d..03f69e5 100755 --- a/lib/QueryBuilder.js +++ b/lib/QueryBuilder.js @@ -1,6 +1,6 @@ 'use strict'; -const helpers = require('./helpers'); +const Helpers = require('./Helpers'); const QueryBuilderBase = require('./QueryBuilderBase'); /** @@ -15,6 +15,26 @@ class QueryBuilder extends QueryBuilderBase { // ! Miscellaneous Methods // ---------------------------------------------------------------------------- + /** + * Run a set of queries from a file + * + * @param {string} file - The path to the sql file + * @param {string} [separator=';'] - The character separating each query + * @return {Promise} - The result of all the queries + */ + queryFile (file, separator = ';') { + return Helpers.readFile(file).then(sqlFile => { + const queries = sqlFile.split(separator); + const results = []; + + queries.forEach(sql => { + results.push(this.query(sql)); + }); + + return Promise.all(results); + }); + } + /** * Run an arbitrary sql query. Run as a prepared statement. * @@ -80,12 +100,12 @@ class QueryBuilder extends QueryBuilderBase { // Split/trim fields by comma fields = (Array.isArray(fields)) ? fields - : fields.split(',').map(helpers.stringTrim); + : fields.split(',').map(Helpers.stringTrim); // Split on 'As' fields.forEach((field, index) => { if (/as/i.test(field)) { - fields[index] = field.split(/ as /i).map(helpers.stringTrim); + fields[index] = field.split(/ as /i).map(Helpers.stringTrim); } }); @@ -113,7 +133,7 @@ class QueryBuilder extends QueryBuilderBase { */ from (tableName) { // Split identifiers on spaces - let identArray = tableName.trim().split(' ').map(helpers.stringTrim); + let identArray = tableName.trim().split(' ').map(Helpers.stringTrim); // Quote/prefix identifiers identArray[0] = this.driver.quoteTable(identArray[0]); @@ -354,7 +374,7 @@ class QueryBuilder extends QueryBuilderBase { type = type || 'inner'; // Prefix/quote table name - table = table.split(' ').map(helpers.stringTrim); + table = table.split(' ').map(Helpers.stringTrim); table[0] = this.driver.quoteTable(table[0]); table = table.map(this.driver.quoteIdentifiers); table = table.join(' '); @@ -376,7 +396,7 @@ class QueryBuilder extends QueryBuilderBase { * @return {QueryBuilder} - The Query Builder object, for chaining */ groupBy (field) { - if (!helpers.isScalar(field)) { + if (!Helpers.isScalar(field)) { let newGroupArray = field.map(this.driver.quoteIdentifiers); this.state.groupArray = this.state.groupArray.concat(newGroupArray); } else { diff --git a/lib/adapters/Mysql/mysql2.js b/lib/adapters/Mysql/mysql2.js index 438c3f3..c13483c 100644 --- a/lib/adapters/Mysql/mysql2.js +++ b/lib/adapters/Mysql/mysql2.js @@ -2,7 +2,7 @@ const Adapter = require('../../Adapter'); const Result = require('../../Result'); -const helpers = require('../../helpers'); +const Helpers = require('../../Helpers'); const mysql2 = require('mysql2/promise'); class Mysql extends Adapter { @@ -22,7 +22,7 @@ class Mysql extends Adapter { // For insert and update queries, the result object // works differently. Just apply the properties of // this special result to the standard result object. - if (helpers.type(result) === 'object') { + if (Helpers.type(result) === 'object') { let r = new Result(); Object.keys(result).forEach(key => { diff --git a/lib/adapters/Pg/Pg.js b/lib/adapters/Pg/Pg.js index b1f37a8..4d50492 100644 --- a/lib/adapters/Pg/Pg.js +++ b/lib/adapters/Pg/Pg.js @@ -2,7 +2,7 @@ const Adapter = require('../../Adapter'); const Result = require('../../Result'); -const helpers = require('../../helpers'); +const Helpers = require('../../Helpers'); const pg = require('pg'); const url = require('url'); @@ -10,7 +10,7 @@ class Pg extends Adapter { constructor (config) { let instance = null; let connectionString = ''; - if (helpers.isObject(config)) { + if (Helpers.isObject(config)) { let host = config.host || 'localhost'; let user = config.user || 'postgres'; let password = `:${config.password}` || ''; @@ -25,7 +25,7 @@ class Pg extends Adapter { }; connectionString = url.format(conn); - } else if (helpers.isString(config)) { + } else if (Helpers.isString(config)) { connectionString = config; } diff --git a/lib/adapters/Sqlite/dblite.js b/lib/adapters/Sqlite/dblite.js index f62bd2c..21af28b 100644 --- a/lib/adapters/Sqlite/dblite.js +++ b/lib/adapters/Sqlite/dblite.js @@ -2,12 +2,12 @@ const Adapter = require('../../Adapter'); const Result = require('../../Result'); -const helpers = require('../../helpers'); +const Helpers = require('../../Helpers'); const dbliteAdapter = require('dblite'); class SqliteDblite extends Adapter { constructor (config) { - let file = (helpers.isString(config)) ? config : config.file; + let file = (Helpers.isString(config)) ? config : config.file; const instance = new Promise((resolve, reject) => { let conn = dbliteAdapter(file); diff --git a/lib/adapters/Sqlite/sqlite3.js b/lib/adapters/Sqlite/sqlite3.js index b80bc00..4c0529a 100644 --- a/lib/adapters/Sqlite/sqlite3.js +++ b/lib/adapters/Sqlite/sqlite3.js @@ -2,12 +2,12 @@ const Adapter = require('../../Adapter'); const Result = require('../../Result'); -const helpers = require('../../helpers'); +const Helpers = require('../../Helpers'); const sqlite3 = require('sqlite3').verbose(); class SqliteSqlite3 extends Adapter { constructor (config) { - let file = (helpers.isString(config)) ? config : config.file; + let file = (Helpers.isString(config)) ? config : config.file; const instance = new Promise((resolve, reject) => { let conn = new sqlite3.Database(file, sqlite3.OPEN_READWRITE | sqlite3.OPEN_CREATE, err => { diff --git a/test/adapters/00node-firebird_test.js b/test/adapters/00node-firebirdtest.js similarity index 100% rename from test/adapters/00node-firebird_test.js rename to test/adapters/00node-firebirdtest.js diff --git a/test/adapters/mysql2_test.js b/test/adapters/mysql2_test.js index ea87a72..0d85888 100644 --- a/test/adapters/mysql2_test.js +++ b/test/adapters/mysql2_test.js @@ -17,6 +17,12 @@ let nodeQuery = reload('../../lib/NodeQuery')(config); let qb = nodeQuery.getQuery(); suite('Mysql2 adapter tests -', () => { + suiteSetup(done => { + qb.queryFile(`${__dirname}/../sql/mysql.sql`) + .then(() => done()) + .catch(e => done(e)); + }); + test('nodeQuery.getQuery = nodeQuery.init', () => { expect(nodeQuery.getQuery()) .to.be.deep.equal(qb); diff --git a/test/adapters/pg_test.js b/test/adapters/pg_test.js index 3a6d3e6..77a4470 100644 --- a/test/adapters/pg_test.js +++ b/test/adapters/pg_test.js @@ -19,6 +19,12 @@ let qb = nodeQuery.getQuery(); let qb2 = null; suite('Pg adapter tests -', () => { + suiteSetup(done => { + qb.queryFile(`${__dirname}/../sql/pgsql.sql`) + .then(() => done()) + .catch(e => done(e)); + }); + test('nodeQuery.getQuery = nodeQuery.init', () => { expect(nodeQuery.getQuery()) .to.be.deep.equal(qb); diff --git a/test/helpers_test.js b/test/helpers_test.js index f44d4e2..9e81967 100644 --- a/test/helpers_test.js +++ b/test/helpers_test.js @@ -5,22 +5,22 @@ const chai = require('chai'); const assert = chai.assert; const expect = chai.expect; -let helpers = require('../lib/helpers'); +let Helpers = require('../lib/Helpers'); suite('Helper Module Tests -', () => { suite('Type-checking methods -', () => { suite('Object wrappers are listed as their native type', () => { test('Boolean Wrapper returns \'boolean\' not \'object\'', () => { let item = Boolean(true); - expect(helpers.type(item)).to.deep.equal('boolean'); + expect(Helpers.type(item)).to.deep.equal('boolean'); }); test('Number Wrapper returns \'number\' not \'object\'', () => { let item = Number(4867); - expect(helpers.type(item)).to.deep.equal('number'); + expect(Helpers.type(item)).to.deep.equal('number'); }); test('String Wrapper returns \'string\' not \'object\'', () => { let item = String('Foo'); - expect(helpers.type(item)).to.deep.equal('string'); + expect(Helpers.type(item)).to.deep.equal('string'); }); }); suite('is..Method methods exist -', () => { @@ -40,7 +40,7 @@ suite('Helper Module Tests -', () => { types.forEach(type => { test(`is${type} method exists`, () => { - assert.ok(helpers[`is${type}`]); + assert.ok(Helpers[`is${type}`]); }); }); }); @@ -52,7 +52,7 @@ suite('Helper Module Tests -', () => { }; Object.keys(trueCases).forEach(desc => { test(desc, () => { - expect(helpers.isScalar(trueCases[desc])).to.be.true; + expect(Helpers.isScalar(trueCases[desc])).to.be.true; }); }); @@ -62,24 +62,24 @@ suite('Helper Module Tests -', () => { }; Object.keys(falseCases).forEach(desc => { test(desc, () => { - expect(helpers.isScalar(falseCases[desc])).to.be.false; + expect(Helpers.isScalar(falseCases[desc])).to.be.false; }); }); }); suite('isInfinity -', () => { test('The type of 1/0 is infinity', () => { - expect(helpers.type(1 / 0)).to.equal('infinity'); + expect(Helpers.type(1 / 0)).to.equal('infinity'); }); test('isInfinity is the same as isInfinite', () => { - expect(helpers.isInfinite(1 / 0)).to.be.true; + expect(Helpers.isInfinite(1 / 0)).to.be.true; }); }); suite('isNaN -', () => { test('The type of 0 / 0 is NaN', () => { - expect(helpers.type(0 / 0)).to.equal('nan'); + expect(Helpers.type(0 / 0)).to.equal('nan'); }); test('isNaN method agrees with type', () => { - expect(helpers.isNaN(0 / 0)).to.be.true; + expect(Helpers.isNaN(0 / 0)).to.be.true; }); }); }); @@ -89,7 +89,7 @@ suite('Helper Module Tests -', () => { let orig = [' x y ', 'z ', ' q']; let ret = ['x y', 'z', 'q']; - expect(orig.map(helpers.stringTrim)).to.be.deep.equal(ret); + expect(orig.map(Helpers.stringTrim)).to.be.deep.equal(ret); }); }); suite('arrayPluck -', () => { @@ -106,13 +106,13 @@ suite('Helper Module Tests -', () => { ]; test('Finding members in all objects', () => { - expect(helpers.arrayPluck(orig, 'foo')).to.be.deep.equal([1, 2, 3]); + expect(Helpers.arrayPluck(orig, 'foo')).to.be.deep.equal([1, 2, 3]); }); test('Some members are missing in some objects', () => { - expect(helpers.arrayPluck(orig, 'bar')).to.be.deep.equal([10, 15]); + expect(Helpers.arrayPluck(orig, 'bar')).to.be.deep.equal([10, 15]); }); test('Empty case', () => { - expect(helpers.arrayPluck([], 'apple')).to.be.deep.equal([]); + expect(Helpers.arrayPluck([], 'apple')).to.be.deep.equal([]); }); }); suite('regexInArray -', () => { @@ -133,25 +133,25 @@ suite('Helper Module Tests -', () => { Object.keys(boolCase).forEach(desc => { test(desc, () => { if (i) { - expect(helpers.regexInArray(orig, boolCase[desc])).to.be.true; + expect(Helpers.regexInArray(orig, boolCase[desc])).to.be.true; } else { - expect(helpers.regexInArray(orig, boolCase[desc])).to.be.false; + expect(Helpers.regexInArray(orig, boolCase[desc])).to.be.false; } }); }); }); test('First argument is not an array', () => { - expect(helpers.regexInArray(5, /5/)).to.be.false; + expect(Helpers.regexInArray(5, /5/)).to.be.false; }); test('Array is empty', () => { - expect(helpers.regexInArray([], /.*/)).to.be.false; + expect(Helpers.regexInArray([], /.*/)).to.be.false; }); }); suite('upperCaseFirst -', () => { test('Capitalizes only the first letter of the string', () => { - expect(helpers.upperCaseFirst('foobar')).to.equal('Foobar'); - expect(helpers.upperCaseFirst('FOOBAR')).to.equal('FOOBAR'); + expect(Helpers.upperCaseFirst('foobar')).to.equal('Foobar'); + expect(Helpers.upperCaseFirst('FOOBAR')).to.equal('FOOBAR'); }); }); }); diff --git a/test/sql/mysql.sql b/test/sql/mysql.sql index 5b06c28..bad4cea 100644 --- a/test/sql/mysql.sql +++ b/test/sql/mysql.sql @@ -3,25 +3,21 @@ -- -- Table structure for table `create_join` -- - DROP TABLE IF EXISTS `create_join` CASCADE; -CREATE TABLE `create_join` ( - `id` int(10) NOT NULL, - `key` text, - `val` text +CREATE TABLE IF NOT EXISTS `create_join` ( + `id` int(10) PRIMARY KEY NOT NULL, + `key` VARCHAR(128), + `val` MEDIUMTEXT ); -ALTER TABLE `create_join` - ADD PRIMARY KEY (`id`); -- -- Table structure for table `create_test` -- - DROP TABLE IF EXISTS `create_test` CASCADE; CREATE TABLE `create_test` ( `id` int(10) NOT NULL, - `key` text, - `val` text + `key` TEXT, + `val` LONGTEXT ); ALTER TABLE `create_test` - ADD PRIMARY KEY (`id`); \ No newline at end of file + ADD PRIMARY KEY (`id`) diff --git a/test/sql/pgsql.sql b/test/sql/pgsql.sql index 09ad873..cad420c 100644 --- a/test/sql/pgsql.sql +++ b/test/sql/pgsql.sql @@ -13,4 +13,4 @@ CREATE TABLE "create_test" ( id integer NOT NULL, key text, val text -); \ No newline at end of file +) From b54e69570f3f6d857f228d666a1def2bed7a165e Mon Sep 17 00:00:00 2001 From: Timothy Warren Date: Tue, 22 Nov 2016 18:26:43 -0500 Subject: [PATCH 3/3] Make 'navtive' a connection option to use a driver with native bindings --- lib/NodeQuery.js | 6 ++++++ lib/adapters/Pg/PgNative.js | 28 ++++++++++++++++++++++++++++ lib/adapters/Pg/index.js | 5 ++++- lib/adapters/Sqlite/index.js | 6 +++--- package.json | 7 ++++++- test/adapters/dblite_test.js | 12 +++--------- test/adapters/sqlite3_test.js | 12 +++--------- 7 files changed, 53 insertions(+), 23 deletions(-) create mode 100644 lib/adapters/Pg/PgNative.js diff --git a/lib/NodeQuery.js b/lib/NodeQuery.js index e64d696..8af3938 100755 --- a/lib/NodeQuery.js +++ b/lib/NodeQuery.js @@ -28,6 +28,12 @@ class NodeQuery { * Constructor * * @param {object} config - connection parameters + * @param {string} config.driver - the database driver to use, such as mysql, sqlite, mssql, or pgsql + * @param {object|string} config.connection - the connection options for the database + * @param {string} config.connection.host - the ip or hostname of the database server + * @param {string} config.connection.user - the user to log in as + * @param {string} config.connection.password - the password to log in with + * @param {string} config.connection.database - the name of the database to connect to * @example let nodeQuery = require('ci-node-query')({ * driver: 'mysql', * connection: { diff --git a/lib/adapters/Pg/PgNative.js b/lib/adapters/Pg/PgNative.js new file mode 100644 index 0000000..c341d26 --- /dev/null +++ b/lib/adapters/Pg/PgNative.js @@ -0,0 +1,28 @@ +'use strict'; + +const Pg = require('./Pg'); +const Result = require('../../Result'); +const pg = require('pg').native; +const url = require('url'); + +class PgNative extends Pg { + constructor (config) { + super(config); + let instance = null; + let connectionString = Pg._formatConnectionString(config); + + if (connectionString !== '') { + let conn = new pg.Client(connectionString); + conn.connect(err => { + if (err) { + throw new Error(err); + } + }); + + instance = Promise.resolve(conn); + } + super(instance); + } +} + +module.exports = PgNative; diff --git a/lib/adapters/Pg/index.js b/lib/adapters/Pg/index.js index c20cdb3..5a940e7 100644 --- a/lib/adapters/Pg/index.js +++ b/lib/adapters/Pg/index.js @@ -1,7 +1,10 @@ 'use strict'; const Pg = require('./Pg'); +const PgNative = require('./PgNative') module.exports = config => { - return new Pg(config.connection); + return (config.native) + ? new PgNative(config.connection) + : new Pg(config.connection); }; diff --git a/lib/adapters/Sqlite/index.js b/lib/adapters/Sqlite/index.js index b4c891c..b59bcf2 100644 --- a/lib/adapters/Sqlite/index.js +++ b/lib/adapters/Sqlite/index.js @@ -1,9 +1,9 @@ 'use strict'; module.exports = config => { - const Implementation = (config.adapter && config.adapter === 'dblite') - ? require('./dblite') - : require('./sqlite3'); + const Implementation = (config.native) + ? require('./sqlite3') + : require('./dblite'); return new Implementation(config.connection); }; diff --git a/package.json b/package.json index 531487a..4336160 100644 --- a/package.json +++ b/package.json @@ -23,12 +23,14 @@ "codeigniter", "mariadb", "mysql", + "mssql", "query builder", "postgres", "postgresql", "sql", "sqlite", - "sqlite3" + "sqlite3", + "sqlserver" ], "bugs": { "url": "https://git.timshomepage.net/timw4mail/node-query/issues" @@ -43,6 +45,7 @@ "pg": "^6.0.0", "require-reload": "~0.2.2", "sqlite3": "^3.1.8", + "tedious": "^1.14.0", "xregexp": "^3.0.0" }, "devDependencies": { @@ -53,6 +56,7 @@ "globstar": "^1.0.0", "happiness": "^7.1.2", "istanbul": "~0.4.2", + "jsdoc": "^3.4.3", "mocha": "^3.0.0", "npm-run-all": "^3.0.0", "nsp": "^2.2.1" @@ -65,6 +69,7 @@ "default": "npm-run-all --parallel audit lint:src lint:tests && npm run test", "predocs": "globstar -- documentation build -f md -o API.md \"lib/*.js\"", "docs": "globstar -- documentation build -f html -o docs \"lib/*.js\"", + "postdocs": "jsdoc lib -r -d documentation", "happy": "happiness \"lib/**/*.js\" \"test/**/*.js\"", "happy:src": "happiness \"lib/**/*.js\"", "happy:tests": "happiness \"test/**/*.js\"", diff --git a/test/adapters/dblite_test.js b/test/adapters/dblite_test.js index 690fc3e..6a53f4e 100644 --- a/test/adapters/dblite_test.js +++ b/test/adapters/dblite_test.js @@ -17,15 +17,9 @@ let qb = nodeQuery.getQuery(); suite('Dblite adapter tests -', () => { suiteSetup(done => { - // Set up the sqlite database - const createTest = 'CREATE TABLE IF NOT EXISTS "create_test" ("id" INTEGER PRIMARY KEY, "key" TEXT, "val" TEXT);'; - const createJoin = 'CREATE TABLE IF NOT EXISTS "create_join" ("id" INTEGER PRIMARY KEY, "key" TEXT, "val" TEXT);'; - - qb.query(createTest) - .then(() => qb.query(createJoin)) - .then(() => { - return done(); - }); + qb.queryFile(`${__dirname}/../sql/sqlite.sql`) + .then(() => done()) + .catch(e => done(e)); }); testRunner(qb); diff --git a/test/adapters/sqlite3_test.js b/test/adapters/sqlite3_test.js index e86f1fc..a7cb8dd 100644 --- a/test/adapters/sqlite3_test.js +++ b/test/adapters/sqlite3_test.js @@ -17,15 +17,9 @@ let qb = nodeQuery.getQuery(); suite('Sqlite3 adapter tests -', () => { suiteSetup(done => { - // Set up the sqlite database - const createTest = 'CREATE TABLE IF NOT EXISTS "create_test" ("id" INTEGER PRIMARY KEY, "key" TEXT, "val" TEXT);'; - const createJoin = 'CREATE TABLE IF NOT EXISTS "create_join" ("id" INTEGER PRIMARY KEY, "key" TEXT, "val" TEXT);'; - - qb.query(createTest) - .then(() => qb.query(createJoin)) - .then(() => { - return done(); - }); + qb.queryFile(`${__dirname}/../sql/sqlite.sql`) + .then(() => done()) + .catch(e => done(e)); }); testRunner(qb);