From da8b473bc769f8f070f7c576e74c6c61838fb51c Mon Sep 17 00:00:00 2001 From: Timothy J Warren Date: Tue, 26 Jan 2016 19:29:12 -0500 Subject: [PATCH] Add promise interface to database execution methods --- README.md | 1 + docs/assets/style.css | 2 +- docs/index.html | 3411 ++++++++++++------------ gulpfile.js | 24 +- lib/Adapter.js | 22 +- lib/Driver.js | 46 +- lib/NodeQuery.js | 7 +- lib/QueryBuilder.js | 78 +- lib/QueryParser.js | 12 +- lib/State.js | 11 +- lib/adapters/dblite.js | 18 +- lib/adapters/mysql.js | 15 +- lib/adapters/mysql2.js | 15 +- lib/adapters/node-firebird.js | 25 +- lib/adapters/pg.js | 16 +- lib/helpers.js | 11 +- package.json | 43 +- test/adapters/adapterTestBase.js | 479 ---- test/adapters/dblite_test.js | 103 +- test/adapters/mysql-base.js | 64 + test/adapters/mysql2_test.js | 50 +- test/adapters/mysql_test.js | 50 +- test/adapters/node-firebird.js | 51 + test/adapters/node-firebird_test.js | 46 - test/adapters/pg_test.js | 85 +- test/base.js | 13 + test/base/adapterCallbackTestRunner.js | 236 ++ test/base/adapterPromiseTestRunner.js | 181 ++ test/base/tests.js | 246 ++ test/base_test.js | 2 +- test/helpers_test.js | 20 +- test/mocha.opts | 10 +- test/query-parser_test.js | 342 +-- 33 files changed, 3096 insertions(+), 2639 deletions(-) delete mode 100644 test/adapters/adapterTestBase.js create mode 100644 test/adapters/mysql-base.js create mode 100644 test/adapters/node-firebird.js delete mode 100644 test/adapters/node-firebird_test.js create mode 100644 test/base.js create mode 100644 test/base/adapterCallbackTestRunner.js create mode 100644 test/base/adapterPromiseTestRunner.js create mode 100644 test/base/tests.js diff --git a/README.md b/README.md index 324c625..bbf36a9 100755 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ A node query builder for various SQL databases, based on CodeIgniter's query bui ### Installation npm install ci-node-query + [![NPM](https://nodei.co/npm/ci-node-query.png?downloads=true&downloadRank=true&stars=true)](https://nodei.co/npm/ci-node-query/) ### Basic use diff --git a/docs/assets/style.css b/docs/assets/style.css index a675e62..d32c748 100644 --- a/docs/assets/style.css +++ b/docs/assets/style.css @@ -6,7 +6,7 @@ margin:0; } -.force-inline p { +.force-inline, .force-inline p { display: inline; color: #222; } diff --git a/docs/index.html b/docs/index.html index 35f4112..46bd3d6 100644 --- a/docs/index.html +++ b/docs/index.html @@ -1,3 +1,4 @@ + @@ -21,1811 +22,1943 @@ class='col12 block field' type='text' />
+ + NodeQuery + + + #getQuery + + + #init + QueryBuilder - - - State - - - helpers - .arrayPluck + #delete - .isScalar + #end - .regexInArray + #from - .stringTrim + #get - .type + #getCompiledDelete - .upperCaseFirst + #getCompiledInsert + + + #getCompiledSelect + + + #getCompiledUpdate + + + #groupBy + + + #groupEnd + + + #groupStart + + + #having + + + #insert + + + #insertBatch + + + #join + + + #like + + + #limit + + + #notLike + + + #orGroupStart + + + #orHaving + + + #orLike + + + #orNotGroupStart + + + #orNotLike + + + #orWhere + + + #orWhereIn + + + #orWhereIsNotNull + + + #orWhereIsNull + + + #orWhereNotIn + + + #orderBy + + + #resetQuery + + + #select + + + #set + + + #update + + + #where + + + #whereIn + + + #whereIsNotNull + + + #whereIsNull + + + #whereNotIn - - compileJoin - - - constructor - - - delete - - - end - - - filterMatches - - - from - - - get - - - getCompiledDelete - - - getCompiledInsert - - - getCompiledSelect - - - getCompiledUpdate - - - groupBy - - - groupEnd - - - groupStart - - - hasOperator - - - having - - - insert - - - insertBatch - - - join - - - like - - - limit - - - notLike - - - orGroupStart - - - orHaving - - - orLike - - - orNotGroupStart - - - orNotLike - - - orWhere - - - orWhereIn - - - orWhereIsNotNull - - - orWhereIsNull - - - orWhereNotIn - - - orderBy - - - parseJoin - - - parseWhere - - - resetQuery - - - select - - - set - - - update - - - where - - - whereIn - - - whereIsNotNull - - - whereIsNull - - - whereNotIn -
+

+ NodeQuery +

+

Class for connection management

+ +

Instance members

+
+ + + #getQuery + +
+

Return an existing query builder instance

+ +
+
+
+
+

+ getQuery +

+

Return an existing query builder instance

+ +

Returns

+ QueryBuilder + : +
+

The Query Builder object

+ +
+
+
+
+
+ + + #init(driverType, connObject, [connLib]) + +
+

Create a query builder object

+ +
+
+
+
+

+ init(driverType, connObject, [connLib]) +

+

Create a query builder object

+ +

Parameters

+
    +
  • String driverType + : +
    +

    The name of the database type, eg. mysql or pg

    + +
    +
  • +
  • Object connObject + : +
    +

    A connection object from the database library you are connecting with

    + +
    +
  • +
  • [String] connLib + : +
    +

    The name of the db connection library you are using, eg. mysql or mysql2. Optional if the same as driverType

    + +
    +
  • +
+

Returns

+ QueryBuilder + : +
+

The Query Builder object

+ +
+
+
+
+
+

- QueryBuilder + QueryBuilder(Driver, Adapter)

- -
-
-

- State -

- -
-
-

- helpers(o) -

-

Determine whether a variable is of the type specified in the -function name, eg isNumber

-

Types available are Null, Undefined, Object, Array, String, Number, Boolean, Function, RegExp, NaN and Infinite

+

Main object that builds SQL queries.

Parameters

    -
  • mixed o +
  • Driver Driver : - - - +
    +

    The syntax driver for the database

    + +
    +
  • +
  • Adapter Adapter + : +
    +

    The database module adapter for running queries

    + +
-

Returns

- Boolean - - - - -

Static members

-
- +

Instance members

+
+ - .arrayPluck(arr, key) + #delete(table, [where], [callback]) - -

Get a list of values with a common key from an array of objects

+
+

Run the generated delete query

- +
-
+
-

- arrayPluck(arr, key) +

+ delete(table, [where], [callback])

-

Get a list of values with a common key from an array of objects

+

Run the generated delete query

Parameters

    -
  • Array arr +
  • String table : - -

    The array of objects to search

    +
    +

    The table to insert into

    - +
  • -
  • String key +
  • [Object] where : - -

    The key of the object to get

    +
    +

    Where clause for delete statement

    - +
    +
  • +
  • [Function] callback + : +
    +

    Callback for handling response from the database

    + +

Returns

- Array + void or Promise : - -

The new array of plucked values

+
+

If no callback is passed, a promise is returned

- +
-
- +
+ - .isScalar(obj) + #end - -

Determine whether an object is scalar

+
+

Closes the database connection for the current adapter

- +
-
+
-

- isScalar(obj) +

+ end

-

Determine whether an object is scalar

+

Closes the database connection for the current adapter

+ +

Returns

+ void + +
+ +
+
+
+
+
+ + + #from(tableName) + +
+

Specify the database table to select from

+ +
+
+
+
+

+ from(tableName) +

+

Specify the database table to select from

Parameters

    -
  • mixed obj +
  • String tableName : - -

    Object to test

    +
    +

    The table to use for the current query

    - +

Returns

- bool + QueryBuilder : - -

Is object scalar

+
+

The Query Builder object, for chaining

- +
-
- +
+ - .regexInArray(arr, pattern) + #get([table], [limit], [offset], [callback]) - -

Determine if a value matching the passed regular expression is -in the passed array

+
+

Get the results of the compiled query

- +
-
+
-

- regexInArray(arr, pattern) +

+ get([table], [limit], [offset], [callback])

-

Determine if a value matching the passed regular expression is - in the passed array

+

Get the results of the compiled query

Parameters

    -
  • Array arr +
  • [String] table : - -

    The array to search

    +
    +

    The table to select from

    - +
  • -
  • RegExp pattern +
  • [Number] limit : - -

    The pattern to match

    +
    +

    A limit for the query

    - +
    +
  • +
  • [Number] offset + : +
    +

    An offset for the query

    + +
    +
  • +
  • [Function] callback + : +
    +

    A callback for receiving the result

    + +

Returns

- Boolean + void or Promise : - -

If an array item matches the pattern

+
+

If no callback is passed, a promise is returned

- +
-
- +
+ - .stringTrim(str) + #getCompiledDelete(table, [reset]) - -

Wrap String.prototype.trim in a way that is easily mappable

+
+

Return generated delete query SQL

- +
-
+
-

- stringTrim(str) +

+ getCompiledDelete(table, [reset])

-

Wrap String.prototype.trim in a way that is easily mappable

+

Return generated delete query SQL

Parameters

    -
  • String str +
  • String table : - -

    The string to trim

    +
    +

    the name of the table to delete from

    - +
    +
  • +
  • [Boolean] reset + (default true) + : +
    +

    Whether to reset the query builder so another query can be built

    + +

Returns

String : - -

The trimmed string

+
+

The compiled sql statement

- +
-
- +
+ - .type(o) + #getCompiledInsert(table, [reset]) - -

Get the type of the variable passed

+
+

Return generated insert query SQL

- +
-
+
-

- type(o) +

+ getCompiledInsert(table, [reset])

-

Get the type of the variable passed

+

Return generated insert query SQL

Parameters

    -
  • mixed o +
  • String table : - -

    Object to type check

    +
    +

    the name of the table to insert into

    - +
    +
  • +
  • [Boolean] reset + (default true) + : +
    +

    Whether to reset the query builder so another query can be built

    + +

Returns

String : - -

Type of the object

+
+

The compiled sql statement

- +
-
- +
+ - .upperCaseFirst(str) + #getCompiledSelect([table], [reset]) - -

Make the first letter of the string uppercase

+
+

Return generated select query SQL

- +
-
+
-

- upperCaseFirst(str) +

+ getCompiledSelect([table], [reset])

-

Make the first letter of the string uppercase

+

Return generated select query SQL

Parameters

    -
  • String str +
  • [String] table : - -

    The string to modify

    +
    +

    the name of the table to retrieve from

    - +
    +
  • +
  • [Boolean] reset + (default true) + : +
    +

    Whether to reset the query builder so another query can be built

    + +

Returns

String : - -

The modified string

+
+

The compiled sql statement

- +
-
-
-

- compileJoin(condition) -

-

Return the output of the parsing of the join condition

- -

Parameters

-
    -
  • String condition - : - -

    The join condition to evalate

    - -
    -
  • -
-

Returns

- String - : - -

The parsed/escaped join condition

- -
-
-
-

- constructor(driver) -

- -

Parameters

-
    -
  • Driver driver - : - -

    The driver object for the database in use

    - -
    -
  • -
-

Returns

- void - - - - -
-
-

- delete(table, [where], callback) -

-

Run the generated delete query

- -

Parameters

-
    -
  • String table - : - -

    The table to insert into

    - -
    -
  • -
  • [Object] where - : - -

    Where clause for delete statement

    - -
    -
  • -
  • Function callback - : - -

    Callback for handling response from the database

    - -
    -
  • -
-

Returns

- void - - - - -
-
-

- end -

-

Closes the database connection for the current adapter

- -

Returns

- void - - - - -
-
-

- filterMatches(array) -

-

Filter matched patterns

- -

Parameters

-
    -
  • Array array - : - -

    Set of possible matches

    - -
    -
  • -
-

Returns

- Array or - : - -

Filtered set of possible matches

- -
-
-
-

- from(tableName) -

-

Specify the database table to select from

- -

Parameters

-
    -
  • String tableName - : - -

    The table to use for the current query

    - -
    -
  • -
-

Returns

- QueryBuilder - : - -

The Query Builder object, for chaining

- -
-
-
-

- get([table], [limit], [offset], callback) -

-

Get the results of the compiled query

- -

Parameters

-
    -
  • [String] table - : - -

    The table to select from

    - -
    -
  • -
  • [Number] limit - : - -

    A limit for the query

    - -
    -
  • -
  • [Number] offset - : - -

    An offset for the query

    - -
    -
  • -
  • Function callback - : - -

    A callback for receiving the result

    - -
    -
  • -
-

Returns

- void - - - - -
-
-

- getCompiledDelete(table, [reset]) -

-

Return generated delete query SQL

- -

Parameters

-
    -
  • String table - : - -

    the name of the table to delete from

    - -
    -
  • -
  • [Boolean] reset - (default true) - : - -

    Whether to reset the query builder so another query can be built

    - -
    -
  • -
-

Returns

- String - : - -

The compiled sql statement

- -
-
-
-

- getCompiledInsert(table, [reset]) -

-

Return generated insert query SQL

- -

Parameters

-
    -
  • String table - : - -

    the name of the table to insert into

    - -
    -
  • -
  • [Boolean] reset - (default true) - : - -

    Whether to reset the query builder so another query can be built

    - -
    -
  • -
-

Returns

- String - : - -

The compiled sql statement

- -
-
-
-

- getCompiledSelect([table], [reset]) -

-

Return generated select query SQL

- -

Parameters

-
    -
  • [String] table - : - -

    the name of the table to retrieve from

    - -
    -
  • -
  • [Boolean] reset - (default true) - : - -

    Whether to reset the query builder so another query can be built

    - -
    -
  • -
-

Returns

- String - : - -

The compiled sql statement

- -
-
-
-

- getCompiledUpdate(table, [reset]) -

-

Return generated update query SQL

- -

Parameters

-
    -
  • String table - : - -

    the name of the table to update

    - -
    -
  • -
  • [Boolean] reset - (default true) - : - -

    Whether to reset the query builder so another query can be built

    - -
    -
  • -
-

Returns

- String - : - -

The compiled sql statement

- -
-
-
-

- groupBy(field) -

-

Group the results by the selected field(s)

- -

Parameters

-
    -
  • String or Array field - : - -

    The name of the field to group by

    - -
    -
  • -
-

Returns

- QueryBuilder - : - -

The Query Builder object, for chaining

- -
-
-
-

- groupEnd -

-

Ends a logical grouping started with one of the groupStart methods

- -

Returns

- QueryBuilder - : - -

The Query Builder object, for chaining

- -
-
-
-

- groupStart -

-

Adds an open paren to the current query for logical grouping

- -

Returns

- QueryBuilder - : - -

The Query Builder object, for chaining

- -
-
-
-

- hasOperator(string) -

-

Check if the string contains an operator, and if so, return the operator(s). -If there are no matches, return null

- -

Parameters

-
    -
  • String string - : - -

    the string to check

    - -
    -
  • -
-

Returns

- Array or - : - -

List of operators

- -
-
-
-

- having(key, [val]) -

-

Add a 'having' clause

- -

Parameters

-
    -
  • String or Object key - : - -

    The name of the field and the comparision operator, or an object

    - -
    -
  • -
  • [String or Number] val - : - -

    The value to compare if the value of key is a string

    - -
    -
  • -
-

Returns

- QueryBuilder - : - -

The Query Builder object, for chaining

- -
-
-
-

- insert(table, [data], callback) -

-

Run the generated insert query

- -

Parameters

-
    -
  • String table - : - -

    The table to insert into

    - -
    -
  • -
  • [Object] data - : - -

    Data to insert, if not already added with the 'set' method

    - -
    -
  • -
  • Function callback - : - -

    Callback for handling response from the database

    - -
    -
  • -
-

Returns

- void - - - - -
-
-

- insertBatch(table, data, callback) -

-

Insert multiple sets of rows at a time

- -

Parameters

-
    -
  • String table - : - -

    The table to insert into

    - -
    -
  • -
  • Array data - : - -

    The array of objects containing data rows to insert

    - -
    -
  • -
  • Function callback - : - -

    Callback for handling database response

    - -
    -
  • -
-

Returns

- void - - - - -

Examples

-
query.insertBatch('foo',[{id:1,val:'bar'},{id:2,val:'baz'}], callbackFunction);
-
-
-

- join(table, cond, [type]) -

-

Add a join clause to the query

- -

Parameters

-
    -
  • String table - : - -

    The table you are joining

    - -
    -
  • -
  • String cond - : - -

    The join condition.

    - -
    -
  • -
  • [String] type - (default 'inner') - : - -

    The type of join, which defaults to inner

    - -
    -
  • -
-

Returns

- QueryBuilder - : - -

The Query Builder object, for chaining

- -
-
-
-

- like(field, val, [pos]) -

-

Add a 'like/ and like' clause to the query

- -

Parameters

-
    -
  • String field - : - -

    The name of the field to compare to

    - -
    -
  • -
  • String val - : - -

    The value to compare to

    - -
    -
  • -
  • [String] pos - (default both) - : - -

    The placement of the wildcard character(s): before, after, or both

    - -
    -
  • -
-

Returns

- QueryBuilder - : - -

The Query Builder object, for chaining

- -
-
-
-

- limit(limit, [offset]) -

-

Put a limit on the query

- -

Parameters

-
    -
  • Number limit - : - -

    The maximum number of rows to fetch

    - -
    -
  • -
  • [Number] offset - : - -

    The row number to start from

    - -
    -
  • -
-

Returns

- QueryBuilder - : - -

The Query Builder object, for chaining

- -
-
-
-

- notLike(field, val, [pos]) -

-

Add a 'not like/ and not like' clause to the query

- -

Parameters

-
    -
  • String field - : - -

    The name of the field to compare to

    - -
    -
  • -
  • String val - : - -

    The value to compare to

    - -
    -
  • -
  • [String] pos - (default both) - : - -

    The placement of the wildcard character(s): before, after, or both

    - -
    -
  • -
-

Returns

- QueryBuilder - : - -

The Query Builder object, for chaining

- -
-
-
-

- orGroupStart -

-

Adds an open paren to the current query for logical grouping, +

+ + + #getCompiledUpdate(table, [reset]) + +
+

Return generated update query SQL

+ +
+
+
+
+

+ getCompiledUpdate(table, [reset]) +

+

Return generated update query SQL

+ +

Parameters

+
    +
  • String table + : +
    +

    the name of the table to update

    + +
    +
  • +
  • [Boolean] reset + (default true) + : +
    +

    Whether to reset the query builder so another query can be built

    + +
    +
  • +
+

Returns

+ String + : +
+

The compiled sql statement

+ +
+
+
+
+
+ + + #groupBy(field) + +
+

Group the results by the selected field(s)

+ +
+
+
+
+

+ groupBy(field) +

+

Group the results by the selected field(s)

+ +

Parameters

+
    +
  • String or Array field + : +
    +

    The name of the field to group by

    + +
    +
  • +
+

Returns

+ QueryBuilder + : +
+

The Query Builder object, for chaining

+ +
+
+
+
+
+ + + #groupEnd + +
+

Ends a logical grouping started with one of the groupStart methods

+ +
+
+
+
+

+ groupEnd +

+

Ends a logical grouping started with one of the groupStart methods

+ +

Returns

+ QueryBuilder + : +
+

The Query Builder object, for chaining

+ +
+
+
+
+
+ + + #groupStart + +
+

Adds an open paren to the current query for logical grouping

+ +
+
+
+
+

+ groupStart +

+

Adds an open paren to the current query for logical grouping

+ +

Returns

+ QueryBuilder + : +
+

The Query Builder object, for chaining

+ +
+
+
+
+
+ + + #having(key, [val]) + +
+

Add a 'having' clause

+ +
+
+
+
+

+ having(key, [val]) +

+

Add a 'having' clause

+ +

Parameters

+
    +
  • String or Object key + : +
    +

    The name of the field and the comparision operator, or an object

    + +
    +
  • +
  • [String or Number] val + : +
    +

    The value to compare if the value of key is a string

    + +
    +
  • +
+

Returns

+ QueryBuilder + : +
+

The Query Builder object, for chaining

+ +
+
+
+
+
+ + + #insert(table, [data], [callback]) + +
+

Run the generated insert query

+ +
+
+
+
+

+ insert(table, [data], [callback]) +

+

Run the generated insert query

+ +

Parameters

+
    +
  • String table + : +
    +

    The table to insert into

    + +
    +
  • +
  • [Object] data + : +
    +

    Data to insert, if not already added with the 'set' method

    + +
    +
  • +
  • [Function] callback + : +
    +

    Callback for handling response from the database

    + +
    +
  • +
+

Returns

+ void or Promise + : +
+

If no callback is passed, a promise is returned

+ +
+
+
+
+
+ + + #insertBatch(table, data, [callback]) + +
+

Insert multiple sets of rows at a time

+ +
+
+
+
+

+ insertBatch(table, data, [callback]) +

+

Insert multiple sets of rows at a time

+ +

Parameters

+
    +
  • String table + : +
    +

    The table to insert into

    + +
    +
  • +
  • Array data + : +
    +

    The array of objects containing data rows to insert

    + +
    +
  • +
  • [Function] callback + : +
    +

    Callback for handling database response

    + +
    +
  • +
+

Returns

+ void or Promise + : +
+

If no callback is passed, a promise is returned

+ +
+

Examples

+
query.insertBatch('foo',[{id:1,val:'bar'},{id:2,val:'baz'}], callbackFunction);
+
+
+
+
+ + + #join(table, cond, [type]) + +
+

Add a join clause to the query

+ +
+
+
+
+

+ join(table, cond, [type]) +

+

Add a join clause to the query

+ +

Parameters

+
    +
  • String table + : +
    +

    The table you are joining

    + +
    +
  • +
  • String cond + : +
    +

    The join condition.

    + +
    +
  • +
  • [String] type + (default 'inner') + : +
    +

    The type of join, which defaults to inner

    + +
    +
  • +
+

Returns

+ QueryBuilder + : +
+

The Query Builder object, for chaining

+ +
+
+
+
+
+ + + #like(field, val, [pos]) + +
+

Add a 'like/ and like' clause to the query

+ +
+
+
+
+

+ like(field, val, [pos]) +

+

Add a 'like/ and like' clause to the query

+ +

Parameters

+
    +
  • String field + : +
    +

    The name of the field to compare to

    + +
    +
  • +
  • String val + : +
    +

    The value to compare to

    + +
    +
  • +
  • [String] pos + (default both) + : +
    +

    The placement of the wildcard character(s): before, after, or both

    + +
    +
  • +
+

Returns

+ QueryBuilder + : +
+

The Query Builder object, for chaining

+ +
+
+
+
+
+ + + #limit(limit, [offset]) + +
+

Put a limit on the query

+ +
+
+
+
+

+ limit(limit, [offset]) +

+

Put a limit on the query

+ +

Parameters

+
    +
  • Number limit + : +
    +

    The maximum number of rows to fetch

    + +
    +
  • +
  • [Number] offset + : +
    +

    The row number to start from

    + +
    +
  • +
+

Returns

+ QueryBuilder + : +
+

The Query Builder object, for chaining

+ +
+
+
+
+
+ + + #notLike(field, val, [pos]) + +
+

Add a 'not like/ and not like' clause to the query

+ +
+
+
+
+

+ notLike(field, val, [pos]) +

+

Add a 'not like/ and not like' clause to the query

+ +

Parameters

+
    +
  • String field + : +
    +

    The name of the field to compare to

    + +
    +
  • +
  • String val + : +
    +

    The value to compare to

    + +
    +
  • +
  • [String] pos + (default both) + : +
    +

    The placement of the wildcard character(s): before, after, or both

    + +
    +
  • +
+

Returns

+ QueryBuilder + : +
+

The Query Builder object, for chaining

+ +
+
+
+
+
+ + + #orGroupStart + + + +
+
+

+ orGroupStart +

+

Adds an open paren to the current query for logical grouping, + prefixed with 'OR'

+ +

Returns

+ QueryBuilder + : +
+

The Query Builder object, for chaining

+ +
+
+
+
+
-
+ +
+
+

+ orHaving(key, [val]) +

+

Add an 'or having' clause

+ +

Parameters

+
    +
  • String or Object key + : +
    +

    The name of the field and the comparision operator, or an object

    + +
    +
  • +
  • [String or Number] val + : +
    +

    The value to compare if the value of key is a string

    + +
    +
  • +
+

Returns

+ QueryBuilder + : +
+

The Query Builder object, for chaining

+ +
+
+
+
+
+ + + #orLike(field, val, [pos]) + +
+

Add an 'or like' clause to the query

-

Parameters

-
    -
  • String or Object key - : - -

    The name of the field and the comparision operator, or an object

    - -
    -
  • -
  • [String or Number] val - : - -

    The value to compare if the value of key is a string

    - -
    -
  • -
-

Returns

- QueryBuilder - : - -

The Query Builder object, for chaining

- -
- -
-

- orLike(field, val, [pos]) -

-

Add an 'or like' clause to the query

- -

Parameters

-
    -
  • String field - : - -

    The name of the field to compare to

    - -
    -
  • -
  • String val - : - -

    The value to compare to

    - -
    -
  • -
  • [String] pos - (default both) - : - -

    The placement of the wildcard character(s): before, after, or both

    - -
    -
  • -
-

Returns

- QueryBuilder - : - -

The Query Builder object, for chaining

- -
-
-
-

- orNotGroupStart -

-

Adds an open paren to the current query for logical grouping, +

+ +
+
+

+ orLike(field, val, [pos]) +

+

Add an 'or like' clause to the query

+ +

Parameters

+
    +
  • String field + : +
    +

    The name of the field to compare to

    + +
    +
  • +
  • String val + : +
    +

    The value to compare to

    + +
    +
  • +
  • [String] pos + (default both) + : +
    +

    The placement of the wildcard character(s): before, after, or both

    + +
    +
  • +
+

Returns

+ QueryBuilder + : +
+

The Query Builder object, for chaining

+ +
+
+
+
+
+ + + #orNotGroupStart + +
-

- orNotLike(field, val, [pos]) -

-

Add an 'or not like' clause to the query

- -

Parameters

-
    -
  • String field - : - -

    The name of the field to compare to

    - -
    -
  • -
  • String val - : - -

    The value to compare to

    - -
    -
  • -
  • [String] pos - (default both) - : - -

    The placement of the wildcard character(s): before, after, or both

    - -
    -
  • -
-

Returns

- QueryBuilder - : - -

The Query Builder object, for chaining

- -
-
-
-

- orWhere(key, [val]) -

-

Set a 'or where' clause

- -

Parameters

-
    -
  • String or Object key - : - -

    The name of the field and the comparision operator, or an object

    - -
    -
  • -
  • [String or Number] val - : - -

    The value to compare if the value of key is a string

    - -
    -
  • -
-

Returns

- QueryBuilder - : - -

The Query Builder object, for chaining

- -
-
-
-

- orWhereIn(key, val) -

-

Set a 'or where in' clause

- -

Parameters

-
    -
  • String key - : - -

    the field to search

    - -
    -
  • -
  • Array val - : - -

    the array of items to search in

    - -
    -
  • -
-

Returns

- QueryBuilder - : - -

The Query Builder object, for chaining

- -
-
-
-

- orWhereIsNotNull(field) -

-

Field is not null prefixed with 'OR'

- -

Parameters

-
    -
  • String field - : - -

    The name of the field

    - -
    -
  • -
-

Returns

- QueryBuilder - : - -

The Query Builder object, for chaining

- -
-
-
-

- orWhereIsNull(field) -

-

Field is null prefixed with 'OR'

- -

Parameters

-
    -
  • String field - : - -

    The name of the field

    - -
    -
  • -
-

Returns

- QueryBuilder - : - -

The Query Builder object, for chaining

- -
-
-
-

- orWhereNotIn(key, val) -

-

Set a 'or where not in' clause

- -

Parameters

-
    -
  • String key - : - -

    the field to search

    - -
    -
  • -
  • Array val - : - -

    the array of items to search in

    - -
    -
  • -
-

Returns

- QueryBuilder - : - -

The Query Builder object, for chaining

- -
-
-
-

- orderBy(field, [type]) -

-

Order the results by the selected field(s)

- -

Parameters

-
    -
  • String field - : - -

    The field(s) to order by

    - -
    -
  • -
  • [String] type - (default 'ASC') - : - -

    The order direction, ASC or DESC

    - -
    -
  • -
-

Returns

- QueryBuilder - : - -

The Query Builder object, for chaining

- -
-
-
-

- parseJoin(sql) -

-

Tokenize the sql into parts for additional processing

- -

Parameters

-
    -
  • String sql - : - -

    Join sql to parse

    - -
    -
  • -
-

Returns

- Object - : - -

Join condition components

- -
-
-
-

- parseWhere(driver, state) -

-

Parse a where clause to separate functions from values

- -

Parameters

-
    -
  • Driver driver - : - -

    The current db driver

    - -
    -
  • -
  • State state - : - -

    Query Builder state object

    - -
    -
  • -
-

Returns

- String - : - -

The parsed/escaped where condition

- -
-
-
-

- resetQuery -

-

Reset the object state for a new query

- -

Returns

- void - - - - -
-
-

- select(fields) -

-

Specify rows to select in the query

- -

Parameters

-
    -
  • String or Array fields - : - -

    The fields to select from the current table

    - -
    -
  • -
-

Returns

- QueryBuilder - : - -

The Query Builder object, for chaining

- -
-
-
-

- set(key, [val]) -

-

Set values for insertion or updating

- -

Parameters

-
    -
  • String or Object key - : - -

    The key or object to use

    - -
    -
  • -
  • [String] val - : - -

    The value if using a scalar key

    - -
    -
  • -
-

Returns

- QueryBuilder - : - -

The Query Builder object, for chaining

- -
-
-
-

- update(table, [data], callback) -

-

Run the generated update query

- -

Parameters

-
    -
  • String table - : - -

    The table to insert into

    - -
    -
  • -
  • [Object] data - : - -

    Data to insert, if not already added with the 'set' method

    - -
    -
  • -
  • Function callback - : - -

    Callback for handling response from the database

    - -
    -
  • -
-

Returns

- void - - - - -
-
-

- where(key, [val]) -

-

Set a 'where' clause

- -

Parameters

-
    -
  • String or Object key - : - -

    The name of the field and the comparision operator, or an object

    - -
    -
  • -
  • [String or Number] val - : - -

    The value to compare if the value of key is a string

    - -
    -
  • -
-

Returns

- QueryBuilder - : - -

The Query Builder object, for chaining

- -
-
-
-

- whereIn(key, val) -

-

Set a 'where in' clause

- -

Parameters

-
    -
  • String key - : - -

    the field to search

    - -
    -
  • -
  • Array val - : - -

    the array of items to search in

    - -
    -
  • -
-

Returns

- QueryBuilder - : - -

The Query Builder object, for chaining

- -
-
-
-

- whereIsNotNull(field) -

-

Specify that a field IS NOT NULL

- -

Parameters

-
    -
  • String field - : - -

    The name so the field that is not to be null

    - -
    -
  • -
-

Returns

- QueryBuilder - : - -

The Query Builder object, for chaining

- -
-
-
-

- whereIsNull(field) -

-

Select a field that is Null

- -

Parameters

-
    -
  • String field - : - -

    The name of the field that has a NULL value

    - -
    -
  • -
-

Returns

- QueryBuilder - : - -

The Query Builder object, for chaining

- -
-
-
-

- whereNotIn(key, val) -

-

Set a 'where not in' clause

- -

Parameters

-
    -
  • String key - : - -

    the field to search

    - -
    -
  • -
  • Array val - : - -

    the array of items to search in

    - -
    -
  • -
-

Returns

- QueryBuilder - : - -

The Query Builder object, for chaining

- -
+
+ +
+
+

+ orNotGroupStart +

+

Adds an open paren to the current query for logical grouping, + prefixed with 'OR NOT'

+ +

Returns

+ QueryBuilder + : +
+

The Query Builder object, for chaining

+ +
+
+
+
+
+ + + #orNotLike(field, val, [pos]) + +
+

Add an 'or not like' clause to the query

+ +
+
+
+
+

+ orNotLike(field, val, [pos]) +

+

Add an 'or not like' clause to the query

+ +

Parameters

+
    +
  • String field + : +
    +

    The name of the field to compare to

    + +
    +
  • +
  • String val + : +
    +

    The value to compare to

    + +
    +
  • +
  • [String] pos + (default both) + : +
    +

    The placement of the wildcard character(s): before, after, or both

    + +
    +
  • +
+

Returns

+ QueryBuilder + : +
+

The Query Builder object, for chaining

+ +
+
+
+
+
+ + + #orWhere(key, [val]) + +
+

Set a 'or where' clause

+ +
+
+
+
+

+ orWhere(key, [val]) +

+

Set a 'or where' clause

+ +

Parameters

+
    +
  • String or Object key + : +
    +

    The name of the field and the comparision operator, or an object

    + +
    +
  • +
  • [String or Number] val + : +
    +

    The value to compare if the value of key is a string

    + +
    +
  • +
+

Returns

+ QueryBuilder + : +
+

The Query Builder object, for chaining

+ +
+
+
+
+
+ + + #orWhereIn(key, val) + +
+

Set a 'or where in' clause

+ +
+
+
+
+

+ orWhereIn(key, val) +

+

Set a 'or where in' clause

+ +

Parameters

+
    +
  • String key + : +
    +

    the field to search

    + +
    +
  • +
  • Array val + : +
    +

    the array of items to search in

    + +
    +
  • +
+

Returns

+ QueryBuilder + : +
+

The Query Builder object, for chaining

+ +
+
+
+
+
+ + + #orWhereIsNotNull(field) + +
+

Field is not null prefixed with 'OR'

+ +
+
+
+
+

+ orWhereIsNotNull(field) +

+

Field is not null prefixed with 'OR'

+ +

Parameters

+
    +
  • String field + : +
    +

    The name of the field

    + +
    +
  • +
+

Returns

+ QueryBuilder + : +
+

The Query Builder object, for chaining

+ +
+
+
+
+
+ + + #orWhereIsNull(field) + +
+

Field is null prefixed with 'OR'

+ +
+
+
+
+

+ orWhereIsNull(field) +

+

Field is null prefixed with 'OR'

+ +

Parameters

+
    +
  • String field + : +
    +

    The name of the field

    + +
    +
  • +
+

Returns

+ QueryBuilder + : +
+

The Query Builder object, for chaining

+ +
+
+
+
+
+ + + #orWhereNotIn(key, val) + +
+

Set a 'or where not in' clause

+ +
+
+
+
+

+ orWhereNotIn(key, val) +

+

Set a 'or where not in' clause

+ +

Parameters

+
    +
  • String key + : +
    +

    the field to search

    + +
    +
  • +
  • Array val + : +
    +

    the array of items to search in

    + +
    +
  • +
+

Returns

+ QueryBuilder + : +
+

The Query Builder object, for chaining

+ +
+
+
+
+
+ + + #orderBy(field, [type]) + +
+

Order the results by the selected field(s)

+ +
+
+
+
+

+ orderBy(field, [type]) +

+

Order the results by the selected field(s)

+ +

Parameters

+
    +
  • String field + : +
    +

    The field(s) to order by

    + +
    +
  • +
  • [String] type + (default 'ASC') + : +
    +

    The order direction, ASC or DESC

    + +
    +
  • +
+

Returns

+ QueryBuilder + : +
+

The Query Builder object, for chaining

+ +
+
+
+
+
+ + + #resetQuery + +
+

Reset the object state for a new query

+ +
+
+
+
+

+ resetQuery +

+

Reset the object state for a new query

+ +

Returns

+ void + +
+ +
+
+
+
+
+ + + #select(fields) + +
+

Specify rows to select in the query

+ +
+
+
+
+

+ select(fields) +

+

Specify rows to select in the query

+ +

Parameters

+
    +
  • String or Array fields + : +
    +

    The fields to select from the current table

    + +
    +
  • +
+

Returns

+ QueryBuilder + : +
+

The Query Builder object, for chaining

+ +
+
+
+
+
+ + + #set(key, [val]) + +
+

Set values for insertion or updating

+ +
+
+
+
+

+ set(key, [val]) +

+

Set values for insertion or updating

+ +

Parameters

+
    +
  • String or Object key + : +
    +

    The key or object to use

    + +
    +
  • +
  • [String] val + : +
    +

    The value if using a scalar key

    + +
    +
  • +
+

Returns

+ QueryBuilder + : +
+

The Query Builder object, for chaining

+ +
+
+
+
+
+ + + #update(table, [data], [callback]) + +
+

Run the generated update query

+ +
+
+
+
+

+ update(table, [data], [callback]) +

+

Run the generated update query

+ +

Parameters

+
    +
  • String table + : +
    +

    The table to insert into

    + +
    +
  • +
  • [Object] data + : +
    +

    Data to insert, if not already added with the 'set' method

    + +
    +
  • +
  • [Function] callback + : +
    +

    Callback for handling response from the database

    + +
    +
  • +
+

Returns

+ void or Promise + : +
+

If no callback is passed, a promise is returned

+ +
+
+
+
+
+ + + #where(key, [val]) + +
+

Set a 'where' clause

+ +
+
+
+
+

+ where(key, [val]) +

+

Set a 'where' clause

+ +

Parameters

+
    +
  • String or Object key + : +
    +

    The name of the field and the comparision operator, or an object

    + +
    +
  • +
  • [String or Number] val + : +
    +

    The value to compare if the value of key is a string

    + +
    +
  • +
+

Returns

+ QueryBuilder + : +
+

The Query Builder object, for chaining

+ +
+
+
+
+
+ + + #whereIn(key, val) + +
+

Set a 'where in' clause

+ +
+
+
+
+

+ whereIn(key, val) +

+

Set a 'where in' clause

+ +

Parameters

+
    +
  • String key + : +
    +

    the field to search

    + +
    +
  • +
  • Array val + : +
    +

    the array of items to search in

    + +
    +
  • +
+

Returns

+ QueryBuilder + : +
+

The Query Builder object, for chaining

+ +
+
+
+
+
+ + + #whereIsNotNull(field) + +
+

Specify that a field IS NOT NULL

+ +
+
+
+
+

+ whereIsNotNull(field) +

+

Specify that a field IS NOT NULL

+ +

Parameters

+
    +
  • String field + : +
    +

    The name so the field that is not to be null

    + +
    +
  • +
+

Returns

+ QueryBuilder + : +
+

The Query Builder object, for chaining

+ +
+
+
+
+
+ + + #whereIsNull(field) + +
+

Select a field that is Null

+ +
+
+
+
+

+ whereIsNull(field) +

+

Select a field that is Null

+ +

Parameters

+
    +
  • String field + : +
    +

    The name of the field that has a NULL value

    + +
    +
  • +
+

Returns

+ QueryBuilder + : +
+

The Query Builder object, for chaining

+ +
+
+
+
+
+ + + #whereNotIn(key, val) + +
+

Set a 'where not in' clause

+ +
+
+
+
+

+ whereNotIn(key, val) +

+

Set a 'where not in' clause

+ +

Parameters

+
    +
  • String key + : +
    +

    the field to search

    + +
    +
  • +
  • Array val + : +
    +

    the array of items to search in

    + +
    +
  • +
+

Returns

+ QueryBuilder + : +
+

The Query Builder object, for chaining

+ +
+
+
+
diff --git a/gulpfile.js b/gulpfile.js index e321cea..23fc1b8 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -54,6 +54,13 @@ const ESLINT_SETTINGS = { } }; +const MOCHA_OPTIONS = { + ui: 'tdd', + bail: true, + reporter: 'list', + timeout: 10000, +}; + gulp.task('lint', () => { pipe(gulp.src(SRC_FILES), [ eslint(ESLINT_SETTINGS), @@ -82,22 +89,14 @@ gulp.task('sloc', () => gulp.src(SRC_FILES).pipe(sloc())); gulp.task('test-sloc', () => gulp.src(TEST_FILES).pipe(sloc())); gulp.task('docs', () => { - gulp.src('./lib/QueryBuilder.js') + gulp.src(['lib/*.js']) .pipe(documentation({format: 'html'})) .pipe(gulp.dest('docs')); - /*gulp.src('./lib/QueryBuilder.js') - .pipe(documentation({format: 'md'})) - .pipe(gulp.dest('api-docs'));*/ }); gulp.task('mocha', ['lint-tests', 'sloc'], () => { return gulp.src(TEST_FILES) - .pipe(mocha({ - ui: 'tdd', - bail: true, - reporter: 'list', - timeout: 5000 - })) + .pipe(mocha(MOCHA_OPTIONS)) .once('error', () => { process.exit(1); }) @@ -112,10 +111,7 @@ gulp.task('test', ['test-sloc', 'lint-tests'], function(cb) { istanbul.hookRequire() ]).on('finish', () => { pipe(gulp.src(TEST_FILES), [ - mocha({ - ui: 'tdd', - bail: true - }), + mocha(MOCHA_OPTIONS), istanbul.writeReports({ dir: './coverage', reporters: ['lcov', 'lcovonly', 'html', 'text'] diff --git a/lib/Adapter.js b/lib/Adapter.js index 3381415..c5a249b 100755 --- a/lib/Adapter.js +++ b/lib/Adapter.js @@ -1,12 +1,18 @@ + 'use strict'; -/** @module Adapter */ -module.exports = class Adapter { +/** + * Class that wraps database connection libraries + * + * @private + * @param {Object} instance - The connection object + */ +class Adapter { /** * Invoke an adapter * - * @param {Object} instance - The connection objec - * @return {void} + * @constructor + * @param {Object} instance - The connection object */ constructor(instance) { this.instance = instance; @@ -17,8 +23,8 @@ module.exports = class Adapter { * * @param {String} sql - The sql with placeholders * @param {Array} params - The values to insert into the query - * @param {Function} callback - Callback to run when a response is recieved - * @return {void} + * @param {Function} [callback] - Callback to run when a response is recieved + * @return {void|Promise} - returns a promise if no callback is passed */ execute(/*sql, params, callback*/) { throw new Error('Correct adapter not defined for query execution'); @@ -31,4 +37,6 @@ module.exports = class Adapter { close() { this.instance.end(); } -}; \ No newline at end of file +} + +module.exports = Adapter; \ No newline at end of file diff --git a/lib/Driver.js b/lib/Driver.js index a6f3e51..3d9b84d 100755 --- a/lib/Driver.js +++ b/lib/Driver.js @@ -1,13 +1,13 @@ 'use strict'; -let helpers = require('./helpers'); +const helpers = require('./helpers'); /** * Base Database Driver * - * @module driver + * @private */ -let d = { +let Driver = { identifierStartChar: '"', identifierEndChar: '"', tablePrefix: null, @@ -15,14 +15,14 @@ let d = { /** * Low level function for naive quoting of strings - + * * @param {String} str - The sql fragment to quote * @return {String} - The quoted sql fragment * @private */ _quote(str) { - return (helpers.isString(str) && ! (str.startsWith(d.identifierStartChar) || str.endsWith(d.identifierEndChar))) - ? `${d.identifierStartChar}${str}${d.identifierEndChar}` + return (helpers.isString(str) && ! (str.startsWith(Driver.identifierStartChar) || str.endsWith(Driver.identifierEndChar))) + ? `${Driver.identifierStartChar}${str}${Driver.identifierEndChar}` : str; }, @@ -31,7 +31,7 @@ let d = { * @param {String} sql - SQL statement to modify * @param {Number} limit - Maximum number of rows to fetch - * @param {Number|null} offset - Number of rows to skip + * @param {Number} [offset] - Number of rows to skip * @return {String} - Modified SQL statement */ limit(sql, limit, offset) { @@ -53,37 +53,37 @@ let d = { */ quoteTable(table) { // Quote after prefix - return d.quoteIdentifiers(table); + return Driver.quoteIdentifiers(table); }, /** * Use the driver's escape character to quote identifiers * - * @param {String|String[]} - String or array of strings to quote identifiers - * @return {String|String[]} - Quoted identifier(s) + * @param {String|Array} str - String or array of strings to quote identifiers + * @return {String|Array} - Quoted identifier(s) */ quoteIdentifiers(str) { let hiers, raw; let pattern = new RegExp( - `${d.identifierStartChar}(` + `${Driver.identifierStartChar}(` + '([a-zA-Z0-9_]+)' + '(\((.*?)\))' - + `)${d.identifierEndChar}`, 'ig'); + + `)${Driver.identifierEndChar}`, 'ig'); // Recurse for arrays of identifiiers if (Array.isArray(str)) { - return str.map(d.quoteIdentifiers); + return str.map(Driver.quoteIdentifiers); } // Handle commas if (str.includes(',')) { let parts = str.split(',').map(helpers.stringTrim); - str = parts.map(d.quoteIdentifiers).join(','); + str = parts.map(Driver.quoteIdentifiers).join(','); } // Split identifiers by period - hiers = str.split('.').map(d._quote); + hiers = str.split('.').map(Driver._quote); raw = hiers.join('.'); // Fix functions @@ -96,30 +96,30 @@ let d = { // Quote the identifiers inside of the parens let inParens = funcs[3].substring(1, funcs[3].length - 1); - raw = raw.replace(inParens, d.quoteIdentifiers(inParens)); + raw = raw.replace(inParens, Driver.quoteIdentifiers(inParens)); } return raw; }, /** - * SQL to truncate the passed table + * Generate SQL to truncate the passed table * * @param {String} table - Table to truncate * @return {String} - Truncation SQL */ truncate(table) { - let sql = (d.hasTruncate) + let sql = (Driver.hasTruncate) ? 'TRUNCATE ' : 'DELETE FROM '; - sql += d.quoteTable(table); + sql += Driver.quoteTable(table); return sql; }, /** - * SQL to insert a group of rows + * Generate SQL to insert a group of rows * * @param {String} table - The table to insert to * @param {Array} [data] - The array of object containing data to insert @@ -143,9 +143,9 @@ let d = { // Get the field names from the keys of the first // object inserted - table = d.quoteTable(table); + table = Driver.quoteTable(table); - sql += `INSERT INTO ${table} (${d.quoteIdentifiers(fields).join(',')}) VALUES `; + sql += `INSERT INTO ${table} (${Driver.quoteIdentifiers(fields).join(',')}) VALUES `; // Create placeholder groups params = Array(fields.length).fill('?'); @@ -161,4 +161,4 @@ let d = { }, }; -module.exports = d; \ No newline at end of file +module.exports = Driver; \ No newline at end of file diff --git a/lib/NodeQuery.js b/lib/NodeQuery.js index 2b3f50a..753956e 100755 --- a/lib/NodeQuery.js +++ b/lib/NodeQuery.js @@ -5,14 +5,15 @@ let fs = require('fs'), QueryBuilder = require('./QueryBuilder'); /** - * @module NodeQuery + * Class for connection management */ class NodeQuery { /** * Constructor * - * @return {void} + * @private + * @constructor */ constructor() { this.instance = null; @@ -23,7 +24,7 @@ class NodeQuery { * * @param {String} driverType - The name of the database type, eg. mysql or pg * @param {Object} connObject - A connection object from the database library you are connecting with - * @param {String} [connLib] - The name of the db connection library you are using, eg. mysql or mysql2. Optional if the same as drivername + * @param {String} [connLib] - The name of the db connection library you are using, eg. mysql or mysql2. Optional if the same as driverType * @return {QueryBuilder} - The Query Builder object */ init(driverType, connObject, connLib) { diff --git a/lib/QueryBuilder.js b/lib/QueryBuilder.js index 807f049..f6fe67c 100755 --- a/lib/QueryBuilder.js +++ b/lib/QueryBuilder.js @@ -1,23 +1,26 @@ 'use strict'; -/** @module QueryBuilder */ let getArgs = require('getargs'), helpers = require('./helpers'), State = require('./State'), QueryParser = require('./QueryParser'); -module.exports = class QueryBuilder { - /* - * SQL generation object - * - * @param {driver} - The syntax driver for the database - * @param {adapter} - The database module adapter for running queries - * @return {QueryBuilder} - The Query Builder object, for chaining +/** + * Main object that builds SQL queries. + * + * @param {Driver} Driver - The syntax driver for the database + * @param {Adapter} Adapter - The database module adapter for running queries + */ +class QueryBuilder { + /** + * @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; + constructor(Driver, Adapter) { + this.driver = Driver; + this.adapter = Adapter; this.parser = new QueryParser(this.driver); this.state = new State(); } @@ -265,8 +268,11 @@ module.exports = class QueryBuilder { this._resetState(); // Pass the sql and values to the adapter to run on the database - this.adapter.execute(sql, vals, callback); - + if (callback) { + return this.adapter.execute(sql, vals, callback); + } else { + return this.adapter.execute(sql, vals); + } } _getCompile(type, table, reset) { @@ -743,11 +749,11 @@ module.exports = class QueryBuilder { * @param {String} [table] - The table to select from * @param {Number} [limit] - A limit for the query * @param {Number} [offset] - An offset for the query - * @param {Function} callback - A callback for receiving the result - * @return {void} + * @param {Function} [callback] - A callback for receiving the result + * @return {void|Promise} - If no callback is passed, a promise is returned */ - get(/* [table], [limit], [offset], callback */) { - let args = getArgs('[table]:string, [limit]:number, [offset]:number, callback:function', arguments); + get(/* [table], [limit], [offset], [callback] */) { + let args = getArgs('[table]:string, [limit]:number, [offset]:number, [callback]:function', arguments); if (args.table) { this.from(args.table); @@ -758,7 +764,7 @@ module.exports = class QueryBuilder { } // Run the query - this._run('get', args.table, args.callback); + return this._run('get', args.table, args.callback); } /** @@ -766,18 +772,18 @@ module.exports = class QueryBuilder { * * @param {String} table - The table to insert into * @param {Object} [data] - Data to insert, if not already added with the 'set' method - * @param {Function} callback - Callback for handling response from the database - * @return {void} + * @param {Function} [callback] - Callback for handling response from the database + * @return {void|Promise} - If no callback is passed, a promise is returned */ insert(/* table, data, callback */) { - let args = getArgs('table:string, [data]:object, callback:function', arguments); + let args = getArgs('table:string, [data]:object, [callback]:function', arguments); if (args.data) { this.set(args.data); } // Run the query - this._run('insert', this.driver.quoteTable(args.table), args.callback); + return this._run('insert', this.driver.quoteTable(args.table), args.callback); } /** @@ -785,16 +791,16 @@ module.exports = class QueryBuilder { * * @param {String} table - The table to insert into * @param {Array} data - The array of objects containing data rows to insert - * @param {Function} callback - Callback for handling database response + * @param {Function} [callback] - Callback for handling database response * @example query.insertBatch('foo',[{id:1,val:'bar'},{id:2,val:'baz'}], callbackFunction); - * @return {void} + * @return {void|Promise} - If no callback is passed, a promise is returned */ 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); // Run the query - this._run('', '', args.callback, batch.sql, batch.values); + return this._run('', '', args.callback, batch.sql, batch.values); } /** @@ -802,18 +808,18 @@ module.exports = class QueryBuilder { * * @param {String} table - The table to insert into * @param {Object} [data] - Data to insert, if not already added with the 'set' method - * @param {Function} callback - Callback for handling response from the database - * @return {void} + * @param {Function} [callback] - Callback for handling response from the database + * @return {void|Promise} - If no callback is passed, a promise is returned */ update(/*table, data, callback*/) { - let args = getArgs('table:string, [data]:object, callback:function', arguments); + let args = getArgs('table:string, [data]:object, [callback]:function', arguments); if (args.data) { this.set(args.data); } // Run the query - this._run('update', this.driver.quoteTable(args.table), args.callback); + return this._run('update', this.driver.quoteTable(args.table), args.callback); } /** @@ -821,18 +827,18 @@ module.exports = class QueryBuilder { * * @param {String} table - The table to insert into * @param {Object} [where] - Where clause for delete statement - * @param {Function} callback - Callback for handling response from the database - * @return {void} + * @param {Function} [callback] - Callback for handling response from the database + * @return {void|Promise} - If no callback is passed, a promise is returned */ delete(/*table, [where], callback*/) { - let args = getArgs('table:string, [where]:object, callback:function', arguments); + let args = getArgs('table:string, [where]:object, [callback]:function', arguments); if (args.where) { this.where(args.where); } // Run the query - this._run('delete', this.driver.quoteTable(args.table), args.callback); + return this._run('delete', this.driver.quoteTable(args.table), args.callback); } // ------------------------------------------------------------------------ @@ -887,4 +893,6 @@ module.exports = class QueryBuilder { getCompiledDelete(table, reset) { return this._getCompile('delete', this.driver.quoteTable(table), reset); } -}; \ No newline at end of file +} + +module.exports = QueryBuilder; \ No newline at end of file diff --git a/lib/QueryParser.js b/lib/QueryParser.js index 9ae14ed..461a531 100644 --- a/lib/QueryParser.js +++ b/lib/QueryParser.js @@ -4,7 +4,13 @@ let helpers = require('./helpers'); // -------------------------------------------------------------------------- -module.exports = class QueryParser { +/** + * Internal object for parsing query fragments + * + * @private + * @param {Driver} driver - The driver object for the database in use + */ +class QueryParser { /** * @constructor * @@ -237,4 +243,6 @@ module.exports = class QueryParser { return state; } -}; \ No newline at end of file +} + +module.exports = QueryParser; \ No newline at end of file diff --git a/lib/State.js b/lib/State.js index edb2385..66cae2b 100644 --- a/lib/State.js +++ b/lib/State.js @@ -1,7 +1,10 @@ 'use strict'; -/** @module State */ -module.exports = class State { +/** + * Class for objects containing the query builder state + * @private + */ +class State { constructor() { // Arrays/maps this.queryMap = []; @@ -25,6 +28,8 @@ module.exports = class State { this.limit = null; this.offset = null; } -}; +} + +module.exports = State; // End of module State \ No newline at end of file diff --git a/lib/adapters/dblite.js b/lib/adapters/dblite.js index 371844e..76f325d 100644 --- a/lib/adapters/dblite.js +++ b/lib/adapters/dblite.js @@ -1,7 +1,8 @@ 'use strict'; let Adapter = require('../Adapter'), - getArgs = require('getargs'); + getArgs = require('getargs'), + Promise = require('bluebird'); module.exports = class dblite extends Adapter { /** @@ -9,16 +10,23 @@ module.exports = class dblite extends Adapter { * * @param {String} sql - The sql with placeholders * @param {Array} params - The values to insert into the query - * @param {Function} callback - Callback to run when a response is recieved - * @return {void} + * @param {Function} [callback] - Callback to run when a response is recieved + * @return {void|Promise} - Returns a promise if no callback is provided */ execute(/*sql, params, callback*/) { - let args = getArgs('sql:string, [params]:array, callback:function', arguments); - this.instance.query(args.sql, args.params, args.callback); + let args = getArgs('sql:string, [params]:array, [callback]:function', arguments); + let instance = Promise.promisifyAll(this.instance); + + if (! args.callback) { + return instance.queryAsync(args.sql, args.params); + } + + return this.instance.query(args.sql, args.params, args.callback); } /** * Close the current database connection + * @return {void} */ close() { diff --git a/lib/adapters/mysql.js b/lib/adapters/mysql.js index 9f0c1e2..6624618 100644 --- a/lib/adapters/mysql.js +++ b/lib/adapters/mysql.js @@ -1,7 +1,8 @@ 'use strict'; let Adapter = require('../Adapter'), - getArgs = require('getargs'); + getArgs = require('getargs'), + Promise = require('bluebird'); module.exports = class mysql extends Adapter { /** @@ -9,11 +10,17 @@ module.exports = class mysql extends Adapter { * * @param {String} sql - The sql with placeholders * @param {Array} params - The values to insert into the query - * @param {Function} callback - Callback to run when a response is recieved - * @return {void} + * @param {Function} [callback] - Callback to run when a response is recieved + * @return {void|Promise} - Returns a promise if no callback is provided */ execute(sql, params, callback) { - let args = getArgs('sql:string, [params], callback:function', arguments); + let args = getArgs('sql:string, [params], [callback]:function', arguments); + let instance = Promise.promisifyAll(this.instance); + + if (! args.callback) { + return instance.queryAsync(args.sql, args.params); + } + return this.instance.query(args.sql, args.params, args.callback); } }; \ No newline at end of file diff --git a/lib/adapters/mysql2.js b/lib/adapters/mysql2.js index 3b1d8f8..fc76a19 100644 --- a/lib/adapters/mysql2.js +++ b/lib/adapters/mysql2.js @@ -1,7 +1,8 @@ 'use strict'; let Adapter = require('../Adapter'), - getArgs = require('getargs'); + getArgs = require('getargs'), + Promise = require('bluebird'); module.exports = class mysql2 extends Adapter { /** @@ -9,11 +10,17 @@ module.exports = class mysql2 extends Adapter { * * @param {String} sql - The sql with placeholders * @param {Array} params - The values to insert into the query - * @param {Function} callback - Callback to run when a response is recieved - * @return {void} + * @param {Function} [callback] - Callback to run when a response is recieved + * @return {void|Promise} - Returns a promise if no callback is provided */ execute(/*sql, params, callback*/) { - let args = getArgs('sql:string, [params], callback:function', arguments); + let args = getArgs('sql:string, [params], [callback]:function', arguments); + let instance = Promise.promisifyAll(this.instance); + + if (! args.callback) { + return instance.executeAsync(args.sql, args.params); + } + return this.instance.execute(args.sql, args.params, args.callback); } }; \ No newline at end of file diff --git a/lib/adapters/node-firebird.js b/lib/adapters/node-firebird.js index e06d812..67e06fd 100644 --- a/lib/adapters/node-firebird.js +++ b/lib/adapters/node-firebird.js @@ -1,7 +1,8 @@ 'use strict'; let Adapter = require('../Adapter'), - getArgs = require('getargs'); + getArgs = require('getargs'), + Promise = require('bluebird'); module.exports = class nodefirebird extends Adapter { /** @@ -9,11 +10,25 @@ module.exports = class nodefirebird extends Adapter { * * @param {String} sql - The sql with placeholders * @param {Array} params - The values to insert into the query - * @param {Function} callback - Callback to run when a response is recieved - * @return {void} + * @param {Function} [callback] - Callback to run when a response is recieved + * @return {void|Promise} - Returns a promise if no callback is provided */ execute(/*sql, params, callback*/) { - let args = getArgs('sql:string, [params], callback:function', arguments); - return this.instance.execute(args.sql, args.params, args.callback); + let args = getArgs('sql:string, [params], [callback]:function', arguments); + let instance = Promise.promisifyAll(this.instance); + + if (! args.callback) { + return instance.queryAsync(args.sql, args.params); + } + + return this.instance.query(args.sql, args.params, args.callback); + } + + /** + * Close the current database connection + * @return {void} + */ + close() { + this.instance.detach(); } }; \ No newline at end of file diff --git a/lib/adapters/pg.js b/lib/adapters/pg.js index 65fb543..704f37e 100644 --- a/lib/adapters/pg.js +++ b/lib/adapters/pg.js @@ -1,7 +1,8 @@ 'use strict'; let Adapter = require('../Adapter'), - getArgs = require('getargs'); + getArgs = require('getargs'), + Promise = require('bluebird'); module.exports = class pg extends Adapter { /** @@ -9,11 +10,12 @@ module.exports = class pg extends Adapter { * * @param {String} sql - The sql with placeholders * @param {Array} params - The values to insert into the query - * @param {Function} callback - Callback to run when a response is recieved - * @return {void} + * @param {Function} [callback] - Callback to run when a response is recieved + * @return {void|Promise} - Returns a promise if no callback is provided */ execute(/*sql, params, callback*/) { - let args = getArgs('sql:string, [params]:array, callback:function', arguments); + let args = getArgs('sql:string, [params]:array, [callback]:function', arguments); + let instance = Promise.promisifyAll(this.instance); // Replace question marks with numbered placeholders, because this adapter is different... let count = 0; @@ -22,6 +24,10 @@ module.exports = class pg extends Adapter { return `$${count}`; }); - this.instance.query(args.sql, args.params, args.callback); + if (! args.callback) { + return instance.queryAsync(args.sql, args.params); + } + + return this.instance.query(args.sql, args.params, args.callback); } }; \ No newline at end of file diff --git a/lib/helpers.js b/lib/helpers.js index 3280079..f59bf8d 100755 --- a/lib/helpers.js +++ b/lib/helpers.js @@ -1,5 +1,10 @@ 'use strict'; +/** + * Various internal helper functions + * + * @private + */ let helpers = { /** * Wrap String.prototype.trim in a way that is easily mappable @@ -124,9 +129,9 @@ types.forEach(t => { * * Types available are Null, Undefined, Object, Array, String, Number, Boolean, Function, RegExp, NaN and Infinite * - * @name is[type] - * @param {mixed} o - * @return {Boolean} + * @private + * @param {mixed} o - The object to check its type + * @return {Boolean} - If the type matches */ helpers[`is${t}`] = function(o) { if (t.toLowerCase() === 'infinite') { diff --git a/package.json b/package.json index e1ef6a4..482e483 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,14 @@ { "name": "ci-node-query", - "version": "3.0.1", + "version": "3.1.0", "description": "A query builder for node based on the one in CodeIgniter", "author": "Timothy J Warren ", "engines": { "node": ">=4.0.0" }, + "files": [ + "lib/" + ], "contributors": [ { "name": "Timothy J Warren", @@ -33,36 +36,34 @@ }, "main": "lib/NodeQuery.js", "dependencies": { - "getargs": "", - "mysql": "^2.9.0", - "mysql2": "^0.15.8", - "node-firebird": "^0.7.0", - "pg": "^4.4.3", - "require-reload": "*" - }, - "optionalDependencies": { - "dblite": "*", - "node-firebird": "*", - "pg": "*" + "bluebird": "^3.1.4", + "dblite": "~0.7.6", + "getargs": "~0.0.8", + "mysql": "~2.10.2", + "mysql2": "~0.15.8", + "node-firebird": "~0.7.2", + "pg": "~4.4.3", + "require-reload": "~0.2.2" }, "devDependencies": { - "chai": "", + "chai": "~3.4.1", + "chai-as-promised": "^5.2.0", "documentation": "", - "eslint": "", - "glob": "^6.0.1", - "gulp": "", - "gulp-documentation": "^2.1.0", - "gulp-eslint": "", + "eslint": "~1.10.3", + "glob": "~6.0.4", + "gulp": "~3.9.0", + "gulp-documentation": "~2.1.0", + "gulp-eslint": "~1.1.1", "gulp-istanbul": "^0.10.3", "gulp-jscs": "^3.0.2", "gulp-mocha": "^2.2.0", "gulp-pipe": "^1.0.4", - "gulp-sloc": "", - "istanbul": "", + "gulp-sloc": "~1.0.4", + "istanbul": "~0.4.2", "mocha": "" }, "license": "MIT", "scripts": { "test": "gulp test" } -} +} \ No newline at end of file diff --git a/test/adapters/adapterTestBase.js b/test/adapters/adapterTestBase.js deleted file mode 100644 index aad4aa4..0000000 --- a/test/adapters/adapterTestBase.js +++ /dev/null @@ -1,479 +0,0 @@ -'use strict'; - -module.exports.tests = { - 'Get tests': { - 'Get with function': { - select: ['id, COUNT(id) as count'], - from: ['create_test'], - groupBy: ['id'], - get: [], - }, - 'Basic select all get': { - get: ['create_test'], - }, - 'Basic select all with from': { - from: ['create_test'], - get: [], - }, - 'Get with limit': { - get: ['create_test', 2], - }, - 'Get with limit and offset': { - get: ['create_test', 2, 1], - }, - 'Get with having': { - select: ['id'], - from: ['create_test'], - groupBy: ['id'], - having: [ - 'multiple', - [{'id >': 1}], - ['id !=', 3], - ['id', 900], - ], - get: [], - }, - 'Get with orHaving': { - select: ['id'], - from: ['create_test'], - groupBy: ['id'], - having: [{'id >': 1}], - orHaving: ['id !=', 3], - get: [], - }, - }, - 'Select tests': { - 'Select where get': { - select: [['id', 'key as k', 'val']], - where: [ - 'multiple', - ['id >', 1], - ['id <', 900], - ], - get: ['create_test', 2, 1], - }, - 'Select where get 2': { - select: ['id, key as k, val'], - where: ['id !=', 1], - get: ['create_test', 2, 1], - }, - 'Multi Order By': { - from: ['create_test'], - orderBy: ['id, key'], - get: [], - }, - 'Select get': { - select: ['id, key as k, val'], - get: ['create_test', 2, 1], - }, - 'Select from get': { - select: ['id, key as k, val'], - from: ['create_test ct'], - where: ['id >', 1], - get: [], - }, - 'Select from limit get': { - select: ['id, key as k, val'], - from: ['create_test ct'], - where: ['id >', 1], - limit: [3], - get: [], - }, - 'Select where IS NOT NULL': { - select: ['id', 'key as k', 'val'], - from: ['create_test ct'], - whereIsNotNull: ['id'], - get: [], - }, - 'Select where IS NULL': { - select: ['id', 'key as k', 'val'], - from: ['create_test ct'], - whereIsNull: ['id'], - get: [], - }, - 'Select where OR IS NOT NULL': { - select: ['id', 'key as k', 'val'], - from: ['create_test ct'], - whereIsNull: ['id'], - orWhereIsNotNull: ['id'], - get: [], - }, - 'Select where OR IS NULL': { - select: ['id', 'key as k', 'val'], - from: ['create_test ct'], - where: ['id', 3], - orWhereIsNull: ['id'], - get: [], - }, - 'Select with string where value': { - select: ['id', 'key as k', 'val'], - from: ['create_test ct'], - where: ['id > 3'], - get: [], - }, - 'Select with function and argument in WHERE clause': { - select: ['id'], - from: ['create_test ct'], - where: ['id', 'CEILING(SQRT(88))'], - get: [], - }, - }, - 'Where in tests': { - 'Where in': { - from: ['create_test'], - whereIn: ['id', [0, 6, 56, 563, 341]], - get: [], - }, - 'Or Where in': { - from: ['create_test'], - where: ['key', 'false'], - orWhereIn: ['id', [0, 6, 56, 563, 341]], - get: [], - }, - 'Where Not in': { - from: ['create_test'], - where: ['key', 'false'], - whereNotIn: ['id', [0, 6, 56, 563, 341]], - get: [], - }, - 'Or Where Not in': { - from: ['create_test'], - where: ['key', 'false'], - orWhereNotIn: ['id', [0, 6, 56, 563, 341]], - get: [], - }, - }, - 'Query modifier tests': { - 'Order By': { - select: ['id, key as k, val'], - from: ['create_test'], - where: [ - 'multiple', - ['id >', 0], - ['id <', 9000], - ], - orderBy: [ - 'multiple', - ['id', 'DESC'], - ['k', 'ASC'], - ], - limit: [5, 2], - get: [], - }, - 'Group By': { - select: ['id, key as k, val'], - from: ['create_test'], - where: [ - 'multiple', - ['id >', 0], - ['id <', 9000], - ], - groupBy: [ - 'multiple', - ['k'], - [['id', 'val']], - ], - orderBy: [ - 'multiple', - ['id', 'DESC'], - ['k', 'ASC'], - ], - limit: [5, 2], - get: [], - }, - 'Or Where': { - select: ['id, key as k, val'], - from: ['create_test'], - where: [' id ', 1], - orWhere: ['key > ', 0], - limit: [2, 1], - get: [], - }, - Like: { - from: ['create_test'], - like: ['key', 'og'], - get: [], - }, - 'Or Like': { - from: ['create_test'], - like: ['key', 'og'], - orLike: ['key', 'val'], - get: [], - }, - 'Not Like': { - from: ['create_test'], - like: ['key', 'og', 'before'], - notLike: ['key', 'val'], - get: [], - }, - 'Or Not Like': { - from: ['create_test'], - like: ['key', 'og', 'before'], - orNotLike: ['key', 'val'], - get: [], - }, - 'Like Before': { - from: ['create_test'], - like: ['key', 'og', 'before'], - get: [], - }, - 'Like After': { - from: ['create_test'], - like: ['key', 'og', 'after'], - get: [], - }, - 'Basic Join': { - from: ['create_test ct'], - join: ['create_join cj', 'cj.id=ct.id'], - get: [], - }, - 'Left Join': { - from: ['create_test ct'], - join: ['create_join cj', 'cj.id=ct.id', 'left'], - get: [], - }, - 'Inner Join': { - from: ['create_test ct'], - join: ['create_join cj', 'cj.id=ct.id', 'inner'], - get: [], - }, - 'Join with multiple where values': { - from: ['create_test ct'], - join: ['create_join cj', 'cj.id=ct.id', 'inner'], - where: [ - { - 'ct.id < ': 3, - 'ct.key ': 'foo', - }, - ], - get: [], - }, - }, -}; - -let expect = require('chai').expect, - helpers = require('../../lib/helpers'), - State = require('../../lib/State'); - -module.exports.runner = (tests, qb, callback) => { - Object.keys(tests).forEach(suiteName => { - suite(suiteName, () => { - let currentSuite = tests[suiteName]; - Object.keys(currentSuite).forEach(testDesc => { - test(testDesc, done => { - let methodObj = currentSuite[testDesc]; - let methodNames = Object.keys(methodObj); - let lastMethodIndex = methodNames[methodNames.length - 1]; - - methodObj[lastMethodIndex].push((err, rows) => { - callback(err, done); - }); - - methodNames.forEach(name => { - let args = methodObj[name], - method = qb[name]; - - if (args[0] === 'multiple') { - args.shift(); - args.forEach(argSet => { - method.apply(qb, argSet); - }); - - } else { - method.apply(qb, args); - } - }); - }); - }); - }); - }); - suite('DB update tests', () => { - setup(done => { - let sql = qb.driver.truncate('create_test'); - qb.adapter.execute(sql, (err, res) => { - done(); - }); - }); - test('Test Insert', done => { - qb.set('id', 98) - .set('key', '84') - .set('val', new Buffer('120')) - .insert('create_test', (err, rows) => { - return callback(err, done); - }); - }); - test('Test Insert Object', done => { - qb.insert('create_test', { - id: 587, - key: 1, - val: new Buffer('2'), - }, (err, rows) => { - return callback(err, done); - }); - }); - test('Test Update', done => { - qb.where('id', 7) - .update('create_test', { - id: 7, - key: 'gogle', - val: new Buffer('non-word'), - }, (err, rows) => { - return callback(err, done); - }); - }); - test('Test set Array Update', done => { - let object = { - id: 22, - key: 'gogle', - val: new Buffer('non-word'), - }; - - qb.set(object) - .where('id', 22) - .update('create_test', (err, rows) => { - return callback(err, done); - }); - }); - test('Test where set update', done => { - qb.where('id', 36) - .set('id', 36) - .set('key', 'gogle') - .set('val', new Buffer('non-word')) - .update('create_test', (err, rows) => { - return callback(err, done); - }); - }); - test('Test delete', done => { - qb.delete('create_test', {id: 5}, (err, rows) => { - return callback(err, done); - }); - }); - test('Delete with where', done => { - qb.where('id', 5) - .delete('create_test', (err, rows) => { - return callback(err, done); - }); - }); - test('Delete multiple where values', done => { - qb.delete('create_test', { - id: 5, - key: 'gogle', - }, (err, rows) => { - return callback(err, done); - }); - }); - }); - suite('Grouping tests', () => { - test('Using grouping method', done => { - qb.select('id, key as k, val') - .from('create_test') - .groupStart() - .where('id >', 1) - .where('id <', 900) - .groupEnd() - .limit(2, 1) - .get((err, rows) => { - return callback(err, done); - }); - }); - test('Using where first grouping', done => { - qb.select('id, key as k, val') - .from('create_test') - .where('id !=', 5) - .groupStart() - .where('id >', 1) - .where('id <', 900) - .groupEnd() - .limit(2, 1) - .get((err, rows) => { - return callback(err, done); - }); - }); - test('Using or grouping method', done => { - qb.select('id, key as k, val') - .from('create_test') - .groupStart() - .where('id >', 1) - .where('id <', 900) - .groupEnd() - .orGroupStart() - .where('id', 0) - .groupEnd() - .limit(2, 1) - .get((err, rows) => { - return callback(err, done); - }); - }); - test('Using or not grouping method', done => { - qb.select('id, key as k, val') - .from('create_test') - .groupStart() - .where('id >', 1) - .where('id <', 900) - .groupEnd() - .orNotGroupStart() - .where('id', 0) - .groupEnd() - .limit(2, 1) - .get((err, rows) => { - return callback(err, done); - }); - }); - }); - suite('Get compiled tests', () => { - test('select', () => { - let sql = qb.select('id') - .from('create_test') - .getCompiledSelect(true); - - expect(helpers.isString(sql)).to.be.true; - }); - test('select from', () => { - let sql = qb.select('id') - .getCompiledSelect('create_test', true); - - expect(helpers.isString(sql)).to.be.true; - }); - test('insert', () => { - let sql = qb.set('id', 3) - .getCompiledInsert('create_test'); - - expect(helpers.isString(sql)).to.be.true; - }); - test('update', () => { - let sql = qb.set('id', 3) - .where('id', 5) - .getCompiledUpdate('create_test'); - - expect(helpers.isString(sql)).to.be.true; - }); - test('delete', () => { - let sql = qb.where('id', 5) - .getCompiledDelete('create_test'); - - expect(helpers.isString(sql)).to.be.true; - }); - }); - suite('Misc tests', () => { - test('Get State', () => { - qb.select('foo') - .from('bar') - .where('baz', 'foobar'); - - let state = new State(); - - expect(JSON.stringify(state)).to.not.be.deep.equal(qb.getState()); - }); - test('Reset State', () => { - qb.select('foo') - .from('bar') - .where('baz', 'foobar'); - - qb.resetQuery(); - - let state = new State(); - - expect(qb.getState()).to.be.deep.equal(state); - }); - }); -}; \ No newline at end of file diff --git a/test/adapters/dblite_test.js b/test/adapters/dblite_test.js index 1d56bd1..d96a387 100644 --- a/test/adapters/dblite_test.js +++ b/test/adapters/dblite_test.js @@ -1,11 +1,14 @@ 'use strict'; // Load the test base -let reload = require('require-reload')(require); -let getArgs = require('getargs'); -let expect = require('chai').expect; -let tests = reload('./adapterTestBase').tests; -let testRunner = reload('./adapterTestBase').runner; +const reload = require('require-reload')(require); +reload.emptyCache(); +const testBase = reload('../base'); +const expect = testBase.expect, + promiseTestRunner = testBase.promiseTestRunner, + testRunner = testBase.testRunner; + +let tests = reload('../base/tests'); // Load the test config file let adapterName = 'dblite'; @@ -25,53 +28,73 @@ if (connection) { let nodeQuery = require('../../lib/NodeQuery'); let qb = nodeQuery.init('sqlite', connection, adapterName); - // Add a test for this adapter - tests['Select tests']['Select with function and argument in WHERE clause'] = { - select: ['id'], - from: ['create_test'], - where: ['id', 'ABS(-88)'], - get: [], - }; - - suite('Dblite adapter tests', () => { - suiteSetup(() => { + suite('Dblite adapter tests -', () => { + suiteSetup(done => { // Set up the sqlite database let sql = 'CREATE TABLE IF NOT EXISTS "create_test" ("id" INTEGER PRIMARY KEY, "key" TEXT, "val" TEXT);' + 'CREATE TABLE IF NOT EXISTS "create_join" ("id" INTEGER PRIMARY KEY, "key" TEXT, "val" TEXT);'; - connection.query(sql); + connection.query(sql, () => { + return done(); + }); }); - testRunner(tests, qb, (err, done) => { + test('nodeQuery.getQuery = nodeQuery.init', () => { + expect(nodeQuery.getQuery()) + .to.be.deep.equal(qb); + }); + + /*--------------------------------------------------------------------------- + Callback Tests + ---------------------------------------------------------------------------*/ + + testRunner(qb, (err, done) => { expect(err).is.not.ok; done(); }); - suite('Adapter-specific tests', () => { - test('nodeQuery.getQuery = nodeQuery.init', () => { - expect(nodeQuery.getQuery()) - .to.be.deep.equal(qb); - }); - test('Test Insert Batch', done => { - let data = [ - { - id: 544, - key: 3, - val: new Buffer('7'), - }, { - id: 89, - key: 34, - val: new Buffer('10 o\'clock'), - }, { - id: 48, - key: 403, - val: new Buffer('97'), - }, - ]; - - qb.insertBatch('create_test', data, (err, rows) => { + test('Callback - Select with function and argument in WHERE clause', done => { + qb.select('id') + .from('create_test') + .where('id', 'ABS(-88)') + .get((err, rows) => { expect(err).is.not.ok; return done(); }); + }); + test('Callback - Test Insert Batch', done => { + let data = [ + { + id: 544, + key: 3, + val: new Buffer('7'), + }, { + id: 89, + key: 34, + val: new Buffer('10 o\'clock'), + }, { + id: 48, + key: 403, + val: new Buffer('97'), + }, + ]; + + qb.insertBatch('create_test', data, (err, rows) => { + expect(err).is.not.ok; + return done(); }); }); + + /*--------------------------------------------------------------------------- + Promise Tests + ---------------------------------------------------------------------------*/ + promiseTestRunner(qb); + test('Promise - Select with function and argument in WHERE clause', () => { + let promise = qb.select('id') + .from('create_test') + .where('id', 'ABS(-88)') + .get(); + + expect(promise).to.be.fulfilled; + }); + suiteTeardown(() => { qb.end(); }); diff --git a/test/adapters/mysql-base.js b/test/adapters/mysql-base.js new file mode 100644 index 0000000..3f9bd06 --- /dev/null +++ b/test/adapters/mysql-base.js @@ -0,0 +1,64 @@ +'use strict'; + +module.exports = function mysqlBase(qb, nodeQuery, expect, testRunner, promiseTestRunner) { + test('nodeQuery.getQuery = nodeQuery.init', () => { + expect(nodeQuery.getQuery()) + .to.be.deep.equal(qb); + }); + + /*--------------------------------------------------------------------------- + Callback Tests + ---------------------------------------------------------------------------*/ + testRunner(qb, (err, done) => { + expect(err).is.not.ok; + done(); + }); + test('Callback - Select with function and argument in WHERE clause', done => { + qb.select('id') + .from('create_test') + .where('id', 'CEILING(SQRT(88))') + .get((err, rows) => { + expect(err).is.not.ok; + return done(); + }); + }); + test('Test Insert Batch', done => { + let data = [ + { + id: 544, + key: 3, + val: new Buffer('7'), + }, { + id: 89, + key: 34, + val: new Buffer('10 o\'clock'), + }, { + id: 48, + key: 403, + val: new Buffer('97'), + }, + ]; + + qb.insertBatch('create_test', data, (err, rows) => { + expect(err).is.not.ok; + return done(); + }); + }); + + /*--------------------------------------------------------------------------- + Promise Tests + ---------------------------------------------------------------------------*/ + promiseTestRunner(qb); + test('Promise - Select with function and argument in WHERE clause', () => { + let promise = qb.select('id') + .from('create_test') + .where('id', 'CEILING(SQRT(88))') + .get(); + + expect(promise).to.be.fulfilled; + }); + + suiteTeardown(() => { + qb.end(); + }); +}; \ No newline at end of file diff --git a/test/adapters/mysql2_test.js b/test/adapters/mysql2_test.js index 965f3c0..8314700 100644 --- a/test/adapters/mysql2_test.js +++ b/test/adapters/mysql2_test.js @@ -1,14 +1,16 @@ 'use strict'; -let configFile = (process.env.CI) ? '../config-travis.json' : '../config.json'; +const configFile = (process.env.CI) ? '../config-travis.json' : '../config.json'; // Load the test base -let reload = require('require-reload')(require); +const reload = require('require-reload')(require); reload.emptyCache(); +const testBase = reload('../base'); +const expect = testBase.expect, + promiseTestRunner = testBase.promiseTestRunner, + testRunner = testBase.testRunner; + let getArgs = reload('getargs'); -let expect = reload('chai').expect; -let tests = reload('./adapterTestBase').tests; -let testRunner = reload('./adapterTestBase').runner; // Load the test config file let adapterName = 'mysql2'; @@ -22,40 +24,6 @@ let connection = mysql2.createConnection(config.conn); let nodeQuery = reload('../../lib/NodeQuery'); let qb = nodeQuery.init('mysql', connection, adapterName); -suite('Mysql2 adapter tests', () => { - testRunner(tests, qb, (err, done) => { - expect(err).is.not.ok; - done(); - }); - suite('Adapter-specific tests', () => { - test('nodeQuery.getQuery = nodeQuery.init', () => { - expect(nodeQuery.getQuery()) - .to.be.deep.equal(qb); - }); - test('Test Insert Batch', done => { - let data = [ - { - id: 544, - key: 3, - val: new Buffer('7'), - }, { - id: 89, - key: 34, - val: new Buffer('10 o\'clock'), - }, { - id: 48, - key: 403, - val: new Buffer('97'), - }, - ]; - - qb.insertBatch('create_test', data, (err, rows) => { - expect(err).is.not.ok; - return done(); - }); - }); - }); - suiteTeardown(() => { - qb.end(); - }); +suite('Mysql2 adapter tests -', () => { + require('./mysql-base')(qb, nodeQuery, expect, testRunner, promiseTestRunner); }); \ No newline at end of file diff --git a/test/adapters/mysql_test.js b/test/adapters/mysql_test.js index adb2811..05d047c 100644 --- a/test/adapters/mysql_test.js +++ b/test/adapters/mysql_test.js @@ -1,14 +1,16 @@ 'use strict'; -let configFile = (process.env.CI) ? '../config-travis.json' : '../config.json'; +const configFile = (process.env.CI) ? '../config-travis.json' : '../config.json'; // Load the test base -let reload = require('require-reload')(require); +const reload = require('require-reload')(require); reload.emptyCache(); +const testBase = reload('../base'); +const expect = testBase.expect, + promiseTestRunner = testBase.promiseTestRunner, + testRunner = testBase.testRunner; + let getArgs = reload('getargs'); -let expect = reload('chai').expect; -let tests = reload('./adapterTestBase').tests; -let testRunner = reload('./adapterTestBase').runner; // Load the test config file let adapterName = 'mysql'; @@ -22,40 +24,6 @@ let connection = mysql.createConnection(config.conn); let nodeQuery = reload('../../lib/NodeQuery'); let qb = nodeQuery.init('mysql', connection); -suite('Mysql adapter tests', () => { - testRunner(tests, qb, (err, done) => { - expect(err).is.not.ok; - done(); - }); - suite('Adapter-specific tests', () => { - test('nodeQuery.getQuery = nodeQuery.init', () => { - expect(nodeQuery.getQuery()) - .to.be.deep.equal(qb); - }); - test('Test Insert Batch', done => { - let data = [ - { - id: 544, - key: 3, - val: new Buffer('7'), - }, { - id: 89, - key: 34, - val: new Buffer('10 o\'clock'), - }, { - id: 48, - key: 403, - val: new Buffer('97'), - }, - ]; - - qb.insertBatch('create_test', data, (err, rows) => { - expect(err).is.not.ok; - return done(); - }); - }); - }); - suiteTeardown(() => { - qb.end(); - }); +suite('Mysql adapter tests -', () => { + require('./mysql-base')(qb, nodeQuery, expect, testRunner, promiseTestRunner); }); \ No newline at end of file diff --git a/test/adapters/node-firebird.js b/test/adapters/node-firebird.js new file mode 100644 index 0000000..8eda1e6 --- /dev/null +++ b/test/adapters/node-firebird.js @@ -0,0 +1,51 @@ +'use strict'; + +// Load the test base +const path = require('path'); +const reload = require('require-reload')(require); +const testBase = reload('../base'); +const expect = reload('chai').expect; +const testRunner = testBase.testRunner; +const promiseTestRunner = testBase.promiseTestRunner; + +// Load the test config file +let adapterName = 'node-firebird'; +let Firebird = reload(adapterName); +const config = reload('../config.json')[adapterName]; +config.conn.database = path.join(__dirname, config.conn.database); +let nodeQuery = reload('../../lib/NodeQuery'); + +let qb = null; + +// Skip on TravisCi +if (process.env.CI || process.env.JENKINS_HOME) { + return; +} + +suite('Firebird adapter tests -', () => { + // Set up the query builder object + suiteSetup('Database connection', connected => { + Firebird.attach(config.conn, (err, db) => { + qb = nodeQuery.init('firebird', db, adapterName); + return connected(err); + }); + }); + testRunner(qb, (err, done) => { + expect(err).is.not.ok; + done(); + }); + suite('Adapter-specific tests', () => { + test('nodeQuery.getQuery = nodeQuery.init', () => { + expect(nodeQuery.getQuery()) + .to.be.deep.equal(qb); + }); + test('insertBatch throws error', () => { + expect(() => { + qb.driver.insertBatch('create_test', []); + }).to.throw(Error, "Not Implemented"); + }); + }); + suiteTeardown(() => { + qb.end(); + }); +}); \ No newline at end of file diff --git a/test/adapters/node-firebird_test.js b/test/adapters/node-firebird_test.js deleted file mode 100644 index 983d864..0000000 --- a/test/adapters/node-firebird_test.js +++ /dev/null @@ -1,46 +0,0 @@ -'use strict'; - -// Load the test base -let reload = require('require-reload')(require); -let expect = reload('chai').expect; -let tests = reload('./adapterTestBase').tests; -let testRunner = reload('./adapterTestBase').runner; - -// Load the test config file -let adapterName = 'node-firebird'; -let Firebird = reload(adapterName); -let config = reload('../config.json')[adapterName]; -config.conn.database = __dirname + config.conn.database; -let nodeQuery = reload('../../lib/NodeQuery'); - -// Skip on TravisCi -if (process.env.CI || process.env.JENKINS_HOME) -{ - return; -} - -suite('Firebird adapter tests', () => { - Firebird.attach(config.conn, (err, db) => { - // Set up the query builder object - let qb = nodeQuery.init('firebird', db, adapterName); - - testRunner(tests, qb, (err, done) => { - expect(err).is.not.ok; - done(); - }); - suite('Adapter-specific tests', () => { - test('nodeQuery.getQuery = nodeQuery.init', () => { - expect(nodeQuery.getQuery()) - .to.be.deep.equal(qb); - }); - test('insertBatch throws error', () => { - expect(() => { - qb.driver.insertBatch('create_test', []); - }).to.throw(Error, "Not Implemented"); - }); - }); - suiteTeardown(() => { - db.detach(); - }); - }); -}); \ No newline at end of file diff --git a/test/adapters/pg_test.js b/test/adapters/pg_test.js index 4d53d1b..c515871 100644 --- a/test/adapters/pg_test.js +++ b/test/adapters/pg_test.js @@ -3,10 +3,12 @@ let configFile = (process.env.CI) ? '../config-travis.json' : '../config.json'; // Load the test base -let reload = require('require-reload')(require); -let expect = reload('chai').expect; -let tests = reload('./adapterTestBase').tests; -let testRunner = reload('./adapterTestBase').runner; +const reload = require('require-reload')(require); +reload.emptyCache(); +const testBase = reload('../base'); +const expect = testBase.expect, + promiseTestRunner = testBase.promiseTestRunner, + testRunner = testBase.testRunner; // Load the test config file let adapterName = 'pg'; @@ -25,40 +27,59 @@ connection.connect(err => { let nodeQuery = reload('../../lib/NodeQuery'); let qb = nodeQuery.init('pg', connection); -suite('Pg adapter tests', () => { - testRunner(tests, qb, (err, done) => { +suite('Pg adapter tests -', () => { + test('nodeQuery.getQuery = nodeQuery.init', () => { + expect(nodeQuery.getQuery()) + .to.be.deep.equal(qb); + }); + + /*--------------------------------------------------------------------------- + Callback Tests + ---------------------------------------------------------------------------*/ + testRunner(qb, (err, done) => { expect(err).is.not.ok; done(); }); - suite('Adapter-specific tests', () => { - test('nodeQuery.getQuery = nodeQuery.init', () => { - expect(nodeQuery.getQuery()) - .to.be.deep.equal(qb); - }); - test('Test Insert Batch', done => { - let data = [ - { - id: 544, - key: 3, - val: new Buffer('7'), - }, { - id: 89, - key: 34, - val: new Buffer('10 o\'clock'), - }, { - id: 48, - key: 403, - val: new Buffer('97'), - }, - ]; - - qb.insertBatch('create_test', data, (err, rows) => { + test('Callback - Select with function and argument in WHERE clause', done => { + qb.select('id') + .from('create_test') + .where('id', 'CEILING(SQRT(88))') + .get((err, rows) => { expect(err).is.not.ok; return done(); }); - }); }); - suiteTeardown(() => { - qb.end(); + + /*--------------------------------------------------------------------------- + Promise Tests + ---------------------------------------------------------------------------*/ + promiseTestRunner(qb); + test('Promise - Select with function and argument in WHERE clause', () => { + let promise = qb.select('id') + .from('create_test') + .where('id', 'CEILING(SQRT(88))') + .get(); + + expect(promise).to.be.fulfilled; + }); + test('Promise - Test Insert Batch', () => { + let data = [ + { + id: 544, + key: 3, + val: new Buffer('7'), + }, { + id: 89, + key: 34, + val: new Buffer('10 o\'clock'), + }, { + id: 48, + key: 403, + val: new Buffer('97'), + }, + ]; + + let promise = qb.insertBatch('create_test', data); + return expect(promise).to.be.fulfilled; }); }); \ No newline at end of file diff --git a/test/base.js b/test/base.js new file mode 100644 index 0000000..af0c5bb --- /dev/null +++ b/test/base.js @@ -0,0 +1,13 @@ +'use strict'; + +const chai = require('chai'); +const chaiAsPromised = require('chai-as-promised'); +chai.use(chaiAsPromised); +const expect = chai.expect; + +module.exports = { + expect: expect, + tests: require('./base/tests'), + testRunner: require('./base/adapterCallbackTestRunner'), + promiseTestRunner: require('./base/adapterPromiseTestRunner'), +}; \ No newline at end of file diff --git a/test/base/adapterCallbackTestRunner.js b/test/base/adapterCallbackTestRunner.js new file mode 100644 index 0000000..ce874fa --- /dev/null +++ b/test/base/adapterCallbackTestRunner.js @@ -0,0 +1,236 @@ +'use strict'; + +// Load the test base +const chai = require('chai'); +const chaiAsPromised = require('chai-as-promised'); +chai.use(chaiAsPromised); +const expect = chai.expect; + +const reload = require('require-reload')(require); +let tests = reload('../base/tests'); + +let helpers = reload('../../lib/helpers'), + State = reload('../../lib/State'); + +module.exports = function testRunner(qb, callback) { + Object.keys(tests).forEach(suiteName => { + suite(suiteName, () => { + let currentSuite = tests[suiteName]; + Object.keys(currentSuite).forEach(testDesc => { + test(`Callback - ${testDesc}`, done => { + let methodObj = currentSuite[testDesc]; + let methodNames = Object.keys(methodObj); + let lastMethodIndex = methodNames[methodNames.length - 1]; + + methodObj[lastMethodIndex].push((err, rows) => { + callback(err, done); + }); + + methodNames.forEach(name => { + let args = methodObj[name], + method = qb[name]; + + if (args[0] === 'multiple') { + args.shift(); + args.forEach(argSet => { + method.apply(qb, argSet); + }); + + } else { + method.apply(qb, args); + } + }); + }); + }); + }); + }); + suite('DB update tests -', () => { + setup(done => { + let sql = qb.driver.truncate('create_test'); + qb.adapter.execute(sql, (err, res) => { + done(); + }); + }); + test('Callback - Test Insert', done => { + qb.set('id', 98) + .set('key', '84') + .set('val', new Buffer('120')) + .insert('create_test', (err, rows) => { + return callback(err, done); + }); + }); + test('Callback - Test Insert Object', done => { + qb.insert('create_test', { + id: 587, + key: 1, + val: new Buffer('2'), + }, (err, rows) => { + return callback(err, done); + }); + }); + test('Callback - Test Update', done => { + qb.where('id', 7) + .update('create_test', { + id: 7, + key: 'gogle', + val: new Buffer('non-word'), + }, (err, rows) => { + return callback(err, done); + }); + }); + test('Callback - Test set Array Update', done => { + let object = { + id: 22, + key: 'gogle', + val: new Buffer('non-word'), + }; + + qb.set(object) + .where('id', 22) + .update('create_test', (err, rows) => { + return callback(err, done); + }); + }); + test('Callback - Test where set update', done => { + qb.where('id', 36) + .set('id', 36) + .set('key', 'gogle') + .set('val', new Buffer('non-word')) + .update('create_test', (err, rows) => { + return callback(err, done); + }); + }); + test('Callback - Test delete', done => { + qb.delete('create_test', {id: 5}, (err, rows) => { + return callback(err, done); + }); + }); + test('Callback - Delete with where', done => { + qb.where('id', 5) + .delete('create_test', (err, rows) => { + return callback(err, done); + }); + }); + test('Callback - Delete multiple where values', done => { + qb.delete('create_test', { + id: 5, + key: 'gogle', + }, (err, rows) => { + return callback(err, done); + }); + }); + }); + suite('Grouping tests -', () => { + test('Callback - Using grouping method', done => { + qb.select('id, key as k, val') + .from('create_test') + .groupStart() + .where('id >', 1) + .where('id <', 900) + .groupEnd() + .limit(2, 1) + .get((err, rows) => { + return callback(err, done); + }); + }); + test('Callback - Using where first grouping', done => { + qb.select('id, key as k, val') + .from('create_test') + .where('id !=', 5) + .groupStart() + .where('id >', 1) + .where('id <', 900) + .groupEnd() + .limit(2, 1) + .get((err, rows) => { + return callback(err, done); + }); + }); + test('Callback - Using or grouping method', done => { + qb.select('id, key as k, val') + .from('create_test') + .groupStart() + .where('id >', 1) + .where('id <', 900) + .groupEnd() + .orGroupStart() + .where('id', 0) + .groupEnd() + .limit(2, 1) + .get((err, rows) => { + return callback(err, done); + }); + }); + test('Callback - Using or not grouping method', done => { + qb.select('id, key as k, val') + .from('create_test') + .groupStart() + .where('id >', 1) + .where('id <', 900) + .groupEnd() + .orNotGroupStart() + .where('id', 0) + .groupEnd() + .limit(2, 1) + .get((err, rows) => { + return callback(err, done); + }); + }); + }); + suite('Get compiled tests -', () => { + test('select', () => { + let sql = qb.select('id') + .from('create_test') + .getCompiledSelect(true); + + expect(helpers.isString(sql)).to.be.true; + }); + test('select from', () => { + let sql = qb.select('id') + .getCompiledSelect('create_test', true); + + expect(helpers.isString(sql)).to.be.true; + }); + test('insert', () => { + let sql = qb.set('id', 3) + .getCompiledInsert('create_test'); + + expect(helpers.isString(sql)).to.be.true; + }); + test('update', () => { + let sql = qb.set('id', 3) + .where('id', 5) + .getCompiledUpdate('create_test'); + + expect(helpers.isString(sql)).to.be.true; + }); + test('delete', () => { + let sql = qb.where('id', 5) + .getCompiledDelete('create_test'); + + expect(helpers.isString(sql)).to.be.true; + }); + }); + suite('Misc tests -', () => { + test('Get State', () => { + qb.select('foo') + .from('bar') + .where('baz', 'foobar'); + + let state = new State(); + + expect(JSON.stringify(state)).to.not.be.deep.equal(qb.getState()); + }); + test('Reset State', () => { + qb.select('foo') + .from('bar') + .where('baz', 'foobar'); + + qb.resetQuery(); + + let state = new State(); + + expect(qb.getState()).to.be.deep.equal(state); + }); + }); +}; \ No newline at end of file diff --git a/test/base/adapterPromiseTestRunner.js b/test/base/adapterPromiseTestRunner.js new file mode 100644 index 0000000..0dbf97d --- /dev/null +++ b/test/base/adapterPromiseTestRunner.js @@ -0,0 +1,181 @@ +'use strict'; + +// Load the test base +const chai = require('chai'); +const chaiAsPromised = require('chai-as-promised'); +chai.use(chaiAsPromised); +const expect = chai.expect; + +const reload = require('require-reload')(require); +let tests = reload('../base/tests'); + +let helpers = reload('../../lib/helpers'), + State = reload('../../lib/State'); + +module.exports = function promiseTestRunner(qb) { + Object.keys(tests).forEach(suiteName => { + suite(suiteName, () => { + let currentSuite = tests[suiteName]; + Object.keys(currentSuite).forEach(testDesc => { + test(`Promise - ${testDesc}`, () => { + let methodObj = currentSuite[testDesc]; + let methodNames = Object.keys(methodObj); + let lastMethodIndex = methodNames[methodNames.length - 1]; + let results = []; + + methodNames.forEach(name => { + let args = methodObj[name], + method = qb[name]; + + if (args[0] === 'multiple') { + args.shift(); + args.forEach(argSet => { + results.push(method.apply(qb, argSet)); + }); + + } else { + results.push(method.apply(qb, args)); + } + }); + + let promise = results.pop(); + expect(promise).to.be.fulfilled; + }); + }); + }); + }); + suite('DB update tests -', () => { + suiteSetup(done => { + let sql = qb.driver.truncate('create_test'); + qb.adapter.execute(sql).then(res => { + return done(); + }).catch(err => { + return done(err); + }); + }); + test('Promise - Test Insert', () => { + let promise = qb.set('id', 98) + .set('key', '84') + .set('val', new Buffer('120')) + .insert('create_test'); + + expect(promise).to.be.fulfilled; + }); + test('Promise - Test Insert Object', () => { + let promise = qb.insert('create_test', { + id: 587, + key: 1, + val: new Buffer('2'), + }); + + expect(promise).to.be.fulfilled; + }); + test('Promise - Test Update', () => { + let promise = qb.where('id', 7) + .update('create_test', { + id: 7, + key: 'gogle', + val: new Buffer('non-word'), + }); + + expect(promise).to.be.fulfilled; + }); + test('Promise - Test set Array Update', () => { + let object = { + id: 22, + key: 'gogle', + val: new Buffer('non-word'), + }; + + let promise = qb.set(object) + .where('id', 22) + .update('create_test'); + + expect(promise).to.be.fulfilled; + }); + test('Promise - Test where set update', () => { + let promise = qb.where('id', 36) + .set('id', 36) + .set('key', 'gogle') + .set('val', new Buffer('non-word')) + .update('create_test'); + + expect(promise).to.be.fulfilled; + }); + test('Promise - Test delete', () => { + let promise = qb.delete('create_test', {id: 5}); + expect(promise).to.be.fulfilled; + }); + test('Promise - Delete with where', () => { + let promise = qb.where('id', 5) + .delete('create_test'); + + expect(promise).to.be.fulfilled; + }); + test('Promise - Delete multiple where values', () => { + let promise = qb.delete('create_test', { + id: 5, + key: 'gogle', + }); + + expect(promise).to.be.fulfilled; + }); + }); + suite('Grouping tests -', () => { + test('Promise - Using grouping method', () => { + let promise = qb.select('id, key as k, val') + .from('create_test') + .groupStart() + .where('id >', 1) + .where('id <', 900) + .groupEnd() + .limit(2, 1) + .get(); + + expect(promise).to.be.fulfilled; + }); + test('Promise - Using where first grouping', () => { + let promise = qb.select('id, key as k, val') + .from('create_test') + .where('id !=', 5) + .groupStart() + .where('id >', 1) + .where('id <', 900) + .groupEnd() + .limit(2, 1) + .get(); + + expect(promise).to.be.fulfilled; + }); + test('Promise - Using or grouping method', () => { + let promise = qb.select('id, key as k, val') + .from('create_test') + .groupStart() + .where('id >', 1) + .where('id <', 900) + .groupEnd() + .orGroupStart() + .where('id', 0) + .groupEnd() + .limit(2, 1) + .get(); + + expect(promise).to.be.fulfilled; + }); + test('Promise - Using or not grouping method', () => { + let promise = qb.select('id, key as k, val') + .from('create_test') + .groupStart() + .where('id >', 1) + .where('id <', 900) + .groupEnd() + .orNotGroupStart() + .where('id', 0) + .groupEnd() + .limit(2, 1) + .get(); + + expect(promise).to.be.fulfilled; + }); + }); +}; \ No newline at end of file diff --git a/test/base/tests.js b/test/base/tests.js new file mode 100644 index 0000000..6c43bf1 --- /dev/null +++ b/test/base/tests.js @@ -0,0 +1,246 @@ +'use strict'; + +module.exports = { + 'Get tests -': { + 'Get with function': { + select: ['id, COUNT(id) as count'], + from: ['create_test'], + groupBy: ['id'], + get: [], + }, + 'Basic select all get': { + get: ['create_test'], + }, + 'Basic select all with from': { + from: ['create_test'], + get: [], + }, + 'Get with limit': { + get: ['create_test', 2], + }, + 'Get with limit and offset': { + get: ['create_test', 2, 1], + }, + 'Get with having': { + select: ['id'], + from: ['create_test'], + groupBy: ['id'], + having: [ + 'multiple', + [{'id >': 1}], + ['id !=', 3], + ['id', 900], + ], + get: [], + }, + 'Get with orHaving': { + select: ['id'], + from: ['create_test'], + groupBy: ['id'], + having: [{'id >': 1}], + orHaving: ['id !=', 3], + get: [], + }, + }, + 'Select tests -': { + 'Select where get': { + select: [['id', 'key as k', 'val']], + where: [ + 'multiple', + ['id >', 1], + ['id <', 900], + ], + get: ['create_test', 2, 1], + }, + 'Select where get 2': { + select: ['id, key as k, val'], + where: ['id !=', 1], + get: ['create_test', 2, 1], + }, + 'Multi Order By': { + from: ['create_test'], + orderBy: ['id, key'], + get: [], + }, + 'Select get': { + select: ['id, key as k, val'], + get: ['create_test', 2, 1], + }, + 'Select from get': { + select: ['id, key as k, val'], + from: ['create_test ct'], + where: ['id >', 1], + get: [], + }, + 'Select from limit get': { + select: ['id, key as k, val'], + from: ['create_test ct'], + where: ['id >', 1], + limit: [3], + get: [], + }, + 'Select where IS NOT NULL': { + select: ['id', 'key as k', 'val'], + from: ['create_test ct'], + whereIsNotNull: ['id'], + get: [], + }, + 'Select where IS NULL': { + select: ['id', 'key as k', 'val'], + from: ['create_test ct'], + whereIsNull: ['id'], + get: [], + }, + 'Select where OR IS NOT NULL': { + select: ['id', 'key as k', 'val'], + from: ['create_test ct'], + whereIsNull: ['id'], + orWhereIsNotNull: ['id'], + get: [], + }, + 'Select where OR IS NULL': { + select: ['id', 'key as k', 'val'], + from: ['create_test ct'], + where: ['id', 3], + orWhereIsNull: ['id'], + get: [], + }, + 'Select with string where value': { + select: ['id', 'key as k', 'val'], + from: ['create_test ct'], + where: ['id > 3'], + get: [], + }, + }, + 'Where in tests -': { + 'Where in': { + from: ['create_test'], + whereIn: ['id', [0, 6, 56, 563, 341]], + get: [], + }, + 'Or Where in': { + from: ['create_test'], + where: ['key', 'false'], + orWhereIn: ['id', [0, 6, 56, 563, 341]], + get: [], + }, + 'Where Not in': { + from: ['create_test'], + where: ['key', 'false'], + whereNotIn: ['id', [0, 6, 56, 563, 341]], + get: [], + }, + 'Or Where Not in': { + from: ['create_test'], + where: ['key', 'false'], + orWhereNotIn: ['id', [0, 6, 56, 563, 341]], + get: [], + }, + }, + 'Query modifier tests -': { + 'Order By': { + select: ['id, key as k, val'], + from: ['create_test'], + where: [ + 'multiple', + ['id >', 0], + ['id <', 9000], + ], + orderBy: [ + 'multiple', + ['id', 'DESC'], + ['k', 'ASC'], + ], + limit: [5, 2], + get: [], + }, + 'Group By': { + select: ['id, key as k, val'], + from: ['create_test'], + where: [ + 'multiple', + ['id >', 0], + ['id <', 9000], + ], + groupBy: [ + 'multiple', + ['k'], + [['id', 'val']], + ], + orderBy: [ + 'multiple', + ['id', 'DESC'], + ['k', 'ASC'], + ], + limit: [5, 2], + get: [], + }, + 'Or Where': { + select: ['id, key as k, val'], + from: ['create_test'], + where: [' id ', 1], + orWhere: ['key > ', 0], + limit: [2, 1], + get: [], + }, + Like: { + from: ['create_test'], + like: ['key', 'og'], + get: [], + }, + 'Or Like': { + from: ['create_test'], + like: ['key', 'og'], + orLike: ['key', 'val'], + get: [], + }, + 'Not Like': { + from: ['create_test'], + like: ['key', 'og', 'before'], + notLike: ['key', 'val'], + get: [], + }, + 'Or Not Like': { + from: ['create_test'], + like: ['key', 'og', 'before'], + orNotLike: ['key', 'val'], + get: [], + }, + 'Like Before': { + from: ['create_test'], + like: ['key', 'og', 'before'], + get: [], + }, + 'Like After': { + from: ['create_test'], + like: ['key', 'og', 'after'], + get: [], + }, + 'Basic Join': { + from: ['create_test ct'], + join: ['create_join cj', 'cj.id=ct.id'], + get: [], + }, + 'Left Join': { + from: ['create_test ct'], + join: ['create_join cj', 'cj.id=ct.id', 'left'], + get: [], + }, + 'Inner Join': { + from: ['create_test ct'], + join: ['create_join cj', 'cj.id=ct.id', 'inner'], + get: [], + }, + 'Join with multiple where values': { + from: ['create_test ct'], + join: ['create_join cj', 'cj.id=ct.id', 'inner'], + where: [ + { + 'ct.id < ': 3, + 'ct.key ': 'foo', + }, + ], + get: [], + }, + }, +}; \ No newline at end of file diff --git a/test/base_test.js b/test/base_test.js index fbec2c2..81a4ac1 100644 --- a/test/base_test.js +++ b/test/base_test.js @@ -6,7 +6,7 @@ let expect = require('chai').expect, nodeQuery = reload('../lib/NodeQuery'), Adapter = reload('../lib/Adapter'); -suite('Base tests', () => { +suite('Base tests -', () => { suite('Sanity check', () => { let files = glob.sync(`${__dirname}/../lib/**/*.js`); files.forEach(mod => { diff --git a/test/helpers_test.js b/test/helpers_test.js index 8f84b8f..ae60d5f 100644 --- a/test/helpers_test.js +++ b/test/helpers_test.js @@ -7,8 +7,8 @@ let chai = require('chai'), let helpers = require('../lib/helpers'); -suite('Helper Module Tests', () => { - suite('Type-checking methods', () => { +suite('Helper Module Tests -', () => { + suite('Type-checking methods -', () => { suite('Object wrappers are listed as their native type', () => { test('Boolean Wrapper returns \'boolean\' not \'object\'', () => { let item = Boolean(true); @@ -23,7 +23,7 @@ suite('Helper Module Tests', () => { expect(helpers.type(item)).to.deep.equal('string'); }); }); - suite('is..Method methods exist', () => { + suite('is..Method methods exist -', () => { let types = [ 'Null', 'Undefined', @@ -44,7 +44,7 @@ suite('Helper Module Tests', () => { }); }); }); - suite('isScalar', () => { + suite('isScalar -', () => { let trueCases = { 'Strings are scalar': 'foo', 'Booleans are scalar': true, @@ -66,7 +66,7 @@ suite('Helper Module Tests', () => { }); }); }); - suite('isInfinity', () => { + suite('isInfinity -', () => { test('The type of 1/0 is infinity', () => { expect(helpers.type(1 / 0)).to.equal('infinity'); }); @@ -74,7 +74,7 @@ suite('Helper Module Tests', () => { expect(helpers.isInfinite(1 / 0)).to.be.true; }); }); - suite('isNaN', () => { + suite('isNaN -', () => { test('The type of 0 / 0 is NaN', () => { expect(helpers.type(0 / 0)).to.equal('nan'); }); @@ -83,8 +83,8 @@ suite('Helper Module Tests', () => { }); }); }); - suite('Other helper methods', () => { - suite('stringTrim', () => { + suite('Other helper methods -', () => { + suite('stringTrim -', () => { test('stringTrim method works as expected', () => { let orig = [' x y ', 'z ', ' q']; let ret = ['x y', 'z', 'q']; @@ -92,7 +92,7 @@ suite('Helper Module Tests', () => { expect(orig.map(helpers.stringTrim)).to.be.deep.equal(ret); }); }); - suite('arrayPluck', () => { + suite('arrayPluck -', () => { let orig = [ { foo: 1, @@ -115,7 +115,7 @@ suite('Helper Module Tests', () => { expect(helpers.arrayPluck([], 'apple')).to.be.deep.equal([]); }); }); - suite('regexInArray', () => { + suite('regexInArray -', () => { let orig = ['apple', ' string ', 6, 4, 7]; let cases = [ diff --git a/test/mocha.opts b/test/mocha.opts index db5f715..31bf1aa 100644 --- a/test/mocha.opts +++ b/test/mocha.opts @@ -1,7 +1,9 @@ --ui tdd --bail --recursive ---reporter dot ---slow 100 ---timeout 5000 ---check-leaks \ No newline at end of file +--reporter list +--slow 200 +--timeout 10000 +--check-leaks +--growl +test/**/*_test.js \ No newline at end of file diff --git a/test/query-parser_test.js b/test/query-parser_test.js index e61f6d1..8475cab 100644 --- a/test/query-parser_test.js +++ b/test/query-parser_test.js @@ -1,172 +1,172 @@ -'use strict'; -let expect = require('chai').expect; - -// Use the base driver as a mock for testing -let getArgs = require('getargs'); -let helpers = require('../lib/helpers'); -let driver = require('../lib/Driver'); - -let P = require('../lib/QueryParser'); -let parser = new P(driver); - -let State = require('../lib/State'); - -// Simulate query builder state -let state = new State(); - -let mixedSet = function mixedSet(/* $letName, $valType, $key, [$val] */) { - let args = getArgs('$letName:string, $valType:string, $key:object|string|number, [$val]', 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]; - state[args.$letName].push(pushVal); - } else { - state[args.$letName][k] = obj[k]; - } - }); - - return state[args.$letName]; -}; - -let whereMock = function() { - let args = getArgs('key:string|object, [val]', arguments); - - state.whereMap = []; - state.whereValues = []; - - mixedSet('rawWhereValues', 'value', args.key, args.val); - mixedSet('whereMap', 'both', args.key, args.val); -}; - -// ----------------------------------------------------------------------------- -// ! Start Tests -// ----------------------------------------------------------------------------- - -suite('Query Parser Tests', () => { - suite('Has operator tests', () => { - test('Has operator', () => { - let matches = parser.hasOperator('foo <> 2'); - expect(matches).to.be.deep.equal(['<>']); - }); - test('Has no operator', () => { - let matches = parser.hasOperator('foo'); - expect(matches).to.be.null; - }); - }); - suite('Where parser tests', () => { - setup(() => { - state = new State(); - }); - test('Has function full string', () => { - whereMock('time < SUM(FOO(BAR()))'); - parser.parseWhere(driver, state); - expect(state.whereMap) - .to.be.deep.equal(['"time" < SUM(FOO(BAR()))']); - }); - test('Has function key/val', () => { - whereMock('time <', 'SUM(FOO(BAR()))'); - parser.parseWhere(driver, state); - expect(state.whereMap) - .to.be.deep.equal(['"time" < SUM(FOO(BAR()))']); - }); - test('Has function key/val object', () => { - whereMock({ - 'time <': 'SUM(FOO(BAR(\'x\')))', - }); - parser.parseWhere(driver, state); - expect(state.whereMap) - .to.be.deep.equal(['"time" < SUM(FOO(BAR(\'x\')))']); - }); - test('Has literal value', () => { - whereMock({ - foo: 3, - }); - parser.parseWhere(driver, state); - expect(state.whereMap) - .to.be.deep.equal(['"foo" = ?']); - expect(state.whereValues) - .to.be.deep.equal(['3']); - }); - test('Has multiple literal values', () => { - whereMock({ - foo: 3, - bar: 5, - }); - parser.parseWhere(driver, state); - expect(state.whereMap) - .to.be.deep.equal(['"foo" = ?', '"bar" = ?']); - expect(state.whereValues) - .to.be.deep.equal(['3', '5']); - }); - }); - suite('Parse join tests', () => { - let data = [ - { - desc: 'Simple equals condition', - join: 'table1.field1=table2.field2', - expected: ['table1.field1', '=', 'table2.field2'], - }, { - desc: 'Db.table.field condition', - join: 'db1.table1.field1!=db2.table2.field2', - expected: ['db1.table1.field1', '!=', 'db2.table2.field2'], - }, { - desc: 'Underscore in identifier', - join: 'table_1.field1 = tab_le2.field_2', - expected: ['table_1.field1', '=', 'tab_le2.field_2'], - }, { - desc: 'Function in condition', - join: 'table1.field1 > SUM(3+6)', - expected: ['table1.field1', '>', 'SUM(3+6)'], - }, - ]; - - data.forEach(datum => { - test(datum.desc, () => { - let matches = parser.parseJoin(datum.join); - expect(matches.combined).to.be.deep.equal(datum.expected); - }); - }); - }); - suite('Compile join tests', () => { - let data = [ - { - desc: 'Simple equals condition', - clause: 'table1.field1=table2.field2', - expected: '"table1"."field1" = "table2"."field2"', - }, { - desc: 'Db.table.field condition', - clause: 'db1.table1.field1!=db2.table2.field2', - expected: '"db1"."table1"."field1" != "db2"."table2"."field2"', - }, { - desc: 'Underscore in identifier', - clause: 'table_1.field1 = tab_le2.field_2', - expected: '"table_1"."field1" = "tab_le2"."field_2"', - }, { - desc: 'Function in condition', - clause: 'table1.field1 > SUM(3+6)', - expected: '"table1"."field1" > SUM(3+6)', - }, - ]; - - data.forEach(datum => { - test(datum.desc, () => { - let join = parser.compileJoin(datum.clause); - expect(join).to.be.deep.equal(datum.expected); - }); - }); - }); +'use strict'; +let expect = require('chai').expect; + +// Use the base driver as a mock for testing +let getArgs = require('getargs'); +let helpers = require('../lib/helpers'); +let driver = require('../lib/Driver'); + +let P = require('../lib/QueryParser'); +let parser = new P(driver); + +let State = require('../lib/State'); + +// Simulate query builder state +let state = new State(); + +let mixedSet = function mixedSet(/* $letName, $valType, $key, [$val] */) { + let args = getArgs('$letName:string, $valType:string, $key:object|string|number, [$val]', 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]; + state[args.$letName].push(pushVal); + } else { + state[args.$letName][k] = obj[k]; + } + }); + + return state[args.$letName]; +}; + +let whereMock = function() { + let args = getArgs('key:string|object, [val]', arguments); + + state.whereMap = []; + state.whereValues = []; + + mixedSet('rawWhereValues', 'value', args.key, args.val); + mixedSet('whereMap', 'both', args.key, args.val); +}; + +// ----------------------------------------------------------------------------- +// ! Start Tests +// ----------------------------------------------------------------------------- + +suite('Query Parser Tests', () => { + suite('Has operator tests', () => { + test('Has operator', () => { + let matches = parser.hasOperator('foo <> 2'); + expect(matches).to.be.deep.equal(['<>']); + }); + test('Has no operator', () => { + let matches = parser.hasOperator('foo'); + expect(matches).to.be.null; + }); + }); + suite('Where parser tests', () => { + setup(() => { + state = new State(); + }); + test('Has function full string', () => { + whereMock('time < SUM(FOO(BAR()))'); + parser.parseWhere(driver, state); + expect(state.whereMap) + .to.be.deep.equal(['"time" < SUM(FOO(BAR()))']); + }); + test('Has function key/val', () => { + whereMock('time <', 'SUM(FOO(BAR()))'); + parser.parseWhere(driver, state); + expect(state.whereMap) + .to.be.deep.equal(['"time" < SUM(FOO(BAR()))']); + }); + test('Has function key/val object', () => { + whereMock({ + 'time <': 'SUM(FOO(BAR(\'x\')))', + }); + parser.parseWhere(driver, state); + expect(state.whereMap) + .to.be.deep.equal(['"time" < SUM(FOO(BAR(\'x\')))']); + }); + test('Has literal value', () => { + whereMock({ + foo: 3, + }); + parser.parseWhere(driver, state); + expect(state.whereMap) + .to.be.deep.equal(['"foo" = ?']); + expect(state.whereValues) + .to.be.deep.equal(['3']); + }); + test('Has multiple literal values', () => { + whereMock({ + foo: 3, + bar: 5, + }); + parser.parseWhere(driver, state); + expect(state.whereMap) + .to.be.deep.equal(['"foo" = ?', '"bar" = ?']); + expect(state.whereValues) + .to.be.deep.equal(['3', '5']); + }); + }); + suite('Parse join tests', () => { + let data = [ + { + desc: 'Simple equals condition', + join: 'table1.field1=table2.field2', + expected: ['table1.field1', '=', 'table2.field2'], + }, { + desc: 'Db.table.field condition', + join: 'db1.table1.field1!=db2.table2.field2', + expected: ['db1.table1.field1', '!=', 'db2.table2.field2'], + }, { + desc: 'Underscore in identifier', + join: 'table_1.field1 = tab_le2.field_2', + expected: ['table_1.field1', '=', 'tab_le2.field_2'], + }, { + desc: 'Function in condition', + join: 'table1.field1 > SUM(3+6)', + expected: ['table1.field1', '>', 'SUM(3+6)'], + }, + ]; + + data.forEach(datum => { + test(datum.desc, () => { + let matches = parser.parseJoin(datum.join); + expect(matches.combined).to.be.deep.equal(datum.expected); + }); + }); + }); + suite('Compile join tests', () => { + let data = [ + { + desc: 'Simple equals condition', + clause: 'table1.field1=table2.field2', + expected: '"table1"."field1" = "table2"."field2"', + }, { + desc: 'Db.table.field condition', + clause: 'db1.table1.field1!=db2.table2.field2', + expected: '"db1"."table1"."field1" != "db2"."table2"."field2"', + }, { + desc: 'Underscore in identifier', + clause: 'table_1.field1 = tab_le2.field_2', + expected: '"table_1"."field1" = "tab_le2"."field_2"', + }, { + desc: 'Function in condition', + clause: 'table1.field1 > SUM(3+6)', + expected: '"table1"."field1" > SUM(3+6)', + }, + ]; + + data.forEach(datum => { + test(datum.desc, () => { + let join = parser.compileJoin(datum.clause); + expect(join).to.be.deep.equal(datum.expected); + }); + }); + }); }); \ No newline at end of file