From 8d7e4aaa8ccecdb81b468c48c7d893658c3bec4f Mon Sep 17 00:00:00 2001 From: Timothy J Warren Date: Wed, 14 Sep 2016 16:50:32 -0400 Subject: [PATCH] Update dependencies and switch to Happiness code style --- .jscsrc | 7 - API.md | 2 + README.md | 2 + lib/Adapter.js | 10 +- lib/Driver.js | 59 ++-- lib/NodeQuery.js | 18 +- lib/QueryBuilder.js | 383 +++---------------------- lib/QueryBuilderBase.js | 288 +++++++++++++++++++ lib/QueryParser.js | 28 +- lib/Result.js | 16 +- lib/State.js | 4 +- lib/adapters/MariaDB.js | 3 + lib/adapters/Mysql.js | 10 +- lib/adapters/Pg.js | 20 +- lib/adapters/Sqlite.js | 10 +- lib/drivers/MariaDB.js | 8 + lib/drivers/Mysql.js | 19 +- lib/drivers/Pg.js | 4 +- lib/drivers/Sqlite.js | 19 +- lib/helpers.js | 21 +- package.json | 30 +- test/adapters/dblite_test.js | 33 +-- test/adapters/mysql2_test.js | 37 ++- test/adapters/pg_test.js | 50 +++- test/base.js | 4 +- test/base/adapterCallbackTestRunner.js | 29 +- test/base/adapterPromiseTestRunner.js | 29 +- test/base/tests.js | 101 ++++--- test/base_test.js | 15 +- test/helpers_test.js | 28 +- test/query-parser_test.js | 43 +-- 31 files changed, 674 insertions(+), 656 deletions(-) delete mode 100644 .jscsrc create mode 100644 lib/QueryBuilderBase.js create mode 100644 lib/adapters/MariaDB.js create mode 100644 lib/drivers/MariaDB.js diff --git a/.jscsrc b/.jscsrc deleted file mode 100644 index 49f6214..0000000 --- a/.jscsrc +++ /dev/null @@ -1,7 +0,0 @@ -{ - "preset": "airbnb", - "validateIndentation": null, - "requireLineFeedAtFileEnd": null, - "disallowSpaceAfterPrefixUnaryOperators": null, - "disallowMultipleVarDecl": null -} \ No newline at end of file diff --git a/API.md b/API.md index a188d98..8c5d836 100644 --- a/API.md +++ b/API.md @@ -1,3 +1,5 @@ + + # limit Set the limit clause diff --git a/README.md b/README.md index 161466d..7b89667 100755 --- a/README.md +++ b/README.md @@ -95,3 +95,5 @@ As of version 2, `where` and `having` type methods parse the values passed to lo * The `tests/adapters` folder contains examples of how to set up a connection for the appropriate database library * The documentation generated for the latest dev build is also [Available](https://github.timshomepage.net/node-query/docs/index.html) +[![js-happiness-style](https://cdn.rawgit.com/JedWatson/happiness/master/badge.svg)](https://github.com/JedWatson/happiness) + diff --git a/lib/Adapter.js b/lib/Adapter.js index 8ac77ac..4bd19b1 100755 --- a/lib/Adapter.js +++ b/lib/Adapter.js @@ -14,7 +14,7 @@ class Adapter { * @constructor * @param {Object} instance - The connection object */ - constructor(instance) { + constructor (instance) { this.instance = instance; } @@ -26,7 +26,7 @@ class Adapter { * @param {Function} [callback] - Callback to run when a response is recieved * @return {void|Promise} - returns a promise if no callback is passed */ - execute(/*sql, params, callback*/) { + execute (/* sql, params, callback */) { throw new Error('Correct adapter not defined for query execution'); } @@ -36,7 +36,7 @@ class Adapter { * @param {*} originalResult - the original result object from the driver * @return {Result} - the new result object */ - transformResult(originalResult) { + transformResult (originalResult) { throw new Error('Result transformer method not defined for current adapter'); } @@ -44,9 +44,9 @@ class Adapter { * Close the current database connection * @return {void} */ - close() { + close () { this.instance.end(); } } -module.exports = Adapter; \ No newline at end of file +module.exports = Adapter; diff --git a/lib/Driver.js b/lib/Driver.js index cac0cd1..54453d9 100755 --- a/lib/Driver.js +++ b/lib/Driver.js @@ -7,7 +7,7 @@ const helpers = require('./helpers'); * * @private */ -let Driver = { +const Driver = { identifierStartChar: '"', identifierEndChar: '"', tablePrefix: null, @@ -20,9 +20,9 @@ let Driver = { * @return {String} - The quoted sql fragment * @private */ - _quote(str) { - return (helpers.isString(str) - && ! (str.startsWith(Driver.identifierStartChar) || str.endsWith(Driver.identifierEndChar)) + _quote (str) { + return (helpers.isString(str) && + !(str.startsWith(Driver.identifierStartChar) || str.endsWith(Driver.identifierEndChar)) ) ? `${Driver.identifierStartChar}${str}${Driver.identifierEndChar}` : str; @@ -36,11 +36,10 @@ let Driver = { * @param {Number} [offset] - Number of rows to skip * @return {String} - Modified SQL statement */ - limit(sql, limit, offset) { - sql += ` LIMIT ${limit}`; + limit (sql, limit, offset) { + sql += ` LIMIT ${limit}`; - if (helpers.isNumber(offset)) - { + if (helpers.isNumber(offset)) { sql += ` OFFSET ${offset}`; } @@ -53,7 +52,7 @@ let Driver = { * @param {String} table - Table name to quote * @return {String} - Quoted table name */ - quoteTable(table) { + quoteTable (table) { // Quote after prefix return Driver.quoteIdentifiers(table); }, @@ -64,22 +63,20 @@ let Driver = { * @param {String|Array} str - String or array of strings to quote identifiers * @return {String|Array} - Quoted identifier(s) */ - quoteIdentifiers(str) { + quoteIdentifiers (str) { let hiers, raw; let pattern = new RegExp( - `${Driver.identifierStartChar}(` - + '([a-zA-Z0-9_]+)' + '(\((.*?)\))' - + `)${Driver.identifierEndChar}`, 'ig'); + `${Driver.identifierStartChar}(` + + '([a-zA-Z0-9_]+)' + '(((.*?)))' + + `)${Driver.identifierEndChar}`, 'ig'); // Recurse for arrays of identifiiers - if (Array.isArray(str)) - { + if (Array.isArray(str)) { return str.map(Driver.quoteIdentifiers); } // Handle commas - if (str.includes(',')) - { + if (str.includes(',')) { let parts = str.split(',').map(helpers.stringTrim); str = parts.map(Driver.quoteIdentifiers).join(','); } @@ -89,8 +86,7 @@ let Driver = { raw = hiers.join('.'); // Fix functions - if (raw.includes('(') && raw.includes(')')) - { + if (raw.includes('(') && raw.includes(')')) { let funcs = pattern.exec(raw); // Unquote the function @@ -110,7 +106,7 @@ let Driver = { * @param {String} table - Table to truncate * @return {String} - Truncation SQL */ - truncate(table) { + truncate (table) { let sql = (Driver.hasTruncate) ? 'TRUNCATE ' : 'DELETE FROM '; @@ -127,13 +123,10 @@ let Driver = { * @param {Array} [data] - The array of object containing data to insert * @return {String} - Query and data to insert */ - insertBatch(table, data) { - let vals = [], - fields = Object.keys(data[0]), - sql = '', - params = [], - paramString = '', - paramList = []; + insertBatch (table, data) { + const vals = []; + const fields = Object.keys(data[0]); + let sql = ''; // Get the data values to insert, so they can // be parameterized @@ -150,17 +143,17 @@ let Driver = { sql += `INSERT INTO ${table} (${Driver.quoteIdentifiers(fields).join(',')}) VALUES `; // Create placeholder groups - params = Array(fields.length).fill('?'); - paramString = `(${params.join(',')})`; - paramList = Array(data.length).fill(paramString); + let params = Array(fields.length).fill('?'); + let paramString = `(${params.join(',')})`; + let paramList = Array(data.length).fill(paramString); sql += paramList.join(','); return { sql: sql, - values: vals, + values: vals }; - }, + } }; -module.exports = Driver; \ No newline at end of file +module.exports = Driver; diff --git a/lib/NodeQuery.js b/lib/NodeQuery.js index 4cedd6c..d32fa57 100755 --- a/lib/NodeQuery.js +++ b/lib/NodeQuery.js @@ -1,6 +1,5 @@ 'use strict'; -const helpers = require('./helpers'); const QueryBuilder = require('./QueryBuilder'); // Map config driver name to code class name @@ -14,7 +13,7 @@ const dbDriverMap = new Map([ ['postgres', 'Pg'], ['pg', 'Pg'], ['sqlite3', 'Sqlite'], - ['sqlite', 'Sqlite'], + ['sqlite', 'Sqlite'] ]); /** @@ -23,7 +22,6 @@ const dbDriverMap = new Map([ * @param {object} config - connection parameters */ class NodeQuery { - /** * Constructor * @@ -42,20 +40,20 @@ class NodeQuery { * connection: ':memory:' * }); */ - constructor(config) { + constructor (config) { this.instance = null; if (config != null) { let drivername = dbDriverMap.get(config.driver); - if (! drivername) { + if (!drivername) { throw new Error(`Selected driver (${config.driver}) does not exist!`); } - let driver = require(`./drivers/${drivername}`); - let $adapter = require(`./adapters/${drivername}`); + const driver = require(`./drivers/${drivername}`); + const Adapter = require(`./adapters/${drivername}`); - let adapter = new $adapter(config.connection); + let adapter = new Adapter(config.connection); this.instance = new QueryBuilder(driver, adapter); } } @@ -65,7 +63,7 @@ class NodeQuery { * * @return {QueryBuilder} - The Query Builder object */ - getQuery() { + getQuery () { if (this.instance == null) { throw new Error('No Query Builder instance to return'); } @@ -74,4 +72,4 @@ class NodeQuery { } } -module.exports = (config => new NodeQuery(config)); \ No newline at end of file +module.exports = config => new NodeQuery(config); diff --git a/lib/QueryBuilder.js b/lib/QueryBuilder.js index 216ef2c..064b981 100755 --- a/lib/QueryBuilder.js +++ b/lib/QueryBuilder.js @@ -2,308 +2,16 @@ const getArgs = require('getargs'); const helpers = require('./helpers'); -const State = require('./State'); -const QueryParser = require('./QueryParser'); - -class QueryBuilderBase { - /** - * @private - * @constructor - * @param {Driver} Driver - The syntax driver for the database - * @param {Adapter} Adapter - The database module adapter for running queries - */ - constructor(Driver, Adapter) { - this.driver = Driver; - this.adapter = Adapter; - this.parser = new QueryParser(this.driver); - this.state = new State(); - } - - /** - * Complete the sql building based on the type provided - * - * @private - * @param {String} type - Type of SQL query - * @param {String} table - The table to run the query on - * @return {String} - The compiled sql - */ - _compile(type, table) { - // Put together the basic query - let sql = this._compileType(type, table); - - // Set each subClause - ['queryMap', 'groupString', 'orderString', 'havingMap'].forEach(clause => { - let param = this.state[clause]; - - if (! helpers.isScalar(param)) { - Object.keys(param).forEach(part => { - sql += param[part].conjunction + param[part].string; - }); - } else { - sql += param; - } - }); - - // Append the limit, if it exists - if (helpers.isNumber(this.state.limit)) { - sql = this.driver.limit(sql, this.state.limit, this.state.offset); - } - - return sql; - } - - _compileType(type, table) { - let sql = ''; - - switch (type) { - case 'insert': - let params = Array(this.state.setArrayKeys.length).fill('?'); - - sql = `INSERT INTO ${table} (`; - sql += this.state.setArrayKeys.join(','); - sql += `) VALUES (${params.join(',')})`; - break; - - case 'update': - sql = `UPDATE ${table} SET ${this.state.setString}`; - break; - - case 'delete': - sql = `DELETE FROM ${table}`; - break; - - default: - sql = `SELECT * FROM ${this.state.fromString}`; - - // Set the select string - if (this.state.selectString.length > 0) { - // Replace the star with the selected fields - sql = sql.replace('*', this.state.selectString); - } - - break; - } - - return sql; - } - - _like(field, val, pos, like, conj) { - field = this.driver.quoteIdentifiers(field); - - like = `${field} ${like} ?`; - - if (pos === 'before') { - val = `%${val}`; - } else if (pos === 'after') { - val = `${val}%`; - } else { - val = `%${val}%`; - } - - conj = (this.state.queryMap.length < 1) ? ' WHERE ' : ` ${conj} `; - this._appendMap(conj, like, 'like'); - - this.state.whereValues.push(val); - } - - /** - * Append a clause to the query map - * - * @private - * @param {String} conjunction - linking keyword for the clause - * @param {String} string - pre-compiled sql fragment - * @param {String} type - type of sql clause - * @return {void} - */ - _appendMap(conjunction, string, type) { - this.state.queryMap.push({ - type: type, - conjunction: conjunction, - string: string, - }); - } - - /** - * Handle key/value pairs in an object the same way as individual arguments, - * when appending to state - * - * @private - * @return {Array} - modified state array - */ - _mixedSet(/* $letName, $valType, $key, [$val] */) { - const argPattern = '$letName:string, $valType:string, $key:object|string|number, [$val]'; - let args = getArgs(argPattern, arguments); - - let obj = {}; - - if (helpers.isScalar(args.$key) && !helpers.isUndefined(args.$val)) { - // Convert key/val pair to a simple object - obj[args.$key] = args.$val; - } else if (helpers.isScalar(args.$key) && helpers.isUndefined(args.$val)) { - // If just a string for the key, and no value, create a simple object with duplicate key/val - obj[args.$key] = args.$key; - } else { - obj = args.$key; - } - - Object.keys(obj).forEach(k => { - // If a single value for the return - if (['key', 'value'].indexOf(args.$valType) !== -1) { - let pushVal = (args.$valType === 'key') ? k : obj[k]; - this.state[args.$letName].push(pushVal); - } else { - this.state[args.$letName][k] = obj[k]; - } - }); - - return this.state[args.$letName]; - } - - _whereMixedSet(/*key, val*/) { - let args = getArgs('key:string|object, [val]', arguments); - - this.state.whereMap = []; - this.state.rawWhereValues = []; - - this._mixedSet('whereMap', 'both', args.key, args.val); - this._mixedSet('rawWhereValues', 'value', args.key, args.val); - } - - _fixConjunction(conj) { - let lastItem = this.state.queryMap[this.state.queryMap.length - 1]; - let conjunctionList = helpers.arrayPluck(this.state.queryMap, 'conjunction'); - - if (this.state.queryMap.length === 0 || (! helpers.regexInArray(conjunctionList, /^ ?WHERE/i))) { - conj = ' WHERE '; - } else if (lastItem.type === 'groupStart') { - conj = ''; - } else { - conj = ` ${conj} `; - } - - return conj; - } - - _where(key, val, defaultConj) { - // Normalize key and value and insert into this.state.whereMap - this._whereMixedSet(key, val); - - // Parse the where condition to account for operators, - // functions, identifiers, and literal values - this.state = this.parser.parseWhere(this.driver, this.state); - - this.state.whereMap.forEach(clause => { - let conj = this._fixConjunction(defaultConj); - this._appendMap(conj, clause, 'where'); - }); - - this.state.whereMap = {}; - } - - _whereNull(field, stmt, conj) { - field = this.driver.quoteIdentifiers(field); - let item = `${field} ${stmt}`; - - this._appendMap(this._fixConjunction(conj), item, 'whereNull'); - } - - _having(/*key, val, conj*/) { - let args = getArgs('key:string|object, [val]:string|number, [conj]:string', arguments); - args.conj = args.conj || 'AND'; - args.val = args.val || null; - - // Normalize key/val and put in state.whereMap - this._whereMixedSet(args.key, args.val); - - // Parse the having condition to account for operators, - // functions, identifiers, and literal values - this.state = this.parser.parseWhere(this.driver, this.state); - - this.state.whereMap.forEach(clause => { - // Put in the having map - this.state.havingMap.push({ - conjunction: (this.state.havingMap.length > 0) ? ` ${args.conj} ` : ' HAVING ', - string: clause, - }); - }); - - // Clear the where Map - this.state.whereMap = {}; - - } - - _whereIn(/*key, val, inClause, conj*/) { - let args = getArgs('key:string, val:array, inClause:string, conj:string', arguments); - - args.key = this.driver.quoteIdentifiers(args.key); - let params = Array(args.val.length); - params.fill('?'); - - args.val.forEach(value => { - this.state.whereValues.push(value); - }); - - args.conj = (this.state.queryMap.length > 0) ? ` ${args.conj} ` : ' WHERE '; - let str = `${args.key} ${args.inClause} (${params.join(',')}) `; - - this._appendMap(args.conj, str, 'whereIn'); - } - - _run(type, table, callback, sql, vals) { - - if (! sql) { - sql = this._compile(type, table); - } - - if (! vals) { - vals = this.state.values.concat(this.state.whereValues); - } - - // Reset the state so another query can be built - this._resetState(); - - // Pass the sql and values to the adapter to run on the database - if (callback) { - return this.query(sql, vals, callback); - } else { - return this.query(sql, vals); - } - } - - _getCompile(type, table, reset) { - reset = reset || false; - - let sql = this._compile(type, table); - - if (reset) { - this._resetState(); - } - - return sql; - } - - _resetState() { - this.state = new State(); - } -} +const QueryBuilderBase = require('./QueryBuilderBase'); /** * Main object that builds SQL queries. * * @param {Driver} Driver - The syntax driver for the database * @param {Adapter} Adapter - The database module adapter for running queries + * @extends QueryBuilderBase */ class QueryBuilder extends QueryBuilderBase { - /** - * @private - * @constructor - * @param {Driver} Driver - The syntax driver for the database - * @param {Adapter} Adapter - The database module adapter for running queries - */ - constructor(Driver, Adapter) { - super(Driver, Adapter); - } - // ---------------------------------------------------------------------------- // ! Miscellaneous Methods // ---------------------------------------------------------------------------- @@ -316,7 +24,7 @@ class QueryBuilder extends QueryBuilderBase { * @param {function} [callback] - Optional callback * @return {void|Promise} - Returns a promise if no callback is supplied */ - query(/*sql:string, [params]:array, [callback]:function*/) { + query (/* sql:string, [params]:array, [callback]:function */) { return this.adapter.execute.apply(this.adapter, arguments); } @@ -325,7 +33,7 @@ class QueryBuilder extends QueryBuilderBase { * * @return {void} */ - resetQuery() { + resetQuery () { this._resetState(); } @@ -335,7 +43,7 @@ class QueryBuilder extends QueryBuilderBase { * @private * @return {Object} - The State object */ - getState() { + getState () { return this.state; } @@ -346,7 +54,7 @@ class QueryBuilder extends QueryBuilderBase { * @param {function} [callback] - Optional callback * @return {void|Promise} - Returns a promise if no callback is supplied */ - truncate(/*table:string, [callback]:function*/) { + truncate (/* table:string, [callback]:function */) { getArgs('table:string, [callback]:function', arguments); let args = [].slice.apply(arguments); let sql = this.driver.truncate(args.shift()); @@ -359,7 +67,7 @@ class QueryBuilder extends QueryBuilderBase { * * @return {void} */ - end() { + end () { this.adapter.close(); } @@ -375,8 +83,7 @@ class QueryBuilder extends QueryBuilderBase { * @example query.select(['foo', 'bar']); // Select multiple fileds with an array * @return {QueryBuilder} - The Query Builder object, for chaining */ - select(fields) { - + select (fields) { // Split/trim fields by comma fields = (Array.isArray(fields)) ? fields @@ -411,7 +118,7 @@ class QueryBuilder extends QueryBuilderBase { * @example query.from('tableName t'); // Select the table with an alias * @return {QueryBuilder} - The Query Builder object, for chaining */ - from(tableName) { + from (tableName) { // Split identifiers on spaces let identArray = tableName.trim().split(' ').map(helpers.stringTrim); @@ -433,7 +140,7 @@ class QueryBuilder extends QueryBuilderBase { * @param {String} [pos=both] - The placement of the wildcard character(s): before, after, or both * @return {QueryBuilder} - The Query Builder object, for chaining */ - like(field, val, pos) { + like (field, val, pos) { this._like(field, val, pos, ' LIKE ', 'AND'); return this; } @@ -446,7 +153,7 @@ class QueryBuilder extends QueryBuilderBase { * @param {String} [pos=both] - The placement of the wildcard character(s): before, after, or both * @return {QueryBuilder} - The Query Builder object, for chaining */ - notLike(field, val, pos) { + notLike (field, val, pos) { this._like(field, val, pos, ' NOT LIKE ', 'AND'); return this; } @@ -459,7 +166,7 @@ class QueryBuilder extends QueryBuilderBase { * @param {String} [pos=both] - The placement of the wildcard character(s): before, after, or both * @return {QueryBuilder} - The Query Builder object, for chaining */ - orLike(field, val, pos) { + orLike (field, val, pos) { this._like(field, val, pos, ' LIKE ', 'OR'); return this; } @@ -472,7 +179,7 @@ class QueryBuilder extends QueryBuilderBase { * @param {String} [pos=both] - The placement of the wildcard character(s): before, after, or both * @return {QueryBuilder} - The Query Builder object, for chaining */ - orNotLike(field, val, pos) { + orNotLike (field, val, pos) { this._like(field, val, pos, ' NOT LIKE ', 'OR'); return this; } @@ -484,7 +191,7 @@ class QueryBuilder extends QueryBuilderBase { * @param {String|Number} [val] - The value to compare if the value of key is a string * @return {QueryBuilder} - The Query Builder object, for chaining */ - having(/*key, [val]*/) { + having (/* key, [val] */) { let args = getArgs('key:string|object, [val]:string|number', arguments); this._having(args.key, args.val, 'AND'); @@ -498,7 +205,7 @@ class QueryBuilder extends QueryBuilderBase { * @param {String|Number} [val] - The value to compare if the value of key is a string * @return {QueryBuilder} - The Query Builder object, for chaining */ - orHaving(/*key, [val]*/) { + orHaving (/* key, [val] */) { let args = getArgs('key:string|object, [val]:string|number', arguments); this._having(args.key, args.val, 'OR'); @@ -512,7 +219,7 @@ class QueryBuilder extends QueryBuilderBase { * @param {String|Number} [val] - The value to compare if the value of key is a string * @return {QueryBuilder} - The Query Builder object, for chaining */ - where(key, val) { + where (key, val) { this._where(key, val, 'AND'); return this; } @@ -524,7 +231,7 @@ class QueryBuilder extends QueryBuilderBase { * @param {String|Number} [val] - The value to compare if the value of key is a string * @return {QueryBuilder} - The Query Builder object, for chaining */ - orWhere(key, val) { + orWhere (key, val) { this._where(key, val, 'OR'); return this; } @@ -535,7 +242,7 @@ class QueryBuilder extends QueryBuilderBase { * @param {String} field - The name of the field that has a NULL value * @return {QueryBuilder} - The Query Builder object, for chaining */ - whereIsNull(field) { + whereIsNull (field) { this._whereNull(field, 'IS NULL', 'AND'); return this; } @@ -546,7 +253,7 @@ class QueryBuilder extends QueryBuilderBase { * @param {String} field - The name so the field that is not to be null * @return {QueryBuilder} - The Query Builder object, for chaining */ - whereIsNotNull(field) { + whereIsNotNull (field) { this._whereNull(field, 'IS NOT NULL', 'AND'); return this; } @@ -557,7 +264,7 @@ class QueryBuilder extends QueryBuilderBase { * @param {String} field - The name of the field * @return {QueryBuilder} - The Query Builder object, for chaining */ - orWhereIsNull(field) { + orWhereIsNull (field) { this._whereNull(field, 'IS NULL', 'OR'); return this; } @@ -568,7 +275,7 @@ class QueryBuilder extends QueryBuilderBase { * @param {String} field - The name of the field * @return {QueryBuilder} - The Query Builder object, for chaining */ - orWhereIsNotNull(field) { + orWhereIsNotNull (field) { this._whereNull(field, 'IS NOT NULL', 'OR'); return this; } @@ -580,7 +287,7 @@ class QueryBuilder extends QueryBuilderBase { * @param {Array} values - the array of items to search in * @return {QueryBuilder} - The Query Builder object, for chaining */ - whereIn(key, values) { + whereIn (key, values) { this._whereIn(key, values, 'IN', 'AND'); return this; } @@ -592,7 +299,7 @@ class QueryBuilder extends QueryBuilderBase { * @param {Array} values - the array of items to search in * @return {QueryBuilder} - The Query Builder object, for chaining */ - orWhereIn(key, values) { + orWhereIn (key, values) { this._whereIn(key, values, 'IN', 'OR'); return this; } @@ -604,7 +311,7 @@ class QueryBuilder extends QueryBuilderBase { * @param {Array} values - the array of items to search in * @return {QueryBuilder} - The Query Builder object, for chaining */ - whereNotIn(key, values) { + whereNotIn (key, values) { this._whereIn(key, values, 'NOT IN', 'AND'); return this; } @@ -616,7 +323,7 @@ class QueryBuilder extends QueryBuilderBase { * @param {Array} values - the array of items to search in * @return {QueryBuilder} - The Query Builder object, for chaining */ - orWhereNotIn(key, values) { + orWhereNotIn (key, values) { this._whereIn(key, values, 'NOT IN', 'OR'); return this; } @@ -630,7 +337,7 @@ class QueryBuilder extends QueryBuilderBase { * @example query.set({foo:'bar'}); // Set with an object * @return {QueryBuilder} - The Query Builder object, for chaining */ - set(/* $key, [$val] */) { + set (/* $key, [$val] */) { let args = getArgs('$key, [$val]', arguments); // Set the appropriate state variables @@ -656,7 +363,7 @@ class QueryBuilder extends QueryBuilderBase { * @param {String} [type='inner'] - The type of join, which defaults to inner * @return {QueryBuilder} - The Query Builder object, for chaining */ - join(table, cond, type) { + join (table, cond, type) { type = type || 'inner'; // Prefix/quote table name @@ -681,8 +388,8 @@ class QueryBuilder extends QueryBuilderBase { * @param {String|Array} field - The name of the field to group by * @return {QueryBuilder} - The Query Builder object, for chaining */ - groupBy(field) { - if (! helpers.isScalar(field)) { + groupBy (field) { + if (!helpers.isScalar(field)) { let newGroupArray = field.map(this.driver.quoteIdentifiers); this.state.groupArray = this.state.groupArray.concat(newGroupArray); } else { @@ -701,7 +408,7 @@ class QueryBuilder extends QueryBuilderBase { * @param {String} [type='ASC'] - The order direction, ASC or DESC * @return {QueryBuilder} - The Query Builder object, for chaining */ - orderBy(field, type) { + orderBy (field, type) { type = type || 'ASC'; // Set the fields for later manipulation @@ -729,7 +436,7 @@ class QueryBuilder extends QueryBuilderBase { * @param {Number} [offset] - The row number to start from * @return {QueryBuilder} - The Query Builder object, for chaining */ - limit(limit, offset) { + limit (limit, offset) { this.state.limit = limit; this.state.offset = offset || null; @@ -741,7 +448,7 @@ class QueryBuilder extends QueryBuilderBase { * * @return {QueryBuilder} - The Query Builder object, for chaining */ - groupStart() { + groupStart () { let conj = (this.state.queryMap.length < 1) ? ' WHERE ' : ' AND '; this._appendMap(conj, '(', 'groupStart'); @@ -754,7 +461,7 @@ class QueryBuilder extends QueryBuilderBase { * * @return {QueryBuilder} - The Query Builder object, for chaining */ - orGroupStart() { + orGroupStart () { this._appendMap('', ' OR (', 'groupStart'); return this; @@ -766,7 +473,7 @@ class QueryBuilder extends QueryBuilderBase { * * @return {QueryBuilder} - The Query Builder object, for chaining */ - orNotGroupStart() { + orNotGroupStart () { this._appendMap('', ' OR NOT (', 'groupStart'); return this; @@ -777,7 +484,7 @@ class QueryBuilder extends QueryBuilderBase { * * @return {QueryBuilder} - The Query Builder object, for chaining */ - groupEnd() { + groupEnd () { this._appendMap('', ')', 'groupEnd'); return this; @@ -799,7 +506,7 @@ class QueryBuilder extends QueryBuilderBase { * @example query.get(callback); // Get the results of a query generated with other methods * @return {void|Promise} - If no callback is passed, a promise is returned */ - get(/* [table], [limit], [offset], [callback] */) { + get (/* [table], [limit], [offset], [callback] */) { const argPattern = '[table]:string, [limit]:number, [offset]:number, [callback]:function'; let args = getArgs(argPattern, arguments); @@ -823,7 +530,7 @@ class QueryBuilder extends QueryBuilderBase { * @param {Function} [callback] - Callback for handling response from the database * @return {void|Promise} - If no callback is passed, a promise is returned */ - insert(/* table, data, callback */) { + insert (/* table, data, callback */) { let args = getArgs('table:string, [data]:object, [callback]:function', arguments); if (args.data) { @@ -845,7 +552,7 @@ class QueryBuilder extends QueryBuilderBase { *.then(promiseCallback); * @return {void|Promise} - If no callback is passed, a promise is returned */ - insertBatch(/* table, data, callback */) { + insertBatch (/* table, data, callback */) { let args = getArgs('table:string, data:array, [callback]:function', arguments); let batch = this.driver.insertBatch(args.table, args.data); @@ -861,7 +568,7 @@ class QueryBuilder extends QueryBuilderBase { * @param {Function} [callback] - Callback for handling response from the database * @return {void|Promise} - If no callback is passed, a promise is returned */ - update(/*table, data, callback*/) { + update (/* table, data, callback */) { let args = getArgs('table:string, [data]:object, [callback]:function', arguments); if (args.data) { @@ -880,7 +587,7 @@ class QueryBuilder extends QueryBuilderBase { * @param {Function} [callback] - Callback for handling response from the database * @return {void|Promise} - If no callback is passed, a promise is returned */ - delete(/*table, [where], [callback]*/) { + delete (/* table, [where], [callback] */) { let args = getArgs('table:string, [where]:object, [callback]:function', arguments); if (args.where) { @@ -902,7 +609,7 @@ class QueryBuilder extends QueryBuilderBase { * @param {Boolean} [reset=true] - Whether to reset the query builder so another query can be built * @return {String} - The compiled sql statement */ - getCompiledSelect(/*table, reset*/) { + getCompiledSelect (/* table, reset */) { let args = getArgs('[table]:string, [reset]:boolean', arguments); if (args.table) { this.from(args.table); @@ -918,7 +625,7 @@ class QueryBuilder extends QueryBuilderBase { * @param {Boolean} [reset=true] - Whether to reset the query builder so another query can be built * @return {String} - The compiled sql statement */ - getCompiledInsert(table, reset) { + getCompiledInsert (table, reset) { return this._getCompile('insert', this.driver.quoteTable(table), reset); } @@ -929,7 +636,7 @@ class QueryBuilder extends QueryBuilderBase { * @param {Boolean} [reset=true] - Whether to reset the query builder so another query can be built * @return {String} - The compiled sql statement */ - getCompiledUpdate(table, reset) { + getCompiledUpdate (table, reset) { return this._getCompile('update', this.driver.quoteTable(table), reset); } @@ -940,9 +647,9 @@ class QueryBuilder extends QueryBuilderBase { * @param {Boolean} [reset=true] - Whether to reset the query builder so another query can be built * @return {String} - The compiled sql statement */ - getCompiledDelete(table, reset) { + getCompiledDelete (table, reset) { return this._getCompile('delete', this.driver.quoteTable(table), reset); } } -module.exports = QueryBuilder; \ No newline at end of file +module.exports = QueryBuilder; diff --git a/lib/QueryBuilderBase.js b/lib/QueryBuilderBase.js new file mode 100644 index 0000000..8baabc7 --- /dev/null +++ b/lib/QueryBuilderBase.js @@ -0,0 +1,288 @@ +'use strict'; + +const getArgs = require('getargs'); +const helpers = require('./helpers'); +const QueryParser = require('./QueryParser'); +const State = require('./State'); + +class QueryBuilderBase { + /** + * @private + * @constructor + * @param {Driver} Driver - The syntax driver for the database + * @param {Adapter} Adapter - The database module adapter for running queries + */ + constructor (Driver, Adapter) { + this.driver = Driver; + this.adapter = Adapter; + this.parser = new QueryParser(this.driver); + this.state = new State(); + } + + /** + * Complete the sql building based on the type provided + * + * @private + * @param {String} type - Type of SQL query + * @param {String} table - The table to run the query on + * @return {String} - The compiled sql + */ + _compile (type, table) { + // Put together the basic query + let sql = this._compileType(type, table); + + // Set each subClause + ['queryMap', 'groupString', 'orderString', 'havingMap'].forEach(clause => { + let param = this.state[clause]; + + if (!helpers.isScalar(param)) { + Object.keys(param).forEach(part => { + sql += param[part].conjunction + param[part].string; + }); + } else { + sql += param; + } + }); + + // Append the limit, if it exists + if (helpers.isNumber(this.state.limit)) { + sql = this.driver.limit(sql, this.state.limit, this.state.offset); + } + + return sql; + } + + _compileType (type, table) { + let sql = ''; + + switch (type) { + case 'insert': + let params = Array(this.state.setArrayKeys.length).fill('?'); + + sql = `INSERT INTO ${table} (`; + sql += this.state.setArrayKeys.join(','); + sql += `) VALUES (${params.join(',')})`; + break; + + case 'update': + sql = `UPDATE ${table} SET ${this.state.setString}`; + break; + + case 'delete': + sql = `DELETE FROM ${table}`; + break; + + default: + sql = `SELECT * FROM ${this.state.fromString}`; + + // Set the select string + if (this.state.selectString.length > 0) { + // Replace the star with the selected fields + sql = sql.replace('*', this.state.selectString); + } + + break; + } + + return sql; + } + + _like (field, val, pos, like, conj) { + field = this.driver.quoteIdentifiers(field); + + like = `${field} ${like} ?`; + + if (pos === 'before') { + val = `%${val}`; + } else if (pos === 'after') { + val = `${val}%`; + } else { + val = `%${val}%`; + } + + conj = (this.state.queryMap.length < 1) ? ' WHERE ' : ` ${conj} `; + this._appendMap(conj, like, 'like'); + + this.state.whereValues.push(val); + } + + /** + * Append a clause to the query map + * + * @private + * @param {String} conjunction - linking keyword for the clause + * @param {String} string - pre-compiled sql fragment + * @param {String} type - type of sql clause + * @return {void} + */ + _appendMap (conjunction, string, type) { + this.state.queryMap.push({ + type: type, + conjunction: conjunction, + string: string + }); + } + + /** + * Handle key/value pairs in an object the same way as individual arguments, + * when appending to state + * + * @private + * @return {Array} - modified state array + */ + _mixedSet (/* $letName, $valType, $key, [$val] */) { + const argPattern = '$letName:string, $valType:string, $key:object|string|number, [$val]'; + let args = getArgs(argPattern, arguments); + + let obj = {}; + + if (helpers.isScalar(args.$key) && !helpers.isUndefined(args.$val)) { + // Convert key/val pair to a simple object + obj[args.$key] = args.$val; + } else if (helpers.isScalar(args.$key) && helpers.isUndefined(args.$val)) { + // If just a string for the key, and no value, create a simple object with duplicate key/val + obj[args.$key] = args.$key; + } else { + obj = args.$key; + } + + Object.keys(obj).forEach(k => { + // If a single value for the return + if (['key', 'value'].indexOf(args.$valType) !== -1) { + let pushVal = (args.$valType === 'key') ? k : obj[k]; + this.state[args.$letName].push(pushVal); + } else { + this.state[args.$letName][k] = obj[k]; + } + }); + + return this.state[args.$letName]; + } + + _whereMixedSet (/* key, val */) { + let args = getArgs('key:string|object, [val]', arguments); + + this.state.whereMap = []; + this.state.rawWhereValues = []; + + this._mixedSet('whereMap', 'both', args.key, args.val); + this._mixedSet('rawWhereValues', 'value', args.key, args.val); + } + + _fixConjunction (conj) { + let lastItem = this.state.queryMap[this.state.queryMap.length - 1]; + let conjunctionList = helpers.arrayPluck(this.state.queryMap, 'conjunction'); + + if (this.state.queryMap.length === 0 || (!helpers.regexInArray(conjunctionList, /^ ?WHERE/i))) { + conj = ' WHERE '; + } else if (lastItem.type === 'groupStart') { + conj = ''; + } else { + conj = ` ${conj} `; + } + + return conj; + } + + _where (key, val, defaultConj) { + // Normalize key and value and insert into this.state.whereMap + this._whereMixedSet(key, val); + + // Parse the where condition to account for operators, + // functions, identifiers, and literal values + this.state = this.parser.parseWhere(this.driver, this.state); + + this.state.whereMap.forEach(clause => { + let conj = this._fixConjunction(defaultConj); + this._appendMap(conj, clause, 'where'); + }); + + this.state.whereMap = {}; + } + + _whereNull (field, stmt, conj) { + field = this.driver.quoteIdentifiers(field); + let item = `${field} ${stmt}`; + + this._appendMap(this._fixConjunction(conj), item, 'whereNull'); + } + + _having (/* key, val, conj */) { + let args = getArgs('key:string|object, [val]:string|number, [conj]:string', arguments); + args.conj = args.conj || 'AND'; + args.val = args.val || null; + + // Normalize key/val and put in state.whereMap + this._whereMixedSet(args.key, args.val); + + // Parse the having condition to account for operators, + // functions, identifiers, and literal values + this.state = this.parser.parseWhere(this.driver, this.state); + + this.state.whereMap.forEach(clause => { + // Put in the having map + this.state.havingMap.push({ + conjunction: (this.state.havingMap.length > 0) ? ` ${args.conj} ` : ' HAVING ', + string: clause + }); + }); + + // Clear the where Map + this.state.whereMap = {}; + } + + _whereIn (/* key, val, inClause, conj */) { + let args = getArgs('key:string, val:array, inClause:string, conj:string', arguments); + + args.key = this.driver.quoteIdentifiers(args.key); + let params = Array(args.val.length); + params.fill('?'); + + args.val.forEach(value => { + this.state.whereValues.push(value); + }); + + args.conj = (this.state.queryMap.length > 0) ? ` ${args.conj} ` : ' WHERE '; + let str = `${args.key} ${args.inClause} (${params.join(',')}) `; + + this._appendMap(args.conj, str, 'whereIn'); + } + + _run (type, table, callback, sql, vals) { + if (!sql) { + sql = this._compile(type, table); + } + + if (!vals) { + vals = this.state.values.concat(this.state.whereValues); + } + + // Reset the state so another query can be built + this._resetState(); + + // Pass the sql and values to the adapter to run on the database + if (callback) { + return this.query(sql, vals, callback); + } else { + return this.query(sql, vals); + } + } + + _getCompile (type, table, reset) { + reset = reset || false; + + let sql = this._compile(type, table); + + if (reset) { + this._resetState(); + } + + return sql; + } + + _resetState () { + this.state = new State(); + } +} + +module.exports = QueryBuilderBase; diff --git a/lib/QueryParser.js b/lib/QueryParser.js index 02a3cd3..e162385 100644 --- a/lib/QueryParser.js +++ b/lib/QueryParser.js @@ -18,13 +18,13 @@ class QueryParser { * @param {Driver} driver - The driver object for the database in use * @return {void} */ - constructor(driver) { + constructor (driver) { this.driver = driver; const matchPatterns = { function: /([a-z0-9_]+\((.*)\))/i, - operator: /\!=?|\=|\+|&&?|~|\|\|?|\^|\/|<>|>=?|<=?|\-|%|OR|AND|NOT|XOR/ig, - literal: /([0-9]+)|'(.*?)'|true|false/ig, + operator: /!=?|=|\+|&&?|~|\|\|?|\^|\/|<>|>=?|<=?|-|%|OR|AND|NOT|XOR/ig, + literal: /([0-9]+)|'(.*?)'|true|false/ig }; // Full pattern for identifiers @@ -55,7 +55,7 @@ class QueryParser { * @param {Array} array - Set of possible matches * @return {Array|null} - Filtered set of possible matches */ - filterMatches(array) { + filterMatches (array) { let output = []; // Return non-array matches @@ -76,7 +76,7 @@ class QueryParser { * @param {String} string - the string to check * @return {Array|null} - List of operators */ - hasOperator(string) { + hasOperator (string) { return this.filterMatches(string.match(this.matchPatterns.operator)); } @@ -86,13 +86,13 @@ class QueryParser { * @param {String} sql - Join sql to parse * @return {Object} - Join condition components */ - parseJoin(sql) { + parseJoin (sql) { let matches = {}; let output = { functions: [], identifiers: [], operators: [], - literals: [], + literals: [] }; // Get clause components @@ -118,12 +118,12 @@ class QueryParser { * @param {String} condition - The join condition to evalate * @return {String} - The parsed/escaped join condition */ - compileJoin(condition) { + compileJoin (condition) { let parts = this.parseJoin(condition); // Quote the identifiers parts.combined.forEach((part, i) => { - if (parts.identifiers.indexOf(part) !== -1 && ! helpers.isNumber(part)) { + if (parts.identifiers.indexOf(part) !== -1 && !helpers.isNumber(part)) { parts.combined[i] = this.driver.quoteIdentifiers(part); } }); @@ -138,7 +138,7 @@ class QueryParser { * @param {State} state - Query Builder state object * @return {String} - The parsed/escaped where condition */ - parseWhere(driver, state) { + parseWhere (driver, state) { let whereMap = state.whereMap; let whereValues = state.rawWhereValues; @@ -151,7 +151,7 @@ class QueryParser { let fullClause = ''; // Add an explicit = sign where one is inferred - if (! this.hasOperator(key)) { + if (!this.hasOperator(key)) { fullClause = `${key} = ${whereMap[key]}`; } else if (whereMap[key] === key) { fullClause = key; @@ -176,7 +176,7 @@ class QueryParser { if (identIndex !== -1) { parts.identifiers.splice(identIndex, 1); - if (! inOutputArray) { + if (!inOutputArray) { outputValues.push(value); inOutputArray = true; } @@ -187,7 +187,7 @@ class QueryParser { if (litIndex !== -1) { parts.literals.splice(litIndex, 1); - if (! inOutputArray) { + if (!inOutputArray) { outputValues.push(value); inOutputArray = true; } @@ -246,4 +246,4 @@ class QueryParser { } } -module.exports = QueryParser; \ No newline at end of file +module.exports = QueryParser; diff --git a/lib/Result.js b/lib/Result.js index 7f0b185..8e03b96 100644 --- a/lib/Result.js +++ b/lib/Result.js @@ -16,7 +16,7 @@ class Result { * @param {Array} [rows] - the data rows of the result * @param {Array} [columns] - the column names in the result */ - constructor(rows, columns) { + constructor (rows, columns) { this._rows = (rows == null) ? [] : rows; this._columns = (columns == null) ? [] : columns; @@ -37,7 +37,7 @@ class Result { * @private * @return {Array} - the data rows of the result */ - get rows() { + get rows () { return this._rows; } @@ -48,7 +48,7 @@ class Result { * @param {Array} rows - the data rows of the result * @return {void} */ - set rows(rows) { + set rows (rows) { this._rows = rows; } @@ -58,7 +58,7 @@ class Result { * @private * @return {Array} - the column names in the result */ - get columns() { + get columns () { return this._columns; } @@ -69,7 +69,7 @@ class Result { * @param {Array} cols - the array of columns for the current result * @return {void} */ - set columns(cols) { + set columns (cols) { this._columns = cols; } @@ -78,7 +78,7 @@ class Result { * * @return {Number} - the number of rows in the result */ - rowCount() { + rowCount () { return this._rows.length; } @@ -87,9 +87,9 @@ class Result { * * @return {Number} - the number of columns in the result */ - columnCount() { + columnCount () { return this._columns.length; } } -module.exports = Result; \ No newline at end of file +module.exports = Result; diff --git a/lib/State.js b/lib/State.js index 66cae2b..eec885c 100644 --- a/lib/State.js +++ b/lib/State.js @@ -5,7 +5,7 @@ * @private */ class State { - constructor() { + constructor () { // Arrays/maps this.queryMap = []; this.values = []; @@ -31,5 +31,3 @@ class State { } module.exports = State; - -// End of module State \ No newline at end of file diff --git a/lib/adapters/MariaDB.js b/lib/adapters/MariaDB.js new file mode 100644 index 0000000..a2aab43 --- /dev/null +++ b/lib/adapters/MariaDB.js @@ -0,0 +1,3 @@ +'use strict'; + +module.exports = require('./Mysql'); diff --git a/lib/adapters/Mysql.js b/lib/adapters/Mysql.js index 817013b..1e3ab39 100644 --- a/lib/adapters/Mysql.js +++ b/lib/adapters/Mysql.js @@ -8,7 +8,7 @@ const mysql2 = require('mysql2'); class Mysql extends Adapter { - constructor(config) { + constructor (config) { let instance = mysql2.createConnection(config); instance.connect(err => { if (err) { @@ -24,7 +24,7 @@ class Mysql extends Adapter { * @param {*} result - original driver result object * @return {Result} - standard result object */ - transformResult(result) { + transformResult (result) { // For insert and update queries, the result object // works differently. Just apply the properties of // this special result to the standard result object. @@ -49,10 +49,10 @@ class Mysql extends Adapter { * @param {Function} [callback] - Callback to run when a response is recieved * @return {void|Promise} - Returns a promise if no callback is provided */ - execute(/*sql, params, callback*/) { + execute (/* sql, params, callback */) { let args = getArgs('sql:string, [params]:array, [callback]:function', arguments); - if (! args.callback) { + if (!args.callback) { return new Promise((resolve, reject) => { this.instance.execute(args.sql, args.params, (err, result) => (err) @@ -68,4 +68,4 @@ class Mysql extends Adapter { } } -module.exports = Mysql; \ No newline at end of file +module.exports = Mysql; diff --git a/lib/adapters/Pg.js b/lib/adapters/Pg.js index 996737c..3b307af 100644 --- a/lib/adapters/Pg.js +++ b/lib/adapters/Pg.js @@ -9,7 +9,7 @@ const url = require('url'); class Pg extends Adapter { - constructor(config) { + constructor (config) { let instance = null; let connectionString = ''; if (helpers.isObject(config)) { @@ -23,7 +23,7 @@ class Pg extends Adapter { slashes: true, host: `${host}:${port}`, auth: `${user}${password}`, - pathname: config.database, + pathname: config.database }; connectionString = url.format(conn); @@ -32,12 +32,8 @@ class Pg extends Adapter { } if (connectionString !== '') { - let connected = false; instance = new pg.Client(connectionString); - instance.connect(err => { - connected = true; - if (err) { throw new Error(err); } @@ -53,13 +49,15 @@ class Pg extends Adapter { * @param {*} result - original driver result object * @return {Result} - standard result object */ - transformResult(result) { + transformResult (result) { if (result == null) { return new Result(); } let cols = []; - result.fields.forEach(field => cols = field.name); + result.fields.forEach(field => { + cols = field.name; + }); return new Result(result.rows, cols); } @@ -72,7 +70,7 @@ class Pg extends Adapter { * @param {Function} [callback] - Callback to run when a response is recieved * @return {void|Promise} - Returns a promise if no callback is provided */ - execute(/*sql, params, callback*/) { + execute (/* sql, params, callback */) { let args = getArgs('sql:string, [params]:array, [callback]:function', arguments); // Replace question marks with numbered placeholders, because this adapter is different... @@ -82,7 +80,7 @@ class Pg extends Adapter { return `$${count}`; }); - if (! args.callback) { + if (!args.callback) { return new Promise((resolve, reject) => { this.instance.query(args.sql, args.params, (err, result) => (err) @@ -99,4 +97,4 @@ class Pg extends Adapter { } } -module.exports = Pg; \ No newline at end of file +module.exports = Pg; diff --git a/lib/adapters/Sqlite.js b/lib/adapters/Sqlite.js index 19e4a1a..173c795 100644 --- a/lib/adapters/Sqlite.js +++ b/lib/adapters/Sqlite.js @@ -7,7 +7,7 @@ const helpers = require('../helpers'); const dbliteAdapter = require('dblite'); class Sqlite extends Adapter { - constructor(config) { + constructor (config) { let file = (helpers.isString(config)) ? config : config.file; super(dbliteAdapter(file)); @@ -21,7 +21,7 @@ class Sqlite extends Adapter { * @param {*} result - original driver result object * @return {Result} - standard result object */ - transformResult(result) { + transformResult (result) { return new Result(result); } @@ -33,10 +33,10 @@ class Sqlite extends Adapter { * @param {Function} [callback] - Callback to run when a response is recieved * @return {void|Promise} - Returns a promise if no callback is provided */ - execute(/*sql, params, callback*/) { + execute (/* sql, params, callback */) { let args = getArgs('sql:string, [params]:array, [callback]:function', arguments); - if (! args.callback) { + if (!args.callback) { return new Promise((resolve, reject) => { this.instance.query(args.sql, args.params, (err, result) => (err) @@ -56,7 +56,7 @@ class Sqlite extends Adapter { * @return {void} */ - close() { + close () { this.instance.close(); } } diff --git a/lib/drivers/MariaDB.js b/lib/drivers/MariaDB.js new file mode 100644 index 0000000..8d6df4e --- /dev/null +++ b/lib/drivers/MariaDB.js @@ -0,0 +1,8 @@ +'use strict'; + +/** + * Driver for MariaDB databases + * + * @module drivers/MariaDB + */ +module.exports = require('./Mysql'); diff --git a/lib/drivers/Mysql.js b/lib/drivers/Mysql.js index ce3590a..0e91273 100755 --- a/lib/drivers/Mysql.js +++ b/lib/drivers/Mysql.js @@ -3,32 +3,31 @@ /** * Driver for MySQL databases * - * @module drivers/mysql + * @module drivers/Mysql */ module.exports = (() => { delete require.cache[require.resolve('../Driver')]; - let driver = require('../Driver'), - helpers = require('../helpers'); + const driver = require('../Driver'); + const helpers = require('../helpers'); driver.identifierStartChar = '`'; driver.identifierEndChar = '`'; /** * Set the limit clause - + * * @param {String} sql - SQL statement to modify * @param {Number} limit - Maximum number of rows to fetch * @param {Number|null} offset - Number of rows to skip * @return {String} - Modified SQL statement */ driver.limit = (sql, limit, offset) => { - if (! helpers.isNumber(offset)) { - return sql += ` LIMIT ${limit}`; - } + sql += (helpers.isNumber(offset)) + ? ` LIMIT ${offset},${limit}` + : ` LIMIT ${limit}`; - return sql += ` LIMIT ${offset},${limit}`; + return sql; }; return driver; - -})(); \ No newline at end of file +})(); diff --git a/lib/drivers/Pg.js b/lib/drivers/Pg.js index 725b94c..61adf4f 100755 --- a/lib/drivers/Pg.js +++ b/lib/drivers/Pg.js @@ -3,11 +3,11 @@ /** * Driver for PostgreSQL databases * - * @module drivers/pg + * @module drivers/Pg */ module.exports = (() => { delete require.cache[require.resolve('../Driver')]; let driver = require('../Driver'); return driver; -})(); \ No newline at end of file +})(); diff --git a/lib/drivers/Sqlite.js b/lib/drivers/Sqlite.js index f771128..9dfa2d9 100644 --- a/lib/drivers/Sqlite.js +++ b/lib/drivers/Sqlite.js @@ -1,9 +1,9 @@ 'use strict'; /** - * Driver for Sqlite databases + * Driver for SQLite databases * - * @module drivers/sqlite + * @module drivers/Sqlite */ module.exports = (() => { delete require.cache[require.resolve('../Driver')]; @@ -21,14 +21,13 @@ module.exports = (() => { * @return {String} - The generated sql statement */ driver.insertBatch = (table, data) => { - // Get the data values to insert, so they can // be parameterized - let sql = '', - vals = [], - cols = [], - fields = [], - first = data.shift(); + let sql = ''; + let vals = []; + let cols = []; + let fields = []; + let first = data.shift(); data.forEach(obj => { let row = []; @@ -56,9 +55,9 @@ module.exports = (() => { return { sql: sql, - values: null, + values: null }; }; return driver; -})(); \ No newline at end of file +})(); diff --git a/lib/helpers.js b/lib/helpers.js index 76e2adf..afb08d4 100755 --- a/lib/helpers.js +++ b/lib/helpers.js @@ -63,7 +63,7 @@ let helpers = { } arr.forEach(obj => { - if (! helpers.isUndefined(obj[key])) { + if (!helpers.isUndefined(obj[key])) { output.push(obj[key]); } }); @@ -80,13 +80,12 @@ let helpers = { */ regexInArray: (arr, pattern) => { // Empty case(s) - if (! helpers.isArray(arr) || arr.length === 0) { + if (!helpers.isArray(arr) || arr.length === 0) { return false; } - let i, l = arr.length; - - for (i = 0; i < l; i++) { + const l = arr.length; + for (let i = 0; i < l; i++) { // Short circuit if any items match if (pattern.test(arr[i])) { return true; @@ -105,7 +104,7 @@ let helpers = { str += ''; let first = str.charAt(0).toUpperCase(); return first + str.substr(1); - }, + } }; // Define an 'is' method for each type @@ -120,7 +119,7 @@ let types = [ 'Function', 'RegExp', 'NaN', - 'Infinite', + 'Infinite' ]; types.forEach(t => { /** @@ -134,13 +133,13 @@ types.forEach(t => { * @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; \ No newline at end of file +module.exports = helpers; diff --git a/package.json b/package.json index 91a34f5..c3075c7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ci-node-query", - "version": "4.0.0", + "version": "4.0.1", "description": "A query builder for node based on the one in CodeIgniter", "author": "Timothy J Warren ", "engines": { @@ -21,13 +21,14 @@ }, "keywords": [ "codeigniter", - "mysql2", + "mariadb", + "mysql", "query builder", - "pg", "postgres", - "sqlite3", + "postgresql", + "sql", "sqlite", - "dblite" + "sqlite3" ], "bugs": { "url": "https://git.timshomepage.net/timw4mail/node-query/issues" @@ -38,7 +39,7 @@ "getargs": "~0.0.8", "glob": "^7.0.3", "mysql2": "^1.0.0-rc.1", - "pg": "^4.5.1", + "pg": "^6.0.0", "require-reload": "~0.2.2", "xregexp": "^3.0.0" }, @@ -46,13 +47,13 @@ "chai": "^3.5.0", "chai-as-promised": "^5.2.0", "documentation": "", - "eslint": "^2.4.0", + "eslint": "^3.5.0", "glob": "~6.0.4", "globstar": "^1.0.0", + "happiness": "^5.5.0", "istanbul": "~0.4.2", - "jscs": "^2.11.0", - "mocha": "^2.4.5", - "npm-run-all": "^1.6.0", + "mocha": "^3.0.0", + "npm-run-all": "^3.0.0", "nsp": "^2.2.1" }, "license": "MIT", @@ -63,9 +64,12 @@ "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\"", - "lint": "npm-run-all lint:tests lint:src", - "lint:src": "jscs ./lib && eslint ./lib", - "lint:tests": "jscs ./test && eslint ./test", + "happy": "happiness \"lib/**/*.js\" \"test/**/*.js\"", + "happy:src": "happiness \"lib/**/*.js\"", + "happy:tests": "happiness \"test/**/*.js\"", + "lint": "npm-run-all lint:tests lint:src && happy", + "lint:src": "eslint ./lib", + "lint:tests": "eslint ./test", "test": "mocha -R dot" } } diff --git a/test/adapters/dblite_test.js b/test/adapters/dblite_test.js index f90ae30..bdf8d8e 100644 --- a/test/adapters/dblite_test.js +++ b/test/adapters/dblite_test.js @@ -1,3 +1,4 @@ +/* eslint-env node, mocha */ 'use strict'; // Load the test base @@ -5,10 +6,10 @@ const reload = require('require-reload')(require); reload.emptyCache(); const fs = require('fs'); const testBase = reload('../base'); -const expect = testBase.expect; +const expect = testBase.expect; const promiseTestRunner = testBase.promiseTestRunner; const testRunner = testBase.testRunner; -let tests = reload('../base/tests'); +// let tests = reload('../base/tests'); // Load the test config file const config = testBase.config; @@ -29,9 +30,9 @@ suite('Dblite adapter tests -', () => { }); }); - /*--------------------------------------------------------------------------- - Callback Tests - ---------------------------------------------------------------------------*/ + // --------------------------------------------------------------------------- + // Callback Tests + // --------------------------------------------------------------------------- testRunner(qb, (err, result, done) => { expect(err).is.not.ok; @@ -55,16 +56,16 @@ suite('Dblite adapter tests -', () => { { id: 544, key: 3, - val: new Buffer('7'), + val: new Buffer('7') }, { id: 89, key: 34, - val: new Buffer('10 o\'clock'), + val: new Buffer('10 o\'clock') }, { id: 48, key: 403, - val: new Buffer('97'), - }, + val: new Buffer('97') + } ]; qb.insertBatch('create_test', data, err => { @@ -73,9 +74,9 @@ suite('Dblite adapter tests -', () => { }); }); - /*--------------------------------------------------------------------------- - Promise Tests - ---------------------------------------------------------------------------*/ + // --------------------------------------------------------------------------- + // Promise Tests + // --------------------------------------------------------------------------- promiseTestRunner(qb); test('Promise - Select with function and argument in WHERE clause', () => { let promise = qb.select('id') @@ -90,16 +91,16 @@ suite('Dblite adapter tests -', () => { { id: 544, key: 3, - val: new Buffer('7'), + val: new Buffer('7') }, { id: 89, key: 34, - val: new Buffer('10 o\'clock'), + val: new Buffer('10 o\'clock') }, { id: 48, key: 403, - val: new Buffer('97'), - }, + val: new Buffer('97') + } ]; let promise = qb.query(qb.driver.truncate('create_test')).then( diff --git a/test/adapters/mysql2_test.js b/test/adapters/mysql2_test.js index aee01eb..eea59a2 100644 --- a/test/adapters/mysql2_test.js +++ b/test/adapters/mysql2_test.js @@ -1,10 +1,11 @@ +/* eslint-env node, mocha */ 'use strict'; // Load the test base const reload = require('require-reload')(require); reload.emptyCache(); const testBase = reload('../base'); -const expect = testBase.expect; +const expect = testBase.expect; const promiseTestRunner = testBase.promiseTestRunner; const testRunner = testBase.testRunner; @@ -22,9 +23,9 @@ suite('Mysql2 adapter tests -', () => { .to.be.deep.equal(qb); }); - //-------------------------------------------------------------------------- + // -------------------------------------------------------------------------- // Callback Tests - //-------------------------------------------------------------------------- + // -------------------------------------------------------------------------- testRunner(qb, (err, result, done) => { expect(err).is.not.ok; expect(result.rows).is.an('array'); @@ -42,21 +43,26 @@ suite('Mysql2 adapter tests -', () => { return done(); }); }); + test('Callback - Test Truncate', done => { + qb.truncate('create_test', (err, res) => { + return done(err); + }); + }); test('Callback - Test Insert Batch', done => { let data = [ { id: 5441, key: 3, - val: new Buffer('7'), + val: new Buffer('7') }, { id: 891, key: 34, - val: new Buffer('10 o\'clock'), + val: new Buffer('10 o\'clock') }, { id: 481, key: 403, - val: new Buffer('97'), - }, + val: new Buffer('97') + } ]; qb.insertBatch('create_test', data, (err, res) => { @@ -65,9 +71,9 @@ suite('Mysql2 adapter tests -', () => { }); }); - //--------------------------------------------------------------------------- + // --------------------------------------------------------------------------- // Promise Tests - //--------------------------------------------------------------------------- + // --------------------------------------------------------------------------- promiseTestRunner(qb); test('Promise - Select with function and argument in WHERE clause', () => { let promise = qb.select('id') @@ -77,22 +83,25 @@ suite('Mysql2 adapter tests -', () => { return expect(promise).to.be.fulfilled; }); - + test('Test Truncate', () => { + let promise = qb.truncate('create_test'); + return expect(promise).to.be.fullfilled; + }); test('Test Insert Batch', () => { let data = [ { id: 5442, key: 4, - val: new Buffer('7'), + val: new Buffer('7') }, { id: 892, key: 35, - val: new Buffer('10 o\'clock'), + val: new Buffer('10 o\'clock') }, { id: 482, key: 404, - val: 97, - }, + val: 97 + } ]; return expect(qb.insertBatch('create_test', data)).to.be.fulfilled; diff --git a/test/adapters/pg_test.js b/test/adapters/pg_test.js index c701685..40ea8a8 100644 --- a/test/adapters/pg_test.js +++ b/test/adapters/pg_test.js @@ -1,10 +1,11 @@ +/* eslint-env node, mocha */ 'use strict'; // Load the test base const reload = require('require-reload')(require); reload.emptyCache(); const testBase = reload('../base'); -const expect = testBase.expect; +const expect = testBase.expect; const promiseTestRunner = testBase.promiseTestRunner; const testRunner = testBase.testRunner; @@ -32,9 +33,20 @@ suite('Pg adapter tests -', () => { return expect(qb2).to.be.ok; }); - //-------------------------------------------------------------------------- + test('Test Connection Error', done => { + try { + reload('../../lib/NodeQuery')({}); + done(true); + } catch (e) { + expect(e).to.be.ok; + expect(e).is.an('Error'); + done(); + } + }); + + // -------------------------------------------------------------------------- // Callback Tests - //-------------------------------------------------------------------------- + // -------------------------------------------------------------------------- testRunner(qb, (err, result, done) => { expect(err).is.not.ok; expect(result.rows).is.an('array'); @@ -52,21 +64,27 @@ suite('Pg adapter tests -', () => { return done(err); }); }); + test('Callback - Test Truncate', done => { + qb.truncate('create_test', (err, res) => { + expect(err).is.not.ok; + return done(err); + }); + }); test('Callback - Test Insert Batch', done => { let data = [ { id: 5441, key: 3, - val: new Buffer('7'), + val: new Buffer('7') }, { id: 891, key: 34, - val: new Buffer('10 o\'clock'), + val: new Buffer('10 o\'clock') }, { id: 481, key: 403, - val: new Buffer('97'), - }, + val: new Buffer('97') + } ]; qb.insertBatch('create_test', data, (err, res) => { @@ -75,9 +93,9 @@ suite('Pg adapter tests -', () => { }); }); - //-------------------------------------------------------------------------- + // -------------------------------------------------------------------------- // Promise Tests - //-------------------------------------------------------------------------- + // -------------------------------------------------------------------------- promiseTestRunner(qb); test('Promise - Select with function and argument in WHERE clause', () => { let promise = qb.select('id') @@ -87,21 +105,25 @@ suite('Pg adapter tests -', () => { return expect(promise).to.be.fulfilled; }); + test('Promise - Test Truncate', () => { + let promise = qb.truncate('create_test'); + return expect(promise).to.be.fulfilled; + }); test('Promise - Test Insert Batch', () => { let data = [ { id: 544, key: 3, - val: new Buffer('7'), + val: new Buffer('7') }, { id: 89, key: 34, - val: new Buffer('10 o\'clock'), + val: new Buffer('10 o\'clock') }, { id: 48, key: 403, - val: new Buffer('97'), - }, + val: new Buffer('97') + } ]; let promise = qb.insertBatch('create_test', data); @@ -111,4 +133,4 @@ suite('Pg adapter tests -', () => { qb.end(); qb2.end(); }); -}); \ No newline at end of file +}); diff --git a/test/base.js b/test/base.js index 4e29ca7..dec1d99 100644 --- a/test/base.js +++ b/test/base.js @@ -12,5 +12,5 @@ module.exports = { expect: chai.expect, tests: require('./base/tests'), testRunner: require('./base/adapterCallbackTestRunner'), - promiseTestRunner: require('./base/adapterPromiseTestRunner'), -}; \ No newline at end of file + promiseTestRunner: require('./base/adapterPromiseTestRunner') +}; diff --git a/test/base/adapterCallbackTestRunner.js b/test/base/adapterCallbackTestRunner.js index 9304d7b..26bf487 100644 --- a/test/base/adapterCallbackTestRunner.js +++ b/test/base/adapterCallbackTestRunner.js @@ -1,6 +1,6 @@ +/* eslint-env node, mocha */ 'use strict'; -// jscs:disable // Load the test base const chai = require('chai'); const chaiAsPromised = require('chai-as-promised'); @@ -10,31 +10,30 @@ const expect = chai.expect; const reload = require('require-reload')(require); let tests = reload('../base/tests'); -let helpers = reload('../../lib/helpers'), - State = reload('../../lib/State'); +const helpers = reload('../../lib/helpers'); +const State = reload('../../lib/State'); -module.exports = function testRunner(qb, callback) { +module.exports = function testRunner (qb, callback) { Object.keys(tests).forEach(suiteName => { suite(suiteName, () => { let currentSuite = tests[suiteName]; Object.keys(currentSuite).forEach(testDesc => { test(`Callback - ${testDesc}`, done => { - let methodObj = currentSuite[testDesc]; - let methodNames = Object.keys(methodObj); - let lastMethodIndex = methodNames[methodNames.length - 1]; + const methodObj = currentSuite[testDesc]; + const methodNames = Object.keys(methodObj); + const lastMethodIndex = methodNames[methodNames.length - 1]; methodObj[lastMethodIndex].push((err, rows) => callback(err, rows, done)); methodNames.forEach(name => { - let args = methodObj[name], - method = qb[name]; + const args = methodObj[name]; + const method = qb[name]; if (args[0] === 'multiple') { args.shift(); args.forEach(argSet => { method.apply(qb, argSet); }); - } else { method.apply(qb, args); } @@ -57,7 +56,7 @@ module.exports = function testRunner(qb, callback) { qb.insert('create_test', { id: 587, key: 1, - val: new Buffer('2'), + val: new Buffer('2') }, (err, rows) => { return callback(err, rows, done); }); @@ -67,7 +66,7 @@ module.exports = function testRunner(qb, callback) { .update('create_test', { id: 7, key: 'gogle', - val: new Buffer('non-word'), + val: new Buffer('non-word') }, (err, rows) => { return callback(err, rows, done); }); @@ -76,7 +75,7 @@ module.exports = function testRunner(qb, callback) { let object = { id: 22, key: 'gogle', - val: new Buffer('non-word'), + val: new Buffer('non-word') }; qb.set(object) @@ -108,7 +107,7 @@ module.exports = function testRunner(qb, callback) { test('Callback - Delete multiple where values', done => { qb.delete('create_test', { id: 5, - key: 'gogle', + key: 'gogle' }, (err, rows) => { return callback(err, rows, done); }); @@ -227,4 +226,4 @@ module.exports = function testRunner(qb, callback) { expect(qb.getState()).to.be.deep.equal(state); }); }); -}; \ No newline at end of file +}; diff --git a/test/base/adapterPromiseTestRunner.js b/test/base/adapterPromiseTestRunner.js index 4ee2dc5..3a706e3 100644 --- a/test/base/adapterPromiseTestRunner.js +++ b/test/base/adapterPromiseTestRunner.js @@ -1,6 +1,6 @@ +/* eslint-env node, mocha */ 'use strict'; -// jscs:disable // Load the test base const chai = require('chai'); const chaiAsPromised = require('chai-as-promised'); @@ -8,32 +8,27 @@ chai.use(chaiAsPromised); const expect = chai.expect; const reload = require('require-reload')(require); -let tests = reload('../base/tests'); +const tests = reload('../base/tests'); -let helpers = reload('../../lib/helpers'), - State = reload('../../lib/State'); - -module.exports = function promiseTestRunner(qb) { +module.exports = function promiseTestRunner (qb) { Object.keys(tests).forEach(suiteName => { suite(suiteName, () => { let currentSuite = tests[suiteName]; Object.keys(currentSuite).forEach(testDesc => { test(`Promise - ${testDesc}`, () => { - let methodObj = currentSuite[testDesc]; - let methodNames = Object.keys(methodObj); - let lastMethodIndex = methodNames[methodNames.length - 1]; + const methodObj = currentSuite[testDesc]; + const methodNames = Object.keys(methodObj); let results = []; methodNames.forEach(name => { - let args = methodObj[name], - method = qb[name]; + const args = methodObj[name]; + const method = qb[name]; if (args[0] === 'multiple') { args.shift(); args.forEach(argSet => { results.push(method.apply(qb, argSet)); }); - } else { results.push(method.apply(qb, args)); } @@ -63,7 +58,7 @@ module.exports = function promiseTestRunner(qb) { let promise = qb.insert('create_test', { id: 587, key: 1, - val: new Buffer('2'), + val: new Buffer('2') }); return expect(promise).to.be.fulfilled; @@ -73,7 +68,7 @@ module.exports = function promiseTestRunner(qb) { .update('create_test', { id: 7, key: 'gogle', - val: new Buffer('non-word'), + val: new Buffer('non-word') }); return expect(promise).to.be.fulfilled; @@ -82,7 +77,7 @@ module.exports = function promiseTestRunner(qb) { let object = { id: 22, key: 'gogle', - val: new Buffer('non-word'), + val: new Buffer('non-word') }; let promise = qb.set(object) @@ -113,7 +108,7 @@ module.exports = function promiseTestRunner(qb) { test('Promise - Delete multiple where values', () => { let promise = qb.delete('create_test', { id: 5, - key: 'gogle', + key: 'gogle' }); return expect(promise).to.be.fulfilled; @@ -176,4 +171,4 @@ module.exports = function promiseTestRunner(qb) { return expect(promise).to.be.fulfilled; }); }); -}; \ No newline at end of file +}; diff --git a/test/base/tests.js b/test/base/tests.js index d4e3e3d..8583a8d 100644 --- a/test/base/tests.js +++ b/test/base/tests.js @@ -1,26 +1,25 @@ 'use strict'; -// jscs:disable module.exports = { 'Get tests -': { 'Get with function': { select: ['id, COUNT(id) as count'], from: ['create_test'], groupBy: ['id'], - get: [], + get: [] }, 'Basic select all get': { - get: ['create_test'], + get: ['create_test'] }, 'Basic select all with from': { from: ['create_test'], - get: [], + get: [] }, 'Get with limit': { - get: ['create_test', 2], + get: ['create_test', 2] }, 'Get with limit and offset': { - get: ['create_test', 2, 1], + get: ['create_test', 2, 1] }, 'Get with having': { select: ['id'], @@ -30,9 +29,9 @@ module.exports = { 'multiple', [{'id >': 1}], ['id !=', 3], - ['id', 900], + ['id', 900] ], - get: [], + get: [] }, 'Get with orHaving': { select: ['id'], @@ -40,8 +39,8 @@ module.exports = { groupBy: ['id'], having: [{'id >': 1}], orHaving: ['id !=', 3], - get: [], - }, + get: [] + } }, 'Select tests -': { 'Select where get': { @@ -49,94 +48,94 @@ module.exports = { where: [ 'multiple', ['id >', 1], - ['id <', 900], + ['id <', 900] ], - get: ['create_test', 2, 1], + get: ['create_test', 2, 1] }, 'Select where get 2': { select: ['id, key as k, val'], where: ['id !=', 1], - get: ['create_test', 2, 1], + get: ['create_test', 2, 1] }, 'Multi Order By': { from: ['create_test'], orderBy: ['id, key'], - get: [], + get: [] }, 'Select get': { select: ['id, key as k, val'], - get: ['create_test', 2, 1], + get: ['create_test', 2, 1] }, 'Select from get': { select: ['id, key as k, val'], from: ['create_test ct'], where: ['id >', 1], - get: [], + get: [] }, 'Select from limit get': { select: ['id, key as k, val'], from: ['create_test ct'], where: ['id >', 1], limit: [3], - get: [], + get: [] }, 'Select where IS NOT NULL': { select: ['id', 'key as k', 'val'], from: ['create_test ct'], whereIsNotNull: ['id'], - get: [], + get: [] }, 'Select where IS NULL': { select: ['id', 'key as k', 'val'], from: ['create_test ct'], whereIsNull: ['id'], - get: [], + get: [] }, 'Select where OR IS NOT NULL': { select: ['id', 'key as k', 'val'], from: ['create_test ct'], whereIsNull: ['id'], orWhereIsNotNull: ['id'], - get: [], + get: [] }, 'Select where OR IS NULL': { select: ['id', 'key as k', 'val'], from: ['create_test ct'], where: ['id', 3], orWhereIsNull: ['id'], - get: [], + get: [] }, 'Select with string where value': { select: ['id', 'key as k', 'val'], from: ['create_test ct'], where: ['id > 3'], - get: [], - }, + get: [] + } }, 'Where in tests -': { 'Where in': { from: ['create_test'], whereIn: ['id', [0, 6, 56, 563, 341]], - get: [], + get: [] }, 'Or Where in': { from: ['create_test'], where: ['key', 'false'], orWhereIn: ['id', [0, 6, 56, 563, 341]], - get: [], + get: [] }, 'Where Not in': { from: ['create_test'], where: ['key', 'false'], whereNotIn: ['id', [0, 6, 56, 563, 341]], - get: [], + get: [] }, 'Or Where Not in': { from: ['create_test'], where: ['key', 'false'], orWhereNotIn: ['id', [0, 6, 56, 563, 341]], - get: [], - }, + get: [] + } }, 'Query modifier tests -': { 'Order By': { @@ -145,15 +144,15 @@ module.exports = { where: [ 'multiple', ['id >', 0], - ['id <', 9000], + ['id <', 9000] ], orderBy: [ 'multiple', ['id', 'DESC'], - ['k', 'ASC'], + ['k', 'ASC'] ], limit: [5, 2], - get: [], + get: [] }, 'Group By': { select: ['id, key as k, val'], @@ -161,20 +160,20 @@ module.exports = { where: [ 'multiple', ['id >', 0], - ['id <', 9000], + ['id <', 9000] ], groupBy: [ 'multiple', ['k'], - [['id', 'val']], + [['id', 'val']] ], orderBy: [ 'multiple', ['id', 'DESC'], - ['k', 'ASC'], + ['k', 'ASC'] ], limit: [5, 2], - get: [], + get: [] }, 'Or Where': { select: ['id, key as k, val'], @@ -182,55 +181,55 @@ module.exports = { where: [' id ', 1], orWhere: ['key > ', 0], limit: [2, 1], - get: [], + get: [] }, Like: { from: ['create_test'], like: ['key', 'og'], - get: [], + get: [] }, 'Or Like': { from: ['create_test'], like: ['key', 'og'], orLike: ['key', 'val'], - get: [], + get: [] }, 'Not Like': { from: ['create_test'], like: ['key', 'og', 'before'], notLike: ['key', 'val'], - get: [], + get: [] }, 'Or Not Like': { from: ['create_test'], like: ['key', 'og', 'before'], orNotLike: ['key', 'val'], - get: [], + get: [] }, 'Like Before': { from: ['create_test'], like: ['key', 'og', 'before'], - get: [], + get: [] }, 'Like After': { from: ['create_test'], like: ['key', 'og', 'after'], - get: [], + get: [] }, 'Basic Join': { from: ['create_test ct'], join: ['create_join cj', 'cj.id=ct.id'], - get: [], + get: [] }, 'Left Join': { from: ['create_test ct'], join: ['create_join cj', 'cj.id=ct.id', 'left'], - get: [], + get: [] }, 'Inner Join': { from: ['create_test ct'], join: ['create_join cj', 'cj.id=ct.id', 'inner'], - get: [], + get: [] }, 'Join with multiple where values': { from: ['create_test ct'], @@ -238,10 +237,10 @@ module.exports = { where: [ { 'ct.id < ': 3, - 'ct.key ': 'foo', - }, + 'ct.key ': 'foo' + } ], - get: [], - }, - }, -}; \ No newline at end of file + get: [] + } + } +}; diff --git a/test/base_test.js b/test/base_test.js index 883813c..bcaab45 100644 --- a/test/base_test.js +++ b/test/base_test.js @@ -1,10 +1,11 @@ +/* eslint-env node, mocha */ 'use strict'; -let expect = require('chai').expect, - reload = require('require-reload')(require), - glob = require('glob'), - nodeQuery = reload('../lib/NodeQuery')(), - Adapter = reload('../lib/Adapter'); +const expect = require('chai').expect; +const reload = require('require-reload')(require); +const glob = require('glob'); +const nodeQuery = reload('../lib/NodeQuery')(); +const Adapter = reload('../lib/Adapter'); suite('Base tests -', () => { suite('Sanity check', () => { @@ -31,7 +32,7 @@ suite('Base tests -', () => { test('Invalid driver type', () => { expect(() => { reload('../lib/NodeQuery')({ - driver: 'Foo', + driver: 'Foo' }); }).to.throw(Error, 'Selected driver (Foo) does not exist!'); }); @@ -49,4 +50,4 @@ suite('Base tests -', () => { a.transformResult([]); }).to.throw(Error, 'Result transformer method not defined for current adapter'); }); -}); \ No newline at end of file +}); diff --git a/test/helpers_test.js b/test/helpers_test.js index 2e5a0b6..f44d4e2 100644 --- a/test/helpers_test.js +++ b/test/helpers_test.js @@ -1,9 +1,9 @@ +/* eslint-env node, mocha */ 'use strict'; -let chai = require('chai'), - assert = chai.assert, - expect = chai.expect, - should = chai.should(); +const chai = require('chai'); +const assert = chai.assert; +const expect = chai.expect; let helpers = require('../lib/helpers'); @@ -35,7 +35,7 @@ suite('Helper Module Tests -', () => { 'Function', 'RegExp', 'NaN', - 'Infinite', + 'Infinite' ]; types.forEach(type => { @@ -48,7 +48,7 @@ suite('Helper Module Tests -', () => { let trueCases = { 'Strings are scalar': 'foo', 'Booleans are scalar': true, - 'Numbers are scalar': 545, + 'Numbers are scalar': 545 }; Object.keys(trueCases).forEach(desc => { test(desc, () => { @@ -58,7 +58,7 @@ suite('Helper Module Tests -', () => { let falseCases = { 'Arrays are not scalar': [], - 'Objects are not scalar': [], + 'Objects are not scalar': [] }; Object.keys(falseCases).forEach(desc => { test(desc, () => { @@ -95,14 +95,14 @@ suite('Helper Module Tests -', () => { suite('arrayPluck -', () => { let orig = [ { - foo: 1, + foo: 1 }, { foo: 2, - bar: 10, + bar: 10 }, { foo: 3, - bar: 15, - }, + bar: 15 + } ]; test('Finding members in all objects', () => { @@ -121,11 +121,11 @@ suite('Helper Module Tests -', () => { let cases = [ { 'Dollar sign is not in any of the array items': /\$/, - 'None of the numbers in the array match /5/': /5/, + 'None of the numbers in the array match /5/': /5/ }, { '\' string \' matches /^ ?string/': /^ ?string/, - '\'apple\' matches /APPLE/i': /APPLE/i, - }, + '\'apple\' matches /APPLE/i': /APPLE/i + } ]; [0, 1].forEach(i => { diff --git a/test/query-parser_test.js b/test/query-parser_test.js index f4d50e1..ea23447 100644 --- a/test/query-parser_test.js +++ b/test/query-parser_test.js @@ -1,20 +1,21 @@ +/* eslint-env node, mocha */ 'use strict'; -let expect = require('chai').expect; +const expect = require('chai').expect; // Use the base driver as a mock for testing -let getArgs = require('getargs'); -let helpers = require('../lib/helpers'); -let driver = require('../lib/Driver'); +const getArgs = require('getargs'); +const helpers = require('../lib/helpers'); +const driver = require('../lib/Driver'); -let P = require('../lib/QueryParser'); +const P = require('../lib/QueryParser'); let parser = new P(driver); -let State = require('../lib/State'); +const State = require('../lib/State'); // Simulate query builder state let state = new State(); -let mixedSet = function mixedSet(/* $letName, $valType, $key, [$val] */) { +let mixedSet = function mixedSet (/* $letName, $valType, $key, [$val] */) { const argPattern = '$letName:string, $valType:string, $key:object|string|number, [$val]'; let args = getArgs(argPattern, arguments); @@ -86,7 +87,7 @@ suite('Query Parser Tests', () => { }); test('Has function key/val object', () => { whereMock({ - 'time <': 'SUM(FOO(BAR(\'x\')))', + 'time <': 'SUM(FOO(BAR(\'x\')))' }); parser.parseWhere(driver, state); expect(state.whereMap) @@ -94,7 +95,7 @@ suite('Query Parser Tests', () => { }); test('Has literal value', () => { whereMock({ - foo: 3, + foo: 3 }); parser.parseWhere(driver, state); expect(state.whereMap) @@ -105,7 +106,7 @@ suite('Query Parser Tests', () => { test('Has multiple literal values', () => { whereMock({ foo: 3, - bar: 5, + bar: 5 }); parser.parseWhere(driver, state); expect(state.whereMap) @@ -119,20 +120,20 @@ suite('Query Parser Tests', () => { { desc: 'Simple equals condition', join: 'table1.field1=table2.field2', - expected: ['table1.field1', '=', 'table2.field2'], + expected: ['table1.field1', '=', 'table2.field2'] }, { desc: 'Db.table.field condition', join: 'db1.table1.field1!=db2.table2.field2', - expected: ['db1.table1.field1', '!=', 'db2.table2.field2'], + expected: ['db1.table1.field1', '!=', 'db2.table2.field2'] }, { desc: 'Underscore in identifier', join: 'table_1.field1 = tab_le2.field_2', - expected: ['table_1.field1', '=', 'tab_le2.field_2'], + expected: ['table_1.field1', '=', 'tab_le2.field_2'] }, { desc: 'Function in condition', join: 'table1.field1 > SUM(3+6)', - expected: ['table1.field1', '>', 'SUM(3+6)'], - }, + expected: ['table1.field1', '>', 'SUM(3+6)'] + } ]; data.forEach(datum => { @@ -147,20 +148,20 @@ suite('Query Parser Tests', () => { { desc: 'Simple equals condition', clause: 'table1.field1=table2.field2', - expected: '"table1"."field1" = "table2"."field2"', + expected: '"table1"."field1" = "table2"."field2"' }, { desc: 'Db.table.field condition', clause: 'db1.table1.field1!=db2.table2.field2', - expected: '"db1"."table1"."field1" != "db2"."table2"."field2"', + expected: '"db1"."table1"."field1" != "db2"."table2"."field2"' }, { desc: 'Underscore in identifier', clause: 'table_1.field1 = tab_le2.field_2', - expected: '"table_1"."field1" = "tab_le2"."field_2"', + expected: '"table_1"."field1" = "tab_le2"."field_2"' }, { desc: 'Function in condition', clause: 'table1.field1 > SUM(3+6)', - expected: '"table1"."field1" > SUM(3+6)', - }, + expected: '"table1"."field1" > SUM(3+6)' + } ]; data.forEach(datum => { @@ -170,4 +171,4 @@ suite('Query Parser Tests', () => { }); }); }); -}); \ No newline at end of file +});