Update dependencies and switch to Happiness code style

This commit is contained in:
Timothy Warren 2016-09-14 16:50:32 -04:00
parent d3e5da66c4
commit 8d7e4aaa8c
31 changed files with 674 additions and 656 deletions

View File

@ -1,7 +0,0 @@
{
"preset": "airbnb",
"validateIndentation": null,
"requireLineFeedAtFileEnd": null,
"disallowSpaceAfterPrefixUnaryOperators": null,
"disallowMultipleVarDecl": null
}

2
API.md
View File

@ -1,3 +1,5 @@
<!-- Generated by documentation.js. Update this documentation by updating the source code. -->
# limit # limit
Set the limit clause Set the limit clause

View File

@ -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 `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) * 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)

View File

@ -14,7 +14,7 @@ class Adapter {
* @constructor * @constructor
* @param {Object} instance - The connection object * @param {Object} instance - The connection object
*/ */
constructor(instance) { constructor (instance) {
this.instance = instance; this.instance = instance;
} }
@ -26,7 +26,7 @@ class Adapter {
* @param {Function} [callback] - Callback to run when a response is recieved * @param {Function} [callback] - Callback to run when a response is recieved
* @return {void|Promise} - returns a promise if no callback is passed * @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'); 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 * @param {*} originalResult - the original result object from the driver
* @return {Result} - the new result object * @return {Result} - the new result object
*/ */
transformResult(originalResult) { transformResult (originalResult) {
throw new Error('Result transformer method not defined for current adapter'); throw new Error('Result transformer method not defined for current adapter');
} }
@ -44,7 +44,7 @@ class Adapter {
* Close the current database connection * Close the current database connection
* @return {void} * @return {void}
*/ */
close() { close () {
this.instance.end(); this.instance.end();
} }
} }

View File

@ -7,7 +7,7 @@ const helpers = require('./helpers');
* *
* @private * @private
*/ */
let Driver = { const Driver = {
identifierStartChar: '"', identifierStartChar: '"',
identifierEndChar: '"', identifierEndChar: '"',
tablePrefix: null, tablePrefix: null,
@ -20,9 +20,9 @@ let Driver = {
* @return {String} - The quoted sql fragment * @return {String} - The quoted sql fragment
* @private * @private
*/ */
_quote(str) { _quote (str) {
return (helpers.isString(str) return (helpers.isString(str) &&
&& ! (str.startsWith(Driver.identifierStartChar) || str.endsWith(Driver.identifierEndChar)) !(str.startsWith(Driver.identifierStartChar) || str.endsWith(Driver.identifierEndChar))
) )
? `${Driver.identifierStartChar}${str}${Driver.identifierEndChar}` ? `${Driver.identifierStartChar}${str}${Driver.identifierEndChar}`
: str; : str;
@ -36,11 +36,10 @@ let Driver = {
* @param {Number} [offset] - Number of rows to skip * @param {Number} [offset] - Number of rows to skip
* @return {String} - Modified SQL statement * @return {String} - Modified SQL statement
*/ */
limit(sql, limit, offset) { limit (sql, limit, offset) {
sql += ` LIMIT ${limit}`; sql += ` LIMIT ${limit}`;
if (helpers.isNumber(offset)) if (helpers.isNumber(offset)) {
{
sql += ` OFFSET ${offset}`; sql += ` OFFSET ${offset}`;
} }
@ -53,7 +52,7 @@ let Driver = {
* @param {String} table - Table name to quote * @param {String} table - Table name to quote
* @return {String} - Quoted table name * @return {String} - Quoted table name
*/ */
quoteTable(table) { quoteTable (table) {
// Quote after prefix // Quote after prefix
return Driver.quoteIdentifiers(table); return Driver.quoteIdentifiers(table);
}, },
@ -64,22 +63,20 @@ let Driver = {
* @param {String|Array} str - String or array of strings to quote identifiers * @param {String|Array} str - String or array of strings to quote identifiers
* @return {String|Array} - Quoted identifier(s) * @return {String|Array} - Quoted identifier(s)
*/ */
quoteIdentifiers(str) { quoteIdentifiers (str) {
let hiers, raw; let hiers, raw;
let pattern = new RegExp( let pattern = new RegExp(
`${Driver.identifierStartChar}(` `${Driver.identifierStartChar}(` +
+ '([a-zA-Z0-9_]+)' + '(\((.*?)\))' '([a-zA-Z0-9_]+)' + '(((.*?)))' +
+ `)${Driver.identifierEndChar}`, 'ig'); `)${Driver.identifierEndChar}`, 'ig');
// Recurse for arrays of identifiiers // Recurse for arrays of identifiiers
if (Array.isArray(str)) if (Array.isArray(str)) {
{
return str.map(Driver.quoteIdentifiers); return str.map(Driver.quoteIdentifiers);
} }
// Handle commas // Handle commas
if (str.includes(',')) if (str.includes(',')) {
{
let parts = str.split(',').map(helpers.stringTrim); let parts = str.split(',').map(helpers.stringTrim);
str = parts.map(Driver.quoteIdentifiers).join(','); str = parts.map(Driver.quoteIdentifiers).join(',');
} }
@ -89,8 +86,7 @@ let Driver = {
raw = hiers.join('.'); raw = hiers.join('.');
// Fix functions // Fix functions
if (raw.includes('(') && raw.includes(')')) if (raw.includes('(') && raw.includes(')')) {
{
let funcs = pattern.exec(raw); let funcs = pattern.exec(raw);
// Unquote the function // Unquote the function
@ -110,7 +106,7 @@ let Driver = {
* @param {String} table - Table to truncate * @param {String} table - Table to truncate
* @return {String} - Truncation SQL * @return {String} - Truncation SQL
*/ */
truncate(table) { truncate (table) {
let sql = (Driver.hasTruncate) let sql = (Driver.hasTruncate)
? 'TRUNCATE ' ? 'TRUNCATE '
: 'DELETE FROM '; : 'DELETE FROM ';
@ -127,13 +123,10 @@ let Driver = {
* @param {Array} [data] - The array of object containing data to insert * @param {Array} [data] - The array of object containing data to insert
* @return {String} - Query and data to insert * @return {String} - Query and data to insert
*/ */
insertBatch(table, data) { insertBatch (table, data) {
let vals = [], const vals = [];
fields = Object.keys(data[0]), const fields = Object.keys(data[0]);
sql = '', let sql = '';
params = [],
paramString = '',
paramList = [];
// Get the data values to insert, so they can // Get the data values to insert, so they can
// be parameterized // be parameterized
@ -150,17 +143,17 @@ let Driver = {
sql += `INSERT INTO ${table} (${Driver.quoteIdentifiers(fields).join(',')}) VALUES `; sql += `INSERT INTO ${table} (${Driver.quoteIdentifiers(fields).join(',')}) VALUES `;
// Create placeholder groups // Create placeholder groups
params = Array(fields.length).fill('?'); let params = Array(fields.length).fill('?');
paramString = `(${params.join(',')})`; let paramString = `(${params.join(',')})`;
paramList = Array(data.length).fill(paramString); let paramList = Array(data.length).fill(paramString);
sql += paramList.join(','); sql += paramList.join(',');
return { return {
sql: sql, sql: sql,
values: vals, values: vals
}; };
}, }
}; };
module.exports = Driver; module.exports = Driver;

View File

@ -1,6 +1,5 @@
'use strict'; 'use strict';
const helpers = require('./helpers');
const QueryBuilder = require('./QueryBuilder'); const QueryBuilder = require('./QueryBuilder');
// Map config driver name to code class name // Map config driver name to code class name
@ -14,7 +13,7 @@ const dbDriverMap = new Map([
['postgres', 'Pg'], ['postgres', 'Pg'],
['pg', 'Pg'], ['pg', 'Pg'],
['sqlite3', 'Sqlite'], ['sqlite3', 'Sqlite'],
['sqlite', 'Sqlite'], ['sqlite', 'Sqlite']
]); ]);
/** /**
@ -23,7 +22,6 @@ const dbDriverMap = new Map([
* @param {object} config - connection parameters * @param {object} config - connection parameters
*/ */
class NodeQuery { class NodeQuery {
/** /**
* Constructor * Constructor
* *
@ -42,20 +40,20 @@ class NodeQuery {
* connection: ':memory:' * connection: ':memory:'
* }); * });
*/ */
constructor(config) { constructor (config) {
this.instance = null; this.instance = null;
if (config != null) { if (config != null) {
let drivername = dbDriverMap.get(config.driver); let drivername = dbDriverMap.get(config.driver);
if (! drivername) { if (!drivername) {
throw new Error(`Selected driver (${config.driver}) does not exist!`); throw new Error(`Selected driver (${config.driver}) does not exist!`);
} }
let driver = require(`./drivers/${drivername}`); const driver = require(`./drivers/${drivername}`);
let $adapter = require(`./adapters/${drivername}`); const Adapter = require(`./adapters/${drivername}`);
let adapter = new $adapter(config.connection); let adapter = new Adapter(config.connection);
this.instance = new QueryBuilder(driver, adapter); this.instance = new QueryBuilder(driver, adapter);
} }
} }
@ -65,7 +63,7 @@ class NodeQuery {
* *
* @return {QueryBuilder} - The Query Builder object * @return {QueryBuilder} - The Query Builder object
*/ */
getQuery() { getQuery () {
if (this.instance == null) { if (this.instance == null) {
throw new Error('No Query Builder instance to return'); throw new Error('No Query Builder instance to return');
} }
@ -74,4 +72,4 @@ class NodeQuery {
} }
} }
module.exports = (config => new NodeQuery(config)); module.exports = config => new NodeQuery(config);

View File

@ -2,308 +2,16 @@
const getArgs = require('getargs'); const getArgs = require('getargs');
const helpers = require('./helpers'); const helpers = require('./helpers');
const State = require('./State'); const QueryBuilderBase = require('./QueryBuilderBase');
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();
}
}
/** /**
* Main object that builds SQL queries. * Main object that builds SQL queries.
* *
* @param {Driver} Driver - The syntax driver for the database * @param {Driver} Driver - The syntax driver for the database
* @param {Adapter} Adapter - The database module adapter for running queries * @param {Adapter} Adapter - The database module adapter for running queries
* @extends QueryBuilderBase
*/ */
class QueryBuilder 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 // ! Miscellaneous Methods
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@ -316,7 +24,7 @@ class QueryBuilder extends QueryBuilderBase {
* @param {function} [callback] - Optional callback * @param {function} [callback] - Optional callback
* @return {void|Promise} - Returns a promise if no callback is supplied * @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); return this.adapter.execute.apply(this.adapter, arguments);
} }
@ -325,7 +33,7 @@ class QueryBuilder extends QueryBuilderBase {
* *
* @return {void} * @return {void}
*/ */
resetQuery() { resetQuery () {
this._resetState(); this._resetState();
} }
@ -335,7 +43,7 @@ class QueryBuilder extends QueryBuilderBase {
* @private * @private
* @return {Object} - The State object * @return {Object} - The State object
*/ */
getState() { getState () {
return this.state; return this.state;
} }
@ -346,7 +54,7 @@ class QueryBuilder extends QueryBuilderBase {
* @param {function} [callback] - Optional callback * @param {function} [callback] - Optional callback
* @return {void|Promise} - Returns a promise if no callback is supplied * @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); getArgs('table:string, [callback]:function', arguments);
let args = [].slice.apply(arguments); let args = [].slice.apply(arguments);
let sql = this.driver.truncate(args.shift()); let sql = this.driver.truncate(args.shift());
@ -359,7 +67,7 @@ class QueryBuilder extends QueryBuilderBase {
* *
* @return {void} * @return {void}
*/ */
end() { end () {
this.adapter.close(); this.adapter.close();
} }
@ -375,8 +83,7 @@ class QueryBuilder extends QueryBuilderBase {
* @example query.select(['foo', 'bar']); // Select multiple fileds with an array * @example query.select(['foo', 'bar']); // Select multiple fileds with an array
* @return {QueryBuilder} - The Query Builder object, for chaining * @return {QueryBuilder} - The Query Builder object, for chaining
*/ */
select(fields) { select (fields) {
// Split/trim fields by comma // Split/trim fields by comma
fields = (Array.isArray(fields)) fields = (Array.isArray(fields))
? fields ? fields
@ -411,7 +118,7 @@ class QueryBuilder extends QueryBuilderBase {
* @example query.from('tableName t'); // Select the table with an alias * @example query.from('tableName t'); // Select the table with an alias
* @return {QueryBuilder} - The Query Builder object, for chaining * @return {QueryBuilder} - The Query Builder object, for chaining
*/ */
from(tableName) { from (tableName) {
// Split identifiers on spaces // Split identifiers on spaces
let identArray = tableName.trim().split(' ').map(helpers.stringTrim); 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 * @param {String} [pos=both] - The placement of the wildcard character(s): before, after, or both
* @return {QueryBuilder} - The Query Builder object, for chaining * @return {QueryBuilder} - The Query Builder object, for chaining
*/ */
like(field, val, pos) { like (field, val, pos) {
this._like(field, val, pos, ' LIKE ', 'AND'); this._like(field, val, pos, ' LIKE ', 'AND');
return this; 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 * @param {String} [pos=both] - The placement of the wildcard character(s): before, after, or both
* @return {QueryBuilder} - The Query Builder object, for chaining * @return {QueryBuilder} - The Query Builder object, for chaining
*/ */
notLike(field, val, pos) { notLike (field, val, pos) {
this._like(field, val, pos, ' NOT LIKE ', 'AND'); this._like(field, val, pos, ' NOT LIKE ', 'AND');
return this; 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 * @param {String} [pos=both] - The placement of the wildcard character(s): before, after, or both
* @return {QueryBuilder} - The Query Builder object, for chaining * @return {QueryBuilder} - The Query Builder object, for chaining
*/ */
orLike(field, val, pos) { orLike (field, val, pos) {
this._like(field, val, pos, ' LIKE ', 'OR'); this._like(field, val, pos, ' LIKE ', 'OR');
return this; 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 * @param {String} [pos=both] - The placement of the wildcard character(s): before, after, or both
* @return {QueryBuilder} - The Query Builder object, for chaining * @return {QueryBuilder} - The Query Builder object, for chaining
*/ */
orNotLike(field, val, pos) { orNotLike (field, val, pos) {
this._like(field, val, pos, ' NOT LIKE ', 'OR'); this._like(field, val, pos, ' NOT LIKE ', 'OR');
return this; 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 * @param {String|Number} [val] - The value to compare if the value of key is a string
* @return {QueryBuilder} - The Query Builder object, for chaining * @return {QueryBuilder} - The Query Builder object, for chaining
*/ */
having(/*key, [val]*/) { having (/* key, [val] */) {
let args = getArgs('key:string|object, [val]:string|number', arguments); let args = getArgs('key:string|object, [val]:string|number', arguments);
this._having(args.key, args.val, 'AND'); 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 * @param {String|Number} [val] - The value to compare if the value of key is a string
* @return {QueryBuilder} - The Query Builder object, for chaining * @return {QueryBuilder} - The Query Builder object, for chaining
*/ */
orHaving(/*key, [val]*/) { orHaving (/* key, [val] */) {
let args = getArgs('key:string|object, [val]:string|number', arguments); let args = getArgs('key:string|object, [val]:string|number', arguments);
this._having(args.key, args.val, 'OR'); 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 * @param {String|Number} [val] - The value to compare if the value of key is a string
* @return {QueryBuilder} - The Query Builder object, for chaining * @return {QueryBuilder} - The Query Builder object, for chaining
*/ */
where(key, val) { where (key, val) {
this._where(key, val, 'AND'); this._where(key, val, 'AND');
return this; 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 * @param {String|Number} [val] - The value to compare if the value of key is a string
* @return {QueryBuilder} - The Query Builder object, for chaining * @return {QueryBuilder} - The Query Builder object, for chaining
*/ */
orWhere(key, val) { orWhere (key, val) {
this._where(key, val, 'OR'); this._where(key, val, 'OR');
return this; return this;
} }
@ -535,7 +242,7 @@ class QueryBuilder extends QueryBuilderBase {
* @param {String} field - The name of the field that has a NULL value * @param {String} field - The name of the field that has a NULL value
* @return {QueryBuilder} - The Query Builder object, for chaining * @return {QueryBuilder} - The Query Builder object, for chaining
*/ */
whereIsNull(field) { whereIsNull (field) {
this._whereNull(field, 'IS NULL', 'AND'); this._whereNull(field, 'IS NULL', 'AND');
return this; return this;
} }
@ -546,7 +253,7 @@ class QueryBuilder extends QueryBuilderBase {
* @param {String} field - The name so the field that is not to be null * @param {String} field - The name so the field that is not to be null
* @return {QueryBuilder} - The Query Builder object, for chaining * @return {QueryBuilder} - The Query Builder object, for chaining
*/ */
whereIsNotNull(field) { whereIsNotNull (field) {
this._whereNull(field, 'IS NOT NULL', 'AND'); this._whereNull(field, 'IS NOT NULL', 'AND');
return this; return this;
} }
@ -557,7 +264,7 @@ class QueryBuilder extends QueryBuilderBase {
* @param {String} field - The name of the field * @param {String} field - The name of the field
* @return {QueryBuilder} - The Query Builder object, for chaining * @return {QueryBuilder} - The Query Builder object, for chaining
*/ */
orWhereIsNull(field) { orWhereIsNull (field) {
this._whereNull(field, 'IS NULL', 'OR'); this._whereNull(field, 'IS NULL', 'OR');
return this; return this;
} }
@ -568,7 +275,7 @@ class QueryBuilder extends QueryBuilderBase {
* @param {String} field - The name of the field * @param {String} field - The name of the field
* @return {QueryBuilder} - The Query Builder object, for chaining * @return {QueryBuilder} - The Query Builder object, for chaining
*/ */
orWhereIsNotNull(field) { orWhereIsNotNull (field) {
this._whereNull(field, 'IS NOT NULL', 'OR'); this._whereNull(field, 'IS NOT NULL', 'OR');
return this; return this;
} }
@ -580,7 +287,7 @@ class QueryBuilder extends QueryBuilderBase {
* @param {Array} values - the array of items to search in * @param {Array} values - the array of items to search in
* @return {QueryBuilder} - The Query Builder object, for chaining * @return {QueryBuilder} - The Query Builder object, for chaining
*/ */
whereIn(key, values) { whereIn (key, values) {
this._whereIn(key, values, 'IN', 'AND'); this._whereIn(key, values, 'IN', 'AND');
return this; return this;
} }
@ -592,7 +299,7 @@ class QueryBuilder extends QueryBuilderBase {
* @param {Array} values - the array of items to search in * @param {Array} values - the array of items to search in
* @return {QueryBuilder} - The Query Builder object, for chaining * @return {QueryBuilder} - The Query Builder object, for chaining
*/ */
orWhereIn(key, values) { orWhereIn (key, values) {
this._whereIn(key, values, 'IN', 'OR'); this._whereIn(key, values, 'IN', 'OR');
return this; return this;
} }
@ -604,7 +311,7 @@ class QueryBuilder extends QueryBuilderBase {
* @param {Array} values - the array of items to search in * @param {Array} values - the array of items to search in
* @return {QueryBuilder} - The Query Builder object, for chaining * @return {QueryBuilder} - The Query Builder object, for chaining
*/ */
whereNotIn(key, values) { whereNotIn (key, values) {
this._whereIn(key, values, 'NOT IN', 'AND'); this._whereIn(key, values, 'NOT IN', 'AND');
return this; return this;
} }
@ -616,7 +323,7 @@ class QueryBuilder extends QueryBuilderBase {
* @param {Array} values - the array of items to search in * @param {Array} values - the array of items to search in
* @return {QueryBuilder} - The Query Builder object, for chaining * @return {QueryBuilder} - The Query Builder object, for chaining
*/ */
orWhereNotIn(key, values) { orWhereNotIn (key, values) {
this._whereIn(key, values, 'NOT IN', 'OR'); this._whereIn(key, values, 'NOT IN', 'OR');
return this; return this;
} }
@ -630,7 +337,7 @@ class QueryBuilder extends QueryBuilderBase {
* @example query.set({foo:'bar'}); // Set with an object * @example query.set({foo:'bar'}); // Set with an object
* @return {QueryBuilder} - The Query Builder object, for chaining * @return {QueryBuilder} - The Query Builder object, for chaining
*/ */
set(/* $key, [$val] */) { set (/* $key, [$val] */) {
let args = getArgs('$key, [$val]', arguments); let args = getArgs('$key, [$val]', arguments);
// Set the appropriate state variables // 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 * @param {String} [type='inner'] - The type of join, which defaults to inner
* @return {QueryBuilder} - The Query Builder object, for chaining * @return {QueryBuilder} - The Query Builder object, for chaining
*/ */
join(table, cond, type) { join (table, cond, type) {
type = type || 'inner'; type = type || 'inner';
// Prefix/quote table name // Prefix/quote table name
@ -681,8 +388,8 @@ class QueryBuilder extends QueryBuilderBase {
* @param {String|Array} field - The name of the field to group by * @param {String|Array} field - The name of the field to group by
* @return {QueryBuilder} - The Query Builder object, for chaining * @return {QueryBuilder} - The Query Builder object, for chaining
*/ */
groupBy(field) { groupBy (field) {
if (! helpers.isScalar(field)) { if (!helpers.isScalar(field)) {
let newGroupArray = field.map(this.driver.quoteIdentifiers); let newGroupArray = field.map(this.driver.quoteIdentifiers);
this.state.groupArray = this.state.groupArray.concat(newGroupArray); this.state.groupArray = this.state.groupArray.concat(newGroupArray);
} else { } else {
@ -701,7 +408,7 @@ class QueryBuilder extends QueryBuilderBase {
* @param {String} [type='ASC'] - The order direction, ASC or DESC * @param {String} [type='ASC'] - The order direction, ASC or DESC
* @return {QueryBuilder} - The Query Builder object, for chaining * @return {QueryBuilder} - The Query Builder object, for chaining
*/ */
orderBy(field, type) { orderBy (field, type) {
type = type || 'ASC'; type = type || 'ASC';
// Set the fields for later manipulation // Set the fields for later manipulation
@ -729,7 +436,7 @@ class QueryBuilder extends QueryBuilderBase {
* @param {Number} [offset] - The row number to start from * @param {Number} [offset] - The row number to start from
* @return {QueryBuilder} - The Query Builder object, for chaining * @return {QueryBuilder} - The Query Builder object, for chaining
*/ */
limit(limit, offset) { limit (limit, offset) {
this.state.limit = limit; this.state.limit = limit;
this.state.offset = offset || null; this.state.offset = offset || null;
@ -741,7 +448,7 @@ class QueryBuilder extends QueryBuilderBase {
* *
* @return {QueryBuilder} - The Query Builder object, for chaining * @return {QueryBuilder} - The Query Builder object, for chaining
*/ */
groupStart() { groupStart () {
let conj = (this.state.queryMap.length < 1) ? ' WHERE ' : ' AND '; let conj = (this.state.queryMap.length < 1) ? ' WHERE ' : ' AND ';
this._appendMap(conj, '(', 'groupStart'); this._appendMap(conj, '(', 'groupStart');
@ -754,7 +461,7 @@ class QueryBuilder extends QueryBuilderBase {
* *
* @return {QueryBuilder} - The Query Builder object, for chaining * @return {QueryBuilder} - The Query Builder object, for chaining
*/ */
orGroupStart() { orGroupStart () {
this._appendMap('', ' OR (', 'groupStart'); this._appendMap('', ' OR (', 'groupStart');
return this; return this;
@ -766,7 +473,7 @@ class QueryBuilder extends QueryBuilderBase {
* *
* @return {QueryBuilder} - The Query Builder object, for chaining * @return {QueryBuilder} - The Query Builder object, for chaining
*/ */
orNotGroupStart() { orNotGroupStart () {
this._appendMap('', ' OR NOT (', 'groupStart'); this._appendMap('', ' OR NOT (', 'groupStart');
return this; return this;
@ -777,7 +484,7 @@ class QueryBuilder extends QueryBuilderBase {
* *
* @return {QueryBuilder} - The Query Builder object, for chaining * @return {QueryBuilder} - The Query Builder object, for chaining
*/ */
groupEnd() { groupEnd () {
this._appendMap('', ')', 'groupEnd'); this._appendMap('', ')', 'groupEnd');
return this; return this;
@ -799,7 +506,7 @@ class QueryBuilder extends QueryBuilderBase {
* @example query.get(callback); // Get the results of a query generated with other methods * @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 * @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'; const argPattern = '[table]:string, [limit]:number, [offset]:number, [callback]:function';
let args = getArgs(argPattern, arguments); let args = getArgs(argPattern, arguments);
@ -823,7 +530,7 @@ class QueryBuilder extends QueryBuilderBase {
* @param {Function} [callback] - Callback for handling response from the database * @param {Function} [callback] - Callback for handling response from the database
* @return {void|Promise} - If no callback is passed, a promise is returned * @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); let args = getArgs('table:string, [data]:object, [callback]:function', arguments);
if (args.data) { if (args.data) {
@ -845,7 +552,7 @@ class QueryBuilder extends QueryBuilderBase {
*.then(promiseCallback); *.then(promiseCallback);
* @return {void|Promise} - If no callback is passed, a promise is returned * @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 args = getArgs('table:string, data:array, [callback]:function', arguments);
let batch = this.driver.insertBatch(args.table, args.data); 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 * @param {Function} [callback] - Callback for handling response from the database
* @return {void|Promise} - If no callback is passed, a promise is returned * @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); let args = getArgs('table:string, [data]:object, [callback]:function', arguments);
if (args.data) { if (args.data) {
@ -880,7 +587,7 @@ class QueryBuilder extends QueryBuilderBase {
* @param {Function} [callback] - Callback for handling response from the database * @param {Function} [callback] - Callback for handling response from the database
* @return {void|Promise} - If no callback is passed, a promise is returned * @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); let args = getArgs('table:string, [where]:object, [callback]:function', arguments);
if (args.where) { 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 * @param {Boolean} [reset=true] - Whether to reset the query builder so another query can be built
* @return {String} - The compiled sql statement * @return {String} - The compiled sql statement
*/ */
getCompiledSelect(/*table, reset*/) { getCompiledSelect (/* table, reset */) {
let args = getArgs('[table]:string, [reset]:boolean', arguments); let args = getArgs('[table]:string, [reset]:boolean', arguments);
if (args.table) { if (args.table) {
this.from(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 * @param {Boolean} [reset=true] - Whether to reset the query builder so another query can be built
* @return {String} - The compiled sql statement * @return {String} - The compiled sql statement
*/ */
getCompiledInsert(table, reset) { getCompiledInsert (table, reset) {
return this._getCompile('insert', this.driver.quoteTable(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 * @param {Boolean} [reset=true] - Whether to reset the query builder so another query can be built
* @return {String} - The compiled sql statement * @return {String} - The compiled sql statement
*/ */
getCompiledUpdate(table, reset) { getCompiledUpdate (table, reset) {
return this._getCompile('update', this.driver.quoteTable(table), reset); return this._getCompile('update', this.driver.quoteTable(table), reset);
} }
@ -940,7 +647,7 @@ class QueryBuilder extends QueryBuilderBase {
* @param {Boolean} [reset=true] - Whether to reset the query builder so another query can be built * @param {Boolean} [reset=true] - Whether to reset the query builder so another query can be built
* @return {String} - The compiled sql statement * @return {String} - The compiled sql statement
*/ */
getCompiledDelete(table, reset) { getCompiledDelete (table, reset) {
return this._getCompile('delete', this.driver.quoteTable(table), reset); return this._getCompile('delete', this.driver.quoteTable(table), reset);
} }
} }

288
lib/QueryBuilderBase.js Normal file
View File

@ -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;

View File

@ -18,13 +18,13 @@ class QueryParser {
* @param {Driver} driver - The driver object for the database in use * @param {Driver} driver - The driver object for the database in use
* @return {void} * @return {void}
*/ */
constructor(driver) { constructor (driver) {
this.driver = driver; this.driver = driver;
const matchPatterns = { const matchPatterns = {
function: /([a-z0-9_]+\((.*)\))/i, function: /([a-z0-9_]+\((.*)\))/i,
operator: /\!=?|\=|\+|&&?|~|\|\|?|\^|\/|<>|>=?|<=?|\-|%|OR|AND|NOT|XOR/ig, operator: /!=?|=|\+|&&?|~|\|\|?|\^|\/|<>|>=?|<=?|-|%|OR|AND|NOT|XOR/ig,
literal: /([0-9]+)|'(.*?)'|true|false/ig, literal: /([0-9]+)|'(.*?)'|true|false/ig
}; };
// Full pattern for identifiers // Full pattern for identifiers
@ -55,7 +55,7 @@ class QueryParser {
* @param {Array} array - Set of possible matches * @param {Array} array - Set of possible matches
* @return {Array|null} - Filtered set of possible matches * @return {Array|null} - Filtered set of possible matches
*/ */
filterMatches(array) { filterMatches (array) {
let output = []; let output = [];
// Return non-array matches // Return non-array matches
@ -76,7 +76,7 @@ class QueryParser {
* @param {String} string - the string to check * @param {String} string - the string to check
* @return {Array|null} - List of operators * @return {Array|null} - List of operators
*/ */
hasOperator(string) { hasOperator (string) {
return this.filterMatches(string.match(this.matchPatterns.operator)); return this.filterMatches(string.match(this.matchPatterns.operator));
} }
@ -86,13 +86,13 @@ class QueryParser {
* @param {String} sql - Join sql to parse * @param {String} sql - Join sql to parse
* @return {Object} - Join condition components * @return {Object} - Join condition components
*/ */
parseJoin(sql) { parseJoin (sql) {
let matches = {}; let matches = {};
let output = { let output = {
functions: [], functions: [],
identifiers: [], identifiers: [],
operators: [], operators: [],
literals: [], literals: []
}; };
// Get clause components // Get clause components
@ -118,12 +118,12 @@ class QueryParser {
* @param {String} condition - The join condition to evalate * @param {String} condition - The join condition to evalate
* @return {String} - The parsed/escaped join condition * @return {String} - The parsed/escaped join condition
*/ */
compileJoin(condition) { compileJoin (condition) {
let parts = this.parseJoin(condition); let parts = this.parseJoin(condition);
// Quote the identifiers // Quote the identifiers
parts.combined.forEach((part, i) => { 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); parts.combined[i] = this.driver.quoteIdentifiers(part);
} }
}); });
@ -138,7 +138,7 @@ class QueryParser {
* @param {State} state - Query Builder state object * @param {State} state - Query Builder state object
* @return {String} - The parsed/escaped where condition * @return {String} - The parsed/escaped where condition
*/ */
parseWhere(driver, state) { parseWhere (driver, state) {
let whereMap = state.whereMap; let whereMap = state.whereMap;
let whereValues = state.rawWhereValues; let whereValues = state.rawWhereValues;
@ -151,7 +151,7 @@ class QueryParser {
let fullClause = ''; let fullClause = '';
// Add an explicit = sign where one is inferred // Add an explicit = sign where one is inferred
if (! this.hasOperator(key)) { if (!this.hasOperator(key)) {
fullClause = `${key} = ${whereMap[key]}`; fullClause = `${key} = ${whereMap[key]}`;
} else if (whereMap[key] === key) { } else if (whereMap[key] === key) {
fullClause = key; fullClause = key;
@ -176,7 +176,7 @@ class QueryParser {
if (identIndex !== -1) { if (identIndex !== -1) {
parts.identifiers.splice(identIndex, 1); parts.identifiers.splice(identIndex, 1);
if (! inOutputArray) { if (!inOutputArray) {
outputValues.push(value); outputValues.push(value);
inOutputArray = true; inOutputArray = true;
} }
@ -187,7 +187,7 @@ class QueryParser {
if (litIndex !== -1) { if (litIndex !== -1) {
parts.literals.splice(litIndex, 1); parts.literals.splice(litIndex, 1);
if (! inOutputArray) { if (!inOutputArray) {
outputValues.push(value); outputValues.push(value);
inOutputArray = true; inOutputArray = true;
} }

View File

@ -16,7 +16,7 @@ class Result {
* @param {Array} [rows] - the data rows of the result * @param {Array} [rows] - the data rows of the result
* @param {Array} [columns] - the column names in the result * @param {Array} [columns] - the column names in the result
*/ */
constructor(rows, columns) { constructor (rows, columns) {
this._rows = (rows == null) ? [] : rows; this._rows = (rows == null) ? [] : rows;
this._columns = (columns == null) ? [] : columns; this._columns = (columns == null) ? [] : columns;
@ -37,7 +37,7 @@ class Result {
* @private * @private
* @return {Array} - the data rows of the result * @return {Array} - the data rows of the result
*/ */
get rows() { get rows () {
return this._rows; return this._rows;
} }
@ -48,7 +48,7 @@ class Result {
* @param {Array} rows - the data rows of the result * @param {Array} rows - the data rows of the result
* @return {void} * @return {void}
*/ */
set rows(rows) { set rows (rows) {
this._rows = rows; this._rows = rows;
} }
@ -58,7 +58,7 @@ class Result {
* @private * @private
* @return {Array} - the column names in the result * @return {Array} - the column names in the result
*/ */
get columns() { get columns () {
return this._columns; return this._columns;
} }
@ -69,7 +69,7 @@ class Result {
* @param {Array} cols - the array of columns for the current result * @param {Array} cols - the array of columns for the current result
* @return {void} * @return {void}
*/ */
set columns(cols) { set columns (cols) {
this._columns = cols; this._columns = cols;
} }
@ -78,7 +78,7 @@ class Result {
* *
* @return {Number} - the number of rows in the result * @return {Number} - the number of rows in the result
*/ */
rowCount() { rowCount () {
return this._rows.length; return this._rows.length;
} }
@ -87,7 +87,7 @@ class Result {
* *
* @return {Number} - the number of columns in the result * @return {Number} - the number of columns in the result
*/ */
columnCount() { columnCount () {
return this._columns.length; return this._columns.length;
} }
} }

View File

@ -5,7 +5,7 @@
* @private * @private
*/ */
class State { class State {
constructor() { constructor () {
// Arrays/maps // Arrays/maps
this.queryMap = []; this.queryMap = [];
this.values = []; this.values = [];
@ -31,5 +31,3 @@ class State {
} }
module.exports = State; module.exports = State;
// End of module State

3
lib/adapters/MariaDB.js Normal file
View File

@ -0,0 +1,3 @@
'use strict';
module.exports = require('./Mysql');

View File

@ -8,7 +8,7 @@ const mysql2 = require('mysql2');
class Mysql extends Adapter { class Mysql extends Adapter {
constructor(config) { constructor (config) {
let instance = mysql2.createConnection(config); let instance = mysql2.createConnection(config);
instance.connect(err => { instance.connect(err => {
if (err) { if (err) {
@ -24,7 +24,7 @@ class Mysql extends Adapter {
* @param {*} result - original driver result object * @param {*} result - original driver result object
* @return {Result} - standard result object * @return {Result} - standard result object
*/ */
transformResult(result) { transformResult (result) {
// For insert and update queries, the result object // For insert and update queries, the result object
// works differently. Just apply the properties of // works differently. Just apply the properties of
// this special result to the standard result object. // 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 * @param {Function} [callback] - Callback to run when a response is recieved
* @return {void|Promise} - Returns a promise if no callback is provided * @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); let args = getArgs('sql:string, [params]:array, [callback]:function', arguments);
if (! args.callback) { if (!args.callback) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.instance.execute(args.sql, args.params, (err, result) => this.instance.execute(args.sql, args.params, (err, result) =>
(err) (err)

View File

@ -9,7 +9,7 @@ const url = require('url');
class Pg extends Adapter { class Pg extends Adapter {
constructor(config) { constructor (config) {
let instance = null; let instance = null;
let connectionString = ''; let connectionString = '';
if (helpers.isObject(config)) { if (helpers.isObject(config)) {
@ -23,7 +23,7 @@ class Pg extends Adapter {
slashes: true, slashes: true,
host: `${host}:${port}`, host: `${host}:${port}`,
auth: `${user}${password}`, auth: `${user}${password}`,
pathname: config.database, pathname: config.database
}; };
connectionString = url.format(conn); connectionString = url.format(conn);
@ -32,12 +32,8 @@ class Pg extends Adapter {
} }
if (connectionString !== '') { if (connectionString !== '') {
let connected = false;
instance = new pg.Client(connectionString); instance = new pg.Client(connectionString);
instance.connect(err => { instance.connect(err => {
connected = true;
if (err) { if (err) {
throw new Error(err); throw new Error(err);
} }
@ -53,13 +49,15 @@ class Pg extends Adapter {
* @param {*} result - original driver result object * @param {*} result - original driver result object
* @return {Result} - standard result object * @return {Result} - standard result object
*/ */
transformResult(result) { transformResult (result) {
if (result == null) { if (result == null) {
return new Result(); return new Result();
} }
let cols = []; let cols = [];
result.fields.forEach(field => cols = field.name); result.fields.forEach(field => {
cols = field.name;
});
return new Result(result.rows, cols); 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 * @param {Function} [callback] - Callback to run when a response is recieved
* @return {void|Promise} - Returns a promise if no callback is provided * @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); let args = getArgs('sql:string, [params]:array, [callback]:function', arguments);
// Replace question marks with numbered placeholders, because this adapter is different... // Replace question marks with numbered placeholders, because this adapter is different...
@ -82,7 +80,7 @@ class Pg extends Adapter {
return `$${count}`; return `$${count}`;
}); });
if (! args.callback) { if (!args.callback) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.instance.query(args.sql, args.params, (err, result) => this.instance.query(args.sql, args.params, (err, result) =>
(err) (err)

View File

@ -7,7 +7,7 @@ const helpers = require('../helpers');
const dbliteAdapter = require('dblite'); const dbliteAdapter = require('dblite');
class Sqlite extends Adapter { class Sqlite extends Adapter {
constructor(config) { constructor (config) {
let file = (helpers.isString(config)) ? config : config.file; let file = (helpers.isString(config)) ? config : config.file;
super(dbliteAdapter(file)); super(dbliteAdapter(file));
@ -21,7 +21,7 @@ class Sqlite extends Adapter {
* @param {*} result - original driver result object * @param {*} result - original driver result object
* @return {Result} - standard result object * @return {Result} - standard result object
*/ */
transformResult(result) { transformResult (result) {
return new Result(result); return new Result(result);
} }
@ -33,10 +33,10 @@ class Sqlite extends Adapter {
* @param {Function} [callback] - Callback to run when a response is recieved * @param {Function} [callback] - Callback to run when a response is recieved
* @return {void|Promise} - Returns a promise if no callback is provided * @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); let args = getArgs('sql:string, [params]:array, [callback]:function', arguments);
if (! args.callback) { if (!args.callback) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.instance.query(args.sql, args.params, (err, result) => this.instance.query(args.sql, args.params, (err, result) =>
(err) (err)
@ -56,7 +56,7 @@ class Sqlite extends Adapter {
* @return {void} * @return {void}
*/ */
close() { close () {
this.instance.close(); this.instance.close();
} }
} }

8
lib/drivers/MariaDB.js Normal file
View File

@ -0,0 +1,8 @@
'use strict';
/**
* Driver for MariaDB databases
*
* @module drivers/MariaDB
*/
module.exports = require('./Mysql');

View File

@ -3,32 +3,31 @@
/** /**
* Driver for MySQL databases * Driver for MySQL databases
* *
* @module drivers/mysql * @module drivers/Mysql
*/ */
module.exports = (() => { module.exports = (() => {
delete require.cache[require.resolve('../Driver')]; delete require.cache[require.resolve('../Driver')];
let driver = require('../Driver'), const driver = require('../Driver');
helpers = require('../helpers'); const helpers = require('../helpers');
driver.identifierStartChar = '`'; driver.identifierStartChar = '`';
driver.identifierEndChar = '`'; driver.identifierEndChar = '`';
/** /**
* Set the limit clause * Set the limit clause
*
* @param {String} sql - SQL statement to modify * @param {String} sql - SQL statement to modify
* @param {Number} limit - Maximum number of rows to fetch * @param {Number} limit - Maximum number of rows to fetch
* @param {Number|null} offset - Number of rows to skip * @param {Number|null} offset - Number of rows to skip
* @return {String} - Modified SQL statement * @return {String} - Modified SQL statement
*/ */
driver.limit = (sql, limit, offset) => { driver.limit = (sql, limit, offset) => {
if (! helpers.isNumber(offset)) { sql += (helpers.isNumber(offset))
return sql += ` LIMIT ${limit}`; ? ` LIMIT ${offset},${limit}`
} : ` LIMIT ${limit}`;
return sql += ` LIMIT ${offset},${limit}`; return sql;
}; };
return driver; return driver;
})(); })();

View File

@ -3,7 +3,7 @@
/** /**
* Driver for PostgreSQL databases * Driver for PostgreSQL databases
* *
* @module drivers/pg * @module drivers/Pg
*/ */
module.exports = (() => { module.exports = (() => {
delete require.cache[require.resolve('../Driver')]; delete require.cache[require.resolve('../Driver')];

View File

@ -1,9 +1,9 @@
'use strict'; 'use strict';
/** /**
* Driver for Sqlite databases * Driver for SQLite databases
* *
* @module drivers/sqlite * @module drivers/Sqlite
*/ */
module.exports = (() => { module.exports = (() => {
delete require.cache[require.resolve('../Driver')]; delete require.cache[require.resolve('../Driver')];
@ -21,14 +21,13 @@ module.exports = (() => {
* @return {String} - The generated sql statement * @return {String} - The generated sql statement
*/ */
driver.insertBatch = (table, data) => { driver.insertBatch = (table, data) => {
// Get the data values to insert, so they can // Get the data values to insert, so they can
// be parameterized // be parameterized
let sql = '', let sql = '';
vals = [], let vals = [];
cols = [], let cols = [];
fields = [], let fields = [];
first = data.shift(); let first = data.shift();
data.forEach(obj => { data.forEach(obj => {
let row = []; let row = [];
@ -56,7 +55,7 @@ module.exports = (() => {
return { return {
sql: sql, sql: sql,
values: null, values: null
}; };
}; };

View File

@ -63,7 +63,7 @@ let helpers = {
} }
arr.forEach(obj => { arr.forEach(obj => {
if (! helpers.isUndefined(obj[key])) { if (!helpers.isUndefined(obj[key])) {
output.push(obj[key]); output.push(obj[key]);
} }
}); });
@ -80,13 +80,12 @@ let helpers = {
*/ */
regexInArray: (arr, pattern) => { regexInArray: (arr, pattern) => {
// Empty case(s) // Empty case(s)
if (! helpers.isArray(arr) || arr.length === 0) { if (!helpers.isArray(arr) || arr.length === 0) {
return false; return false;
} }
let i, l = arr.length; const l = arr.length;
for (let i = 0; i < l; i++) {
for (i = 0; i < l; i++) {
// Short circuit if any items match // Short circuit if any items match
if (pattern.test(arr[i])) { if (pattern.test(arr[i])) {
return true; return true;
@ -105,7 +104,7 @@ let helpers = {
str += ''; str += '';
let first = str.charAt(0).toUpperCase(); let first = str.charAt(0).toUpperCase();
return first + str.substr(1); return first + str.substr(1);
}, }
}; };
// Define an 'is' method for each type // Define an 'is' method for each type
@ -120,7 +119,7 @@ let types = [
'Function', 'Function',
'RegExp', 'RegExp',
'NaN', 'NaN',
'Infinite', 'Infinite'
]; ];
types.forEach(t => { types.forEach(t => {
/** /**
@ -134,13 +133,13 @@ types.forEach(t => {
* @param {mixed} o - The object to check its type * @param {mixed} o - The object to check its type
* @return {Boolean} - If the type matches * @return {Boolean} - If the type matches
*/ */
helpers[`is${t}`] = function (o) { helpers[`is${t}`] = function (o) {
if (t.toLowerCase() === 'infinite') { if (t.toLowerCase() === 'infinite') {
t = 'infinity'; t = 'infinity';
} }
return helpers.type(o) === t.toLowerCase(); return helpers.type(o) === t.toLowerCase();
}; };
}); });
module.exports = helpers; module.exports = helpers;

View File

@ -1,6 +1,6 @@
{ {
"name": "ci-node-query", "name": "ci-node-query",
"version": "4.0.0", "version": "4.0.1",
"description": "A query builder for node based on the one in CodeIgniter", "description": "A query builder for node based on the one in CodeIgniter",
"author": "Timothy J Warren <tim@timshomepage.net>", "author": "Timothy J Warren <tim@timshomepage.net>",
"engines": { "engines": {
@ -21,13 +21,14 @@
}, },
"keywords": [ "keywords": [
"codeigniter", "codeigniter",
"mysql2", "mariadb",
"mysql",
"query builder", "query builder",
"pg",
"postgres", "postgres",
"sqlite3", "postgresql",
"sql",
"sqlite", "sqlite",
"dblite" "sqlite3"
], ],
"bugs": { "bugs": {
"url": "https://git.timshomepage.net/timw4mail/node-query/issues" "url": "https://git.timshomepage.net/timw4mail/node-query/issues"
@ -38,7 +39,7 @@
"getargs": "~0.0.8", "getargs": "~0.0.8",
"glob": "^7.0.3", "glob": "^7.0.3",
"mysql2": "^1.0.0-rc.1", "mysql2": "^1.0.0-rc.1",
"pg": "^4.5.1", "pg": "^6.0.0",
"require-reload": "~0.2.2", "require-reload": "~0.2.2",
"xregexp": "^3.0.0" "xregexp": "^3.0.0"
}, },
@ -46,13 +47,13 @@
"chai": "^3.5.0", "chai": "^3.5.0",
"chai-as-promised": "^5.2.0", "chai-as-promised": "^5.2.0",
"documentation": "", "documentation": "",
"eslint": "^2.4.0", "eslint": "^3.5.0",
"glob": "~6.0.4", "glob": "~6.0.4",
"globstar": "^1.0.0", "globstar": "^1.0.0",
"happiness": "^5.5.0",
"istanbul": "~0.4.2", "istanbul": "~0.4.2",
"jscs": "^2.11.0", "mocha": "^3.0.0",
"mocha": "^2.4.5", "npm-run-all": "^3.0.0",
"npm-run-all": "^1.6.0",
"nsp": "^2.2.1" "nsp": "^2.2.1"
}, },
"license": "MIT", "license": "MIT",
@ -63,9 +64,12 @@
"default": "npm-run-all --parallel audit lint:src lint:tests && npm run test", "default": "npm-run-all --parallel audit lint:src lint:tests && npm run test",
"predocs": "globstar -- documentation build -f md -o API.md \"lib/*.js\"", "predocs": "globstar -- documentation build -f md -o API.md \"lib/*.js\"",
"docs": "globstar -- documentation build -f html -o docs \"lib/*.js\"", "docs": "globstar -- documentation build -f html -o docs \"lib/*.js\"",
"lint": "npm-run-all lint:tests lint:src", "happy": "happiness \"lib/**/*.js\" \"test/**/*.js\"",
"lint:src": "jscs ./lib && eslint ./lib", "happy:src": "happiness \"lib/**/*.js\"",
"lint:tests": "jscs ./test && eslint ./test", "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" "test": "mocha -R dot"
} }
} }

View File

@ -1,3 +1,4 @@
/* eslint-env node, mocha */
'use strict'; 'use strict';
// Load the test base // Load the test base
@ -5,10 +6,10 @@ const reload = require('require-reload')(require);
reload.emptyCache(); reload.emptyCache();
const fs = require('fs'); const fs = require('fs');
const testBase = reload('../base'); const testBase = reload('../base');
const expect = testBase.expect; const expect = testBase.expect;
const promiseTestRunner = testBase.promiseTestRunner; const promiseTestRunner = testBase.promiseTestRunner;
const testRunner = testBase.testRunner; const testRunner = testBase.testRunner;
let tests = reload('../base/tests'); // let tests = reload('../base/tests');
// Load the test config file // Load the test config file
const config = testBase.config; const config = testBase.config;
@ -29,9 +30,9 @@ suite('Dblite adapter tests -', () => {
}); });
}); });
/*--------------------------------------------------------------------------- // ---------------------------------------------------------------------------
Callback Tests // Callback Tests
---------------------------------------------------------------------------*/ // ---------------------------------------------------------------------------
testRunner(qb, (err, result, done) => { testRunner(qb, (err, result, done) => {
expect(err).is.not.ok; expect(err).is.not.ok;
@ -55,16 +56,16 @@ suite('Dblite adapter tests -', () => {
{ {
id: 544, id: 544,
key: 3, key: 3,
val: new Buffer('7'), val: new Buffer('7')
}, { }, {
id: 89, id: 89,
key: 34, key: 34,
val: new Buffer('10 o\'clock'), val: new Buffer('10 o\'clock')
}, { }, {
id: 48, id: 48,
key: 403, key: 403,
val: new Buffer('97'), val: new Buffer('97')
}, }
]; ];
qb.insertBatch('create_test', data, err => { qb.insertBatch('create_test', data, err => {
@ -73,9 +74,9 @@ suite('Dblite adapter tests -', () => {
}); });
}); });
/*--------------------------------------------------------------------------- // ---------------------------------------------------------------------------
Promise Tests // Promise Tests
---------------------------------------------------------------------------*/ // ---------------------------------------------------------------------------
promiseTestRunner(qb); promiseTestRunner(qb);
test('Promise - Select with function and argument in WHERE clause', () => { test('Promise - Select with function and argument in WHERE clause', () => {
let promise = qb.select('id') let promise = qb.select('id')
@ -90,16 +91,16 @@ suite('Dblite adapter tests -', () => {
{ {
id: 544, id: 544,
key: 3, key: 3,
val: new Buffer('7'), val: new Buffer('7')
}, { }, {
id: 89, id: 89,
key: 34, key: 34,
val: new Buffer('10 o\'clock'), val: new Buffer('10 o\'clock')
}, { }, {
id: 48, id: 48,
key: 403, key: 403,
val: new Buffer('97'), val: new Buffer('97')
}, }
]; ];
let promise = qb.query(qb.driver.truncate('create_test')).then( let promise = qb.query(qb.driver.truncate('create_test')).then(

View File

@ -1,10 +1,11 @@
/* eslint-env node, mocha */
'use strict'; 'use strict';
// Load the test base // Load the test base
const reload = require('require-reload')(require); const reload = require('require-reload')(require);
reload.emptyCache(); reload.emptyCache();
const testBase = reload('../base'); const testBase = reload('../base');
const expect = testBase.expect; const expect = testBase.expect;
const promiseTestRunner = testBase.promiseTestRunner; const promiseTestRunner = testBase.promiseTestRunner;
const testRunner = testBase.testRunner; const testRunner = testBase.testRunner;
@ -22,9 +23,9 @@ suite('Mysql2 adapter tests -', () => {
.to.be.deep.equal(qb); .to.be.deep.equal(qb);
}); });
//-------------------------------------------------------------------------- // --------------------------------------------------------------------------
// Callback Tests // Callback Tests
//-------------------------------------------------------------------------- // --------------------------------------------------------------------------
testRunner(qb, (err, result, done) => { testRunner(qb, (err, result, done) => {
expect(err).is.not.ok; expect(err).is.not.ok;
expect(result.rows).is.an('array'); expect(result.rows).is.an('array');
@ -42,21 +43,26 @@ suite('Mysql2 adapter tests -', () => {
return done(); return done();
}); });
}); });
test('Callback - Test Truncate', done => {
qb.truncate('create_test', (err, res) => {
return done(err);
});
});
test('Callback - Test Insert Batch', done => { test('Callback - Test Insert Batch', done => {
let data = [ let data = [
{ {
id: 5441, id: 5441,
key: 3, key: 3,
val: new Buffer('7'), val: new Buffer('7')
}, { }, {
id: 891, id: 891,
key: 34, key: 34,
val: new Buffer('10 o\'clock'), val: new Buffer('10 o\'clock')
}, { }, {
id: 481, id: 481,
key: 403, key: 403,
val: new Buffer('97'), val: new Buffer('97')
}, }
]; ];
qb.insertBatch('create_test', data, (err, res) => { qb.insertBatch('create_test', data, (err, res) => {
@ -65,9 +71,9 @@ suite('Mysql2 adapter tests -', () => {
}); });
}); });
//--------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Promise Tests // Promise Tests
//--------------------------------------------------------------------------- // ---------------------------------------------------------------------------
promiseTestRunner(qb); promiseTestRunner(qb);
test('Promise - Select with function and argument in WHERE clause', () => { test('Promise - Select with function and argument in WHERE clause', () => {
let promise = qb.select('id') let promise = qb.select('id')
@ -77,22 +83,25 @@ suite('Mysql2 adapter tests -', () => {
return expect(promise).to.be.fulfilled; 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', () => { test('Test Insert Batch', () => {
let data = [ let data = [
{ {
id: 5442, id: 5442,
key: 4, key: 4,
val: new Buffer('7'), val: new Buffer('7')
}, { }, {
id: 892, id: 892,
key: 35, key: 35,
val: new Buffer('10 o\'clock'), val: new Buffer('10 o\'clock')
}, { }, {
id: 482, id: 482,
key: 404, key: 404,
val: 97, val: 97
}, }
]; ];
return expect(qb.insertBatch('create_test', data)).to.be.fulfilled; return expect(qb.insertBatch('create_test', data)).to.be.fulfilled;

View File

@ -1,10 +1,11 @@
/* eslint-env node, mocha */
'use strict'; 'use strict';
// Load the test base // Load the test base
const reload = require('require-reload')(require); const reload = require('require-reload')(require);
reload.emptyCache(); reload.emptyCache();
const testBase = reload('../base'); const testBase = reload('../base');
const expect = testBase.expect; const expect = testBase.expect;
const promiseTestRunner = testBase.promiseTestRunner; const promiseTestRunner = testBase.promiseTestRunner;
const testRunner = testBase.testRunner; const testRunner = testBase.testRunner;
@ -32,9 +33,20 @@ suite('Pg adapter tests -', () => {
return expect(qb2).to.be.ok; 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 // Callback Tests
//-------------------------------------------------------------------------- // --------------------------------------------------------------------------
testRunner(qb, (err, result, done) => { testRunner(qb, (err, result, done) => {
expect(err).is.not.ok; expect(err).is.not.ok;
expect(result.rows).is.an('array'); expect(result.rows).is.an('array');
@ -52,21 +64,27 @@ suite('Pg adapter tests -', () => {
return done(err); 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 => { test('Callback - Test Insert Batch', done => {
let data = [ let data = [
{ {
id: 5441, id: 5441,
key: 3, key: 3,
val: new Buffer('7'), val: new Buffer('7')
}, { }, {
id: 891, id: 891,
key: 34, key: 34,
val: new Buffer('10 o\'clock'), val: new Buffer('10 o\'clock')
}, { }, {
id: 481, id: 481,
key: 403, key: 403,
val: new Buffer('97'), val: new Buffer('97')
}, }
]; ];
qb.insertBatch('create_test', data, (err, res) => { qb.insertBatch('create_test', data, (err, res) => {
@ -75,9 +93,9 @@ suite('Pg adapter tests -', () => {
}); });
}); });
//-------------------------------------------------------------------------- // --------------------------------------------------------------------------
// Promise Tests // Promise Tests
//-------------------------------------------------------------------------- // --------------------------------------------------------------------------
promiseTestRunner(qb); promiseTestRunner(qb);
test('Promise - Select with function and argument in WHERE clause', () => { test('Promise - Select with function and argument in WHERE clause', () => {
let promise = qb.select('id') let promise = qb.select('id')
@ -87,21 +105,25 @@ suite('Pg adapter tests -', () => {
return expect(promise).to.be.fulfilled; 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', () => { test('Promise - Test Insert Batch', () => {
let data = [ let data = [
{ {
id: 544, id: 544,
key: 3, key: 3,
val: new Buffer('7'), val: new Buffer('7')
}, { }, {
id: 89, id: 89,
key: 34, key: 34,
val: new Buffer('10 o\'clock'), val: new Buffer('10 o\'clock')
}, { }, {
id: 48, id: 48,
key: 403, key: 403,
val: new Buffer('97'), val: new Buffer('97')
}, }
]; ];
let promise = qb.insertBatch('create_test', data); let promise = qb.insertBatch('create_test', data);

View File

@ -12,5 +12,5 @@ module.exports = {
expect: chai.expect, expect: chai.expect,
tests: require('./base/tests'), tests: require('./base/tests'),
testRunner: require('./base/adapterCallbackTestRunner'), testRunner: require('./base/adapterCallbackTestRunner'),
promiseTestRunner: require('./base/adapterPromiseTestRunner'), promiseTestRunner: require('./base/adapterPromiseTestRunner')
}; };

View File

@ -1,6 +1,6 @@
/* eslint-env node, mocha */
'use strict'; 'use strict';
// jscs:disable
// Load the test base // Load the test base
const chai = require('chai'); const chai = require('chai');
const chaiAsPromised = require('chai-as-promised'); const chaiAsPromised = require('chai-as-promised');
@ -10,31 +10,30 @@ const expect = chai.expect;
const reload = require('require-reload')(require); const reload = require('require-reload')(require);
let tests = reload('../base/tests'); let tests = reload('../base/tests');
let helpers = reload('../../lib/helpers'), const helpers = reload('../../lib/helpers');
State = reload('../../lib/State'); const State = reload('../../lib/State');
module.exports = function testRunner(qb, callback) { module.exports = function testRunner (qb, callback) {
Object.keys(tests).forEach(suiteName => { Object.keys(tests).forEach(suiteName => {
suite(suiteName, () => { suite(suiteName, () => {
let currentSuite = tests[suiteName]; let currentSuite = tests[suiteName];
Object.keys(currentSuite).forEach(testDesc => { Object.keys(currentSuite).forEach(testDesc => {
test(`Callback - ${testDesc}`, done => { test(`Callback - ${testDesc}`, done => {
let methodObj = currentSuite[testDesc]; const methodObj = currentSuite[testDesc];
let methodNames = Object.keys(methodObj); const methodNames = Object.keys(methodObj);
let lastMethodIndex = methodNames[methodNames.length - 1]; const lastMethodIndex = methodNames[methodNames.length - 1];
methodObj[lastMethodIndex].push((err, rows) => callback(err, rows, done)); methodObj[lastMethodIndex].push((err, rows) => callback(err, rows, done));
methodNames.forEach(name => { methodNames.forEach(name => {
let args = methodObj[name], const args = methodObj[name];
method = qb[name]; const method = qb[name];
if (args[0] === 'multiple') { if (args[0] === 'multiple') {
args.shift(); args.shift();
args.forEach(argSet => { args.forEach(argSet => {
method.apply(qb, argSet); method.apply(qb, argSet);
}); });
} else { } else {
method.apply(qb, args); method.apply(qb, args);
} }
@ -57,7 +56,7 @@ module.exports = function testRunner(qb, callback) {
qb.insert('create_test', { qb.insert('create_test', {
id: 587, id: 587,
key: 1, key: 1,
val: new Buffer('2'), val: new Buffer('2')
}, (err, rows) => { }, (err, rows) => {
return callback(err, rows, done); return callback(err, rows, done);
}); });
@ -67,7 +66,7 @@ module.exports = function testRunner(qb, callback) {
.update('create_test', { .update('create_test', {
id: 7, id: 7,
key: 'gogle', key: 'gogle',
val: new Buffer('non-word'), val: new Buffer('non-word')
}, (err, rows) => { }, (err, rows) => {
return callback(err, rows, done); return callback(err, rows, done);
}); });
@ -76,7 +75,7 @@ module.exports = function testRunner(qb, callback) {
let object = { let object = {
id: 22, id: 22,
key: 'gogle', key: 'gogle',
val: new Buffer('non-word'), val: new Buffer('non-word')
}; };
qb.set(object) qb.set(object)
@ -108,7 +107,7 @@ module.exports = function testRunner(qb, callback) {
test('Callback - Delete multiple where values', done => { test('Callback - Delete multiple where values', done => {
qb.delete('create_test', { qb.delete('create_test', {
id: 5, id: 5,
key: 'gogle', key: 'gogle'
}, (err, rows) => { }, (err, rows) => {
return callback(err, rows, done); return callback(err, rows, done);
}); });

View File

@ -1,6 +1,6 @@
/* eslint-env node, mocha */
'use strict'; 'use strict';
// jscs:disable
// Load the test base // Load the test base
const chai = require('chai'); const chai = require('chai');
const chaiAsPromised = require('chai-as-promised'); const chaiAsPromised = require('chai-as-promised');
@ -8,32 +8,27 @@ chai.use(chaiAsPromised);
const expect = chai.expect; const expect = chai.expect;
const reload = require('require-reload')(require); const reload = require('require-reload')(require);
let tests = reload('../base/tests'); const tests = reload('../base/tests');
let helpers = reload('../../lib/helpers'), module.exports = function promiseTestRunner (qb) {
State = reload('../../lib/State');
module.exports = function promiseTestRunner(qb) {
Object.keys(tests).forEach(suiteName => { Object.keys(tests).forEach(suiteName => {
suite(suiteName, () => { suite(suiteName, () => {
let currentSuite = tests[suiteName]; let currentSuite = tests[suiteName];
Object.keys(currentSuite).forEach(testDesc => { Object.keys(currentSuite).forEach(testDesc => {
test(`Promise - ${testDesc}`, () => { test(`Promise - ${testDesc}`, () => {
let methodObj = currentSuite[testDesc]; const methodObj = currentSuite[testDesc];
let methodNames = Object.keys(methodObj); const methodNames = Object.keys(methodObj);
let lastMethodIndex = methodNames[methodNames.length - 1];
let results = []; let results = [];
methodNames.forEach(name => { methodNames.forEach(name => {
let args = methodObj[name], const args = methodObj[name];
method = qb[name]; const method = qb[name];
if (args[0] === 'multiple') { if (args[0] === 'multiple') {
args.shift(); args.shift();
args.forEach(argSet => { args.forEach(argSet => {
results.push(method.apply(qb, argSet)); results.push(method.apply(qb, argSet));
}); });
} else { } else {
results.push(method.apply(qb, args)); results.push(method.apply(qb, args));
} }
@ -63,7 +58,7 @@ module.exports = function promiseTestRunner(qb) {
let promise = qb.insert('create_test', { let promise = qb.insert('create_test', {
id: 587, id: 587,
key: 1, key: 1,
val: new Buffer('2'), val: new Buffer('2')
}); });
return expect(promise).to.be.fulfilled; return expect(promise).to.be.fulfilled;
@ -73,7 +68,7 @@ module.exports = function promiseTestRunner(qb) {
.update('create_test', { .update('create_test', {
id: 7, id: 7,
key: 'gogle', key: 'gogle',
val: new Buffer('non-word'), val: new Buffer('non-word')
}); });
return expect(promise).to.be.fulfilled; return expect(promise).to.be.fulfilled;
@ -82,7 +77,7 @@ module.exports = function promiseTestRunner(qb) {
let object = { let object = {
id: 22, id: 22,
key: 'gogle', key: 'gogle',
val: new Buffer('non-word'), val: new Buffer('non-word')
}; };
let promise = qb.set(object) let promise = qb.set(object)
@ -113,7 +108,7 @@ module.exports = function promiseTestRunner(qb) {
test('Promise - Delete multiple where values', () => { test('Promise - Delete multiple where values', () => {
let promise = qb.delete('create_test', { let promise = qb.delete('create_test', {
id: 5, id: 5,
key: 'gogle', key: 'gogle'
}); });
return expect(promise).to.be.fulfilled; return expect(promise).to.be.fulfilled;

View File

@ -1,26 +1,25 @@
'use strict'; 'use strict';
// jscs:disable
module.exports = { module.exports = {
'Get tests -': { 'Get tests -': {
'Get with function': { 'Get with function': {
select: ['id, COUNT(id) as count'], select: ['id, COUNT(id) as count'],
from: ['create_test'], from: ['create_test'],
groupBy: ['id'], groupBy: ['id'],
get: [], get: []
}, },
'Basic select all get': { 'Basic select all get': {
get: ['create_test'], get: ['create_test']
}, },
'Basic select all with from': { 'Basic select all with from': {
from: ['create_test'], from: ['create_test'],
get: [], get: []
}, },
'Get with limit': { 'Get with limit': {
get: ['create_test', 2], get: ['create_test', 2]
}, },
'Get with limit and offset': { 'Get with limit and offset': {
get: ['create_test', 2, 1], get: ['create_test', 2, 1]
}, },
'Get with having': { 'Get with having': {
select: ['id'], select: ['id'],
@ -30,9 +29,9 @@ module.exports = {
'multiple', 'multiple',
[{'id >': 1}], [{'id >': 1}],
['id !=', 3], ['id !=', 3],
['id', 900], ['id', 900]
], ],
get: [], get: []
}, },
'Get with orHaving': { 'Get with orHaving': {
select: ['id'], select: ['id'],
@ -40,8 +39,8 @@ module.exports = {
groupBy: ['id'], groupBy: ['id'],
having: [{'id >': 1}], having: [{'id >': 1}],
orHaving: ['id !=', 3], orHaving: ['id !=', 3],
get: [], get: []
}, }
}, },
'Select tests -': { 'Select tests -': {
'Select where get': { 'Select where get': {
@ -49,94 +48,94 @@ module.exports = {
where: [ where: [
'multiple', 'multiple',
['id >', 1], ['id >', 1],
['id <', 900], ['id <', 900]
], ],
get: ['create_test', 2, 1], get: ['create_test', 2, 1]
}, },
'Select where get 2': { 'Select where get 2': {
select: ['id, key as k, val'], select: ['id, key as k, val'],
where: ['id !=', 1], where: ['id !=', 1],
get: ['create_test', 2, 1], get: ['create_test', 2, 1]
}, },
'Multi Order By': { 'Multi Order By': {
from: ['create_test'], from: ['create_test'],
orderBy: ['id, key'], orderBy: ['id, key'],
get: [], get: []
}, },
'Select get': { 'Select get': {
select: ['id, key as k, val'], select: ['id, key as k, val'],
get: ['create_test', 2, 1], get: ['create_test', 2, 1]
}, },
'Select from get': { 'Select from get': {
select: ['id, key as k, val'], select: ['id, key as k, val'],
from: ['create_test ct'], from: ['create_test ct'],
where: ['id >', 1], where: ['id >', 1],
get: [], get: []
}, },
'Select from limit get': { 'Select from limit get': {
select: ['id, key as k, val'], select: ['id, key as k, val'],
from: ['create_test ct'], from: ['create_test ct'],
where: ['id >', 1], where: ['id >', 1],
limit: [3], limit: [3],
get: [], get: []
}, },
'Select where IS NOT NULL': { 'Select where IS NOT NULL': {
select: ['id', 'key as k', 'val'], select: ['id', 'key as k', 'val'],
from: ['create_test ct'], from: ['create_test ct'],
whereIsNotNull: ['id'], whereIsNotNull: ['id'],
get: [], get: []
}, },
'Select where IS NULL': { 'Select where IS NULL': {
select: ['id', 'key as k', 'val'], select: ['id', 'key as k', 'val'],
from: ['create_test ct'], from: ['create_test ct'],
whereIsNull: ['id'], whereIsNull: ['id'],
get: [], get: []
}, },
'Select where OR IS NOT NULL': { 'Select where OR IS NOT NULL': {
select: ['id', 'key as k', 'val'], select: ['id', 'key as k', 'val'],
from: ['create_test ct'], from: ['create_test ct'],
whereIsNull: ['id'], whereIsNull: ['id'],
orWhereIsNotNull: ['id'], orWhereIsNotNull: ['id'],
get: [], get: []
}, },
'Select where OR IS NULL': { 'Select where OR IS NULL': {
select: ['id', 'key as k', 'val'], select: ['id', 'key as k', 'val'],
from: ['create_test ct'], from: ['create_test ct'],
where: ['id', 3], where: ['id', 3],
orWhereIsNull: ['id'], orWhereIsNull: ['id'],
get: [], get: []
}, },
'Select with string where value': { 'Select with string where value': {
select: ['id', 'key as k', 'val'], select: ['id', 'key as k', 'val'],
from: ['create_test ct'], from: ['create_test ct'],
where: ['id > 3'], where: ['id > 3'],
get: [], get: []
}, }
}, },
'Where in tests -': { 'Where in tests -': {
'Where in': { 'Where in': {
from: ['create_test'], from: ['create_test'],
whereIn: ['id', [0, 6, 56, 563, 341]], whereIn: ['id', [0, 6, 56, 563, 341]],
get: [], get: []
}, },
'Or Where in': { 'Or Where in': {
from: ['create_test'], from: ['create_test'],
where: ['key', 'false'], where: ['key', 'false'],
orWhereIn: ['id', [0, 6, 56, 563, 341]], orWhereIn: ['id', [0, 6, 56, 563, 341]],
get: [], get: []
}, },
'Where Not in': { 'Where Not in': {
from: ['create_test'], from: ['create_test'],
where: ['key', 'false'], where: ['key', 'false'],
whereNotIn: ['id', [0, 6, 56, 563, 341]], whereNotIn: ['id', [0, 6, 56, 563, 341]],
get: [], get: []
}, },
'Or Where Not in': { 'Or Where Not in': {
from: ['create_test'], from: ['create_test'],
where: ['key', 'false'], where: ['key', 'false'],
orWhereNotIn: ['id', [0, 6, 56, 563, 341]], orWhereNotIn: ['id', [0, 6, 56, 563, 341]],
get: [], get: []
}, }
}, },
'Query modifier tests -': { 'Query modifier tests -': {
'Order By': { 'Order By': {
@ -145,15 +144,15 @@ module.exports = {
where: [ where: [
'multiple', 'multiple',
['id >', 0], ['id >', 0],
['id <', 9000], ['id <', 9000]
], ],
orderBy: [ orderBy: [
'multiple', 'multiple',
['id', 'DESC'], ['id', 'DESC'],
['k', 'ASC'], ['k', 'ASC']
], ],
limit: [5, 2], limit: [5, 2],
get: [], get: []
}, },
'Group By': { 'Group By': {
select: ['id, key as k, val'], select: ['id, key as k, val'],
@ -161,20 +160,20 @@ module.exports = {
where: [ where: [
'multiple', 'multiple',
['id >', 0], ['id >', 0],
['id <', 9000], ['id <', 9000]
], ],
groupBy: [ groupBy: [
'multiple', 'multiple',
['k'], ['k'],
[['id', 'val']], [['id', 'val']]
], ],
orderBy: [ orderBy: [
'multiple', 'multiple',
['id', 'DESC'], ['id', 'DESC'],
['k', 'ASC'], ['k', 'ASC']
], ],
limit: [5, 2], limit: [5, 2],
get: [], get: []
}, },
'Or Where': { 'Or Where': {
select: ['id, key as k, val'], select: ['id, key as k, val'],
@ -182,55 +181,55 @@ module.exports = {
where: [' id ', 1], where: [' id ', 1],
orWhere: ['key > ', 0], orWhere: ['key > ', 0],
limit: [2, 1], limit: [2, 1],
get: [], get: []
}, },
Like: { Like: {
from: ['create_test'], from: ['create_test'],
like: ['key', 'og'], like: ['key', 'og'],
get: [], get: []
}, },
'Or Like': { 'Or Like': {
from: ['create_test'], from: ['create_test'],
like: ['key', 'og'], like: ['key', 'og'],
orLike: ['key', 'val'], orLike: ['key', 'val'],
get: [], get: []
}, },
'Not Like': { 'Not Like': {
from: ['create_test'], from: ['create_test'],
like: ['key', 'og', 'before'], like: ['key', 'og', 'before'],
notLike: ['key', 'val'], notLike: ['key', 'val'],
get: [], get: []
}, },
'Or Not Like': { 'Or Not Like': {
from: ['create_test'], from: ['create_test'],
like: ['key', 'og', 'before'], like: ['key', 'og', 'before'],
orNotLike: ['key', 'val'], orNotLike: ['key', 'val'],
get: [], get: []
}, },
'Like Before': { 'Like Before': {
from: ['create_test'], from: ['create_test'],
like: ['key', 'og', 'before'], like: ['key', 'og', 'before'],
get: [], get: []
}, },
'Like After': { 'Like After': {
from: ['create_test'], from: ['create_test'],
like: ['key', 'og', 'after'], like: ['key', 'og', 'after'],
get: [], get: []
}, },
'Basic Join': { 'Basic Join': {
from: ['create_test ct'], from: ['create_test ct'],
join: ['create_join cj', 'cj.id=ct.id'], join: ['create_join cj', 'cj.id=ct.id'],
get: [], get: []
}, },
'Left Join': { 'Left Join': {
from: ['create_test ct'], from: ['create_test ct'],
join: ['create_join cj', 'cj.id=ct.id', 'left'], join: ['create_join cj', 'cj.id=ct.id', 'left'],
get: [], get: []
}, },
'Inner Join': { 'Inner Join': {
from: ['create_test ct'], from: ['create_test ct'],
join: ['create_join cj', 'cj.id=ct.id', 'inner'], join: ['create_join cj', 'cj.id=ct.id', 'inner'],
get: [], get: []
}, },
'Join with multiple where values': { 'Join with multiple where values': {
from: ['create_test ct'], from: ['create_test ct'],
@ -238,10 +237,10 @@ module.exports = {
where: [ where: [
{ {
'ct.id < ': 3, 'ct.id < ': 3,
'ct.key ': 'foo', 'ct.key ': 'foo'
}, }
], ],
get: [], get: []
}, }
}, }
}; };

View File

@ -1,10 +1,11 @@
/* eslint-env node, mocha */
'use strict'; 'use strict';
let expect = require('chai').expect, const expect = require('chai').expect;
reload = require('require-reload')(require), const reload = require('require-reload')(require);
glob = require('glob'), const glob = require('glob');
nodeQuery = reload('../lib/NodeQuery')(), const nodeQuery = reload('../lib/NodeQuery')();
Adapter = reload('../lib/Adapter'); const Adapter = reload('../lib/Adapter');
suite('Base tests -', () => { suite('Base tests -', () => {
suite('Sanity check', () => { suite('Sanity check', () => {
@ -31,7 +32,7 @@ suite('Base tests -', () => {
test('Invalid driver type', () => { test('Invalid driver type', () => {
expect(() => { expect(() => {
reload('../lib/NodeQuery')({ reload('../lib/NodeQuery')({
driver: 'Foo', driver: 'Foo'
}); });
}).to.throw(Error, 'Selected driver (Foo) does not exist!'); }).to.throw(Error, 'Selected driver (Foo) does not exist!');
}); });

View File

@ -1,9 +1,9 @@
/* eslint-env node, mocha */
'use strict'; 'use strict';
let chai = require('chai'), const chai = require('chai');
assert = chai.assert, const assert = chai.assert;
expect = chai.expect, const expect = chai.expect;
should = chai.should();
let helpers = require('../lib/helpers'); let helpers = require('../lib/helpers');
@ -35,7 +35,7 @@ suite('Helper Module Tests -', () => {
'Function', 'Function',
'RegExp', 'RegExp',
'NaN', 'NaN',
'Infinite', 'Infinite'
]; ];
types.forEach(type => { types.forEach(type => {
@ -48,7 +48,7 @@ suite('Helper Module Tests -', () => {
let trueCases = { let trueCases = {
'Strings are scalar': 'foo', 'Strings are scalar': 'foo',
'Booleans are scalar': true, 'Booleans are scalar': true,
'Numbers are scalar': 545, 'Numbers are scalar': 545
}; };
Object.keys(trueCases).forEach(desc => { Object.keys(trueCases).forEach(desc => {
test(desc, () => { test(desc, () => {
@ -58,7 +58,7 @@ suite('Helper Module Tests -', () => {
let falseCases = { let falseCases = {
'Arrays are not scalar': [], 'Arrays are not scalar': [],
'Objects are not scalar': [], 'Objects are not scalar': []
}; };
Object.keys(falseCases).forEach(desc => { Object.keys(falseCases).forEach(desc => {
test(desc, () => { test(desc, () => {
@ -95,14 +95,14 @@ suite('Helper Module Tests -', () => {
suite('arrayPluck -', () => { suite('arrayPluck -', () => {
let orig = [ let orig = [
{ {
foo: 1, foo: 1
}, { }, {
foo: 2, foo: 2,
bar: 10, bar: 10
}, { }, {
foo: 3, foo: 3,
bar: 15, bar: 15
}, }
]; ];
test('Finding members in all objects', () => { test('Finding members in all objects', () => {
@ -121,11 +121,11 @@ suite('Helper Module Tests -', () => {
let cases = [ let cases = [
{ {
'Dollar sign is not in any of the array items': /\$/, '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/, '\' string \' matches /^ ?string/': /^ ?string/,
'\'apple\' matches /APPLE/i': /APPLE/i, '\'apple\' matches /APPLE/i': /APPLE/i
}, }
]; ];
[0, 1].forEach(i => { [0, 1].forEach(i => {

View File

@ -1,20 +1,21 @@
/* eslint-env node, mocha */
'use strict'; 'use strict';
let expect = require('chai').expect; const expect = require('chai').expect;
// Use the base driver as a mock for testing // Use the base driver as a mock for testing
let getArgs = require('getargs'); const getArgs = require('getargs');
let helpers = require('../lib/helpers'); const helpers = require('../lib/helpers');
let driver = require('../lib/Driver'); const driver = require('../lib/Driver');
let P = require('../lib/QueryParser'); const P = require('../lib/QueryParser');
let parser = new P(driver); let parser = new P(driver);
let State = require('../lib/State'); const State = require('../lib/State');
// Simulate query builder state // Simulate query builder state
let state = new 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]'; const argPattern = '$letName:string, $valType:string, $key:object|string|number, [$val]';
let args = getArgs(argPattern, arguments); let args = getArgs(argPattern, arguments);
@ -86,7 +87,7 @@ suite('Query Parser Tests', () => {
}); });
test('Has function key/val object', () => { test('Has function key/val object', () => {
whereMock({ whereMock({
'time <': 'SUM(FOO(BAR(\'x\')))', 'time <': 'SUM(FOO(BAR(\'x\')))'
}); });
parser.parseWhere(driver, state); parser.parseWhere(driver, state);
expect(state.whereMap) expect(state.whereMap)
@ -94,7 +95,7 @@ suite('Query Parser Tests', () => {
}); });
test('Has literal value', () => { test('Has literal value', () => {
whereMock({ whereMock({
foo: 3, foo: 3
}); });
parser.parseWhere(driver, state); parser.parseWhere(driver, state);
expect(state.whereMap) expect(state.whereMap)
@ -105,7 +106,7 @@ suite('Query Parser Tests', () => {
test('Has multiple literal values', () => { test('Has multiple literal values', () => {
whereMock({ whereMock({
foo: 3, foo: 3,
bar: 5, bar: 5
}); });
parser.parseWhere(driver, state); parser.parseWhere(driver, state);
expect(state.whereMap) expect(state.whereMap)
@ -119,20 +120,20 @@ suite('Query Parser Tests', () => {
{ {
desc: 'Simple equals condition', desc: 'Simple equals condition',
join: 'table1.field1=table2.field2', join: 'table1.field1=table2.field2',
expected: ['table1.field1', '=', 'table2.field2'], expected: ['table1.field1', '=', 'table2.field2']
}, { }, {
desc: 'Db.table.field condition', desc: 'Db.table.field condition',
join: 'db1.table1.field1!=db2.table2.field2', 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', desc: 'Underscore in identifier',
join: 'table_1.field1 = tab_le2.field_2', 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', desc: 'Function in condition',
join: 'table1.field1 > SUM(3+6)', join: 'table1.field1 > SUM(3+6)',
expected: ['table1.field1', '>', 'SUM(3+6)'], expected: ['table1.field1', '>', 'SUM(3+6)']
}, }
]; ];
data.forEach(datum => { data.forEach(datum => {
@ -147,20 +148,20 @@ suite('Query Parser Tests', () => {
{ {
desc: 'Simple equals condition', desc: 'Simple equals condition',
clause: 'table1.field1=table2.field2', clause: 'table1.field1=table2.field2',
expected: '"table1"."field1" = "table2"."field2"', expected: '"table1"."field1" = "table2"."field2"'
}, { }, {
desc: 'Db.table.field condition', desc: 'Db.table.field condition',
clause: 'db1.table1.field1!=db2.table2.field2', 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', desc: 'Underscore in identifier',
clause: 'table_1.field1 = tab_le2.field_2', 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', desc: 'Function in condition',
clause: 'table1.field1 > SUM(3+6)', clause: 'table1.field1 > SUM(3+6)',
expected: '"table1"."field1" > SUM(3+6)', expected: '"table1"."field1" > SUM(3+6)'
}, }
]; ];
data.forEach(datum => { data.forEach(datum => {