diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..a20743b --- /dev/null +++ b/.editorconfig @@ -0,0 +1,29 @@ +# EditorConfig is awesome: http://EditorConfig.org + +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf +insert_final_newline = true + +# Matches multiple files with brace expansion notation +# Set default charset +[*] +charset = utf-8 + +# Tab indentation (no size specified) +[*] +indent_style = tab +indent_size = 4 + +# Indentation override for all JS under lib directory +[*.js] +indent_size = 2 + +# Matches the exact files either package.json or .travis.yml +[*.yml] +indent_style = space +indent_size = 2 + diff --git a/.gitignore b/.gitignore index 5bf2dd4..6a29d6d 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,7 @@ coverage npm-debug.log node_modules/* .sonar/* -test/config.json \ No newline at end of file +test/config.json +.DS_store +/.idea/ +yarn.lock \ No newline at end of file diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..c559a5c --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,31 @@ +before_script: + # Install dependencies + - bash test/docker_install.sh > /dev/null + - npm install + +services: + - mysql:latest + - postgres:latest + +variables: + MYSQL_ROOT_PASSWORD: foo-bar-baz + MYSQL_DATABASE: test + MYSQL_USER: test + MYSQL_PASSWORD: test + POSTGRES_DB: test + POSTGRES_USER: test + POSTGRES_PASSWORD: test + +# This folder is cached between builds +# http://docs.gitlab.com/ce/ci/yaml/README.html#cache +cache: + paths: + - node_modules/ + +test:6: + image: node:6 + script: npm run test + +test:latest: + image: node:latest + script: npm run test diff --git a/.istanbul.yml b/.istanbul.yml new file mode 100644 index 0000000..51f02d4 --- /dev/null +++ b/.istanbul.yml @@ -0,0 +1,9 @@ +reporting: + print: summary + reports: + - lcov + - lcovonly + - clover + - html + - text + dir: ./coverage \ No newline at end of file diff --git a/.jscsrc b/.jscsrc deleted file mode 100644 index 49f6214..0000000 --- a/.jscsrc +++ /dev/null @@ -1,7 +0,0 @@ -{ - "preset": "airbnb", - "validateIndentation": null, - "requireLineFeedAtFileEnd": null, - "disallowSpaceAfterPrefixUnaryOperators": null, - "disallowMultipleVarDecl": null -} \ No newline at end of file diff --git a/API.md b/API.md index 70e758d..6e294e3 100644 --- a/API.md +++ b/API.md @@ -1,10 +1,12 @@ + + # NodeQuery Class for connection management **Parameters** -- `config` **object** connection parameters +- `config` **[object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** connection parameters ## constructor @@ -12,7 +14,7 @@ Constructor **Parameters** -- `config` **object** connection parameters +- `config` **[object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** connection parameters **Examples** @@ -39,10 +41,12 @@ let nodeQuery = require('ci-node-query')({ Return an existing query builder instance -Returns **QueryBuilder** The Query Builder object +Returns **[QueryBuilder](#querybuilder)** The Query Builder object # QueryBuilder +**Extends QueryBuilderBase** + Main object that builds SQL queries. **Parameters** @@ -50,354 +54,16 @@ Main object that builds SQL queries. - `Driver` **Driver** The syntax driver for the database - `Adapter` **Adapter** The database module adapter for running queries -## delete - -Run the generated delete query - -**Parameters** - -- `table` **String** The table to insert into -- `where` **[Object]** Where clause for delete statement -- `callback` **[Function]** Callback for handling response from the database - -Returns **void or Promise** If no callback is passed, a promise is returned - -## end - -Closes the database connection for the current adapter - -Returns **void** - -## from - -Specify the database table to select from - -**Parameters** - -- `tableName` **String** The table to use for the current query - -**Examples** - -```javascript -query.from('tableName'); -``` - -```javascript -query.from('tableName t'); // Select the table with an alias -``` - -Returns **QueryBuilder** The Query Builder object, for chaining - -## get - -Get the results of the compiled query - -**Parameters** - -- `table` **[String]** The table to select from -- `limit` **[Number]** A limit for the query -- `offset` **[Number]** An offset for the query -- `callback` **[Function]** A callback for receiving the result - -**Examples** - -```javascript -query.get('table_name').then(promiseCallback); // Get all the rows in the table -``` - -```javascript -query.get('table_name', 5, callback); // Get 5 rows from the table -``` - -```javascript -query.get(callback); // Get the results of a query generated with other methods -``` - -Returns **void or Promise** If no callback is passed, a promise is returned - -## getCompiledDelete - -Return generated delete query SQL - -**Parameters** - -- `table` **String** the name of the table to delete from -- `reset` **[Boolean]** Whether to reset the query builder so another query can be built (optional, default `true`) - -Returns **String** The compiled sql statement - -## getCompiledInsert - -Return generated insert query SQL - -**Parameters** - -- `table` **String** the name of the table to insert into -- `reset` **[Boolean]** Whether to reset the query builder so another query can be built (optional, default `true`) - -Returns **String** The compiled sql statement - -## getCompiledSelect - -Return generated select query SQL - -**Parameters** - -- `table` **[String]** the name of the table to retrieve from -- `reset` **[Boolean]** Whether to reset the query builder so another query can be built (optional, default `true`) - -Returns **String** The compiled sql statement - -## getCompiledUpdate - -Return generated update query SQL - -**Parameters** - -- `table` **String** the name of the table to update -- `reset` **[Boolean]** Whether to reset the query builder so another query can be built (optional, default `true`) - -Returns **String** The compiled sql statement - -## groupBy - -Group the results by the selected field(s) - -**Parameters** - -- `field` **String or Array** 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 - -## having - -Add a 'having' clause - -**Parameters** - -- `key` **String or Object** The name of the field and the comparision operator, or an object -- `val` **[String or Number]** The value to compare if the value of key is a string - -Returns **QueryBuilder** The Query Builder object, for chaining - -## insert - -Run the generated insert query - -**Parameters** - -- `table` **String** The table to insert into -- `data` **[Object]** Data to insert, if not already added with the 'set' method -- `callback` **[Function]** Callback for handling response from the database - -Returns **void or Promise** If no callback is passed, a promise is returned - -## insertBatch - -Insert multiple sets of rows at a time - -**Parameters** - -- `table` **String** The table to insert into -- `data` **Array** The array of objects containing data rows to insert -- `callback` **[Function]** Callback for handling database response - -**Examples** - -```javascript -query.insertBatch('foo',[{id:1,val:'bar'},{id:2,val:'baz'}], callbackFunction); -``` - -```javascript -query.insertBatch('foo',[{id:1,val:'bar'},{id:2,val:'baz'}]) -.then(promiseCallback); -``` - -Returns **void or Promise** If no callback is passed, a promise is returned - -## join - -Add a join clause to the query - -**Parameters** - -- `table` **String** The table you are joining -- `cond` **String** The join condition. -- `type` **[String]** The type of join, which defaults to inner (optional, default `'inner'`) - -Returns **QueryBuilder** The Query Builder object, for chaining - -## like - -Add a 'like/ and like' clause to the query - -**Parameters** - -- `field` **String** The name of the field to compare to -- `val` **String** The value to compare to -- `pos` **[String]** The placement of the wildcard character(s): before, after, or both (optional, default `both`) - -Returns **QueryBuilder** The Query Builder object, for chaining - -## limit - -Put a limit on the query - -**Parameters** - -- `limit` **Number** The maximum number of rows to fetch -- `offset` **[Number]** The row number to start from - -Returns **QueryBuilder** The Query Builder object, for chaining - -## notLike - -Add a 'not like/ and not like' clause to the query - -**Parameters** - -- `field` **String** The name of the field to compare to -- `val` **String** The value to compare to -- `pos` **[String]** The placement of the wildcard character(s): before, after, or both (optional, default `both`) - -Returns **QueryBuilder** The Query Builder object, for chaining - -## orGroupStart - -Adds an open paren to the current query for logical grouping, -prefixed with 'OR' - -Returns **QueryBuilder** The Query Builder object, for chaining - -## orHaving - -Add an 'or having' clause - -**Parameters** - -- `key` **String or Object** The name of the field and the comparision operator, or an object -- `val` **[String or Number]** The value to compare if the value of key is a string - -Returns **QueryBuilder** The Query Builder object, for chaining - -## orLike - -Add an 'or like' clause to the query - -**Parameters** - -- `field` **String** The name of the field to compare to -- `val` **String** The value to compare to -- `pos` **[String]** The placement of the wildcard character(s): before, after, or both (optional, default `both`) - -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 - -Add an 'or not like' clause to the query - -**Parameters** - -- `field` **String** The name of the field to compare to -- `val` **String** The value to compare to -- `pos` **[String]** The placement of the wildcard character(s): before, after, or both (optional, default `both`) - -Returns **QueryBuilder** The Query Builder object, for chaining - -## orWhere - -Set a 'or where' clause - -**Parameters** - -- `key` **String or Object** The name of the field and the comparision operator, or an object -- `val` **[String or Number]** The value to compare if the value of key is a string - -Returns **QueryBuilder** The Query Builder object, for chaining - -## orWhereIn - -Set a 'or where in' clause - -**Parameters** - -- `key` **String** the field to search -- `values` **Array** the array of items to search in - -Returns **QueryBuilder** The Query Builder object, for chaining - -## orWhereIsNotNull - -Field is not null prefixed with 'OR' - -**Parameters** - -- `field` **String** The name of the field - -Returns **QueryBuilder** The Query Builder object, for chaining - -## orWhereIsNull - -Field is null prefixed with 'OR' - -**Parameters** - -- `field` **String** The name of the field - -Returns **QueryBuilder** The Query Builder object, for chaining - -## orWhereNotIn - -Set a 'or where not in' clause - -**Parameters** - -- `key` **String** the field to search -- `values` **Array** the array of items to search in - -Returns **QueryBuilder** The Query Builder object, for chaining - -## orderBy - -Order the results by the selected field(s) - -**Parameters** - -- `field` **String** The field(s) to order by -- `type` **[String]** The order direction, ASC or DESC (optional, default `'ASC'`) - -Returns **QueryBuilder** The Query Builder object, for chaining - ## query Run an arbitrary sql query. Run as a prepared statement. **Parameters** -- `sql` **string** The sql to execute -- `params` **[array]** The query parameters -- `callback` **[function]** Optional callback +- `sql` **[string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** The sql to execute +- `params` **\[[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)]** The query parameters -Returns **void or Promise** Returns a promise if no callback is supplied +Returns **[Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)** Promise with result of query ## resetQuery @@ -405,13 +71,29 @@ Reset the object state for a new query Returns **void** +## truncate + +Empties the selected database table + +**Parameters** + +- `table` **[string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** the name of the table to truncate + +Returns **(void | [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise))** Returns a promise if no callback is supplied + +## end + +Closes the database connection for the current adapter + +Returns **void** + ## select Specify rows to select in the query **Parameters** -- `fields` **String or Array** The fields to select from the current table +- `fields` **([String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String) \| [Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array))** The fields to select from the current table **Examples** @@ -423,7 +105,203 @@ query.select('foo, bar'); // Select multiple fields with a string query.select(['foo', 'bar']); // Select multiple fileds with an array ``` -Returns **QueryBuilder** The Query Builder object, for chaining +Returns **[QueryBuilder](#querybuilder)** The Query Builder object, for chaining + +## from + +Specify the database table to select from + +**Parameters** + +- `tableName` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** The table to use for the current query + +**Examples** + +```javascript +query.from('tableName'); +``` + +```javascript +query.from('tableName t'); // Select the table with an alias +``` + +Returns **[QueryBuilder](#querybuilder)** The Query Builder object, for chaining + +## like + +Add a 'like/ and like' clause to the query + +**Parameters** + +- `field` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** The name of the field to compare to +- `val` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** The value to compare to +- `pos` **\[[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)]** The placement of the wildcard character(s): before, after, or both (optional, default `both`) + +Returns **[QueryBuilder](#querybuilder)** The Query Builder object, for chaining + +## notLike + +Add a 'not like/ and not like' clause to the query + +**Parameters** + +- `field` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** The name of the field to compare to +- `val` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** The value to compare to +- `pos` **\[[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)]** The placement of the wildcard character(s): before, after, or both (optional, default `both`) + +Returns **[QueryBuilder](#querybuilder)** The Query Builder object, for chaining + +## orLike + +Add an 'or like' clause to the query + +**Parameters** + +- `field` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** The name of the field to compare to +- `val` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** The value to compare to +- `pos` **\[[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)]** The placement of the wildcard character(s): before, after, or both (optional, default `both`) + +Returns **[QueryBuilder](#querybuilder)** The Query Builder object, for chaining + +## orNotLike + +Add an 'or not like' clause to the query + +**Parameters** + +- `field` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** The name of the field to compare to +- `val` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** The value to compare to +- `pos` **\[[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)]** The placement of the wildcard character(s): before, after, or both (optional, default `both`) + +Returns **[QueryBuilder](#querybuilder)** The Query Builder object, for chaining + +## having + +Add a 'having' clause + +**Parameters** + +- `key` **([String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String) \| [Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object))** The name of the field and the comparision operator, or an object +- `val` **\[([String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String) \| [Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number))]** The value to compare if the value of key is a string + +Returns **[QueryBuilder](#querybuilder)** The Query Builder object, for chaining + +## orHaving + +Add an 'or having' clause + +**Parameters** + +- `key` **([String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String) \| [Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object))** The name of the field and the comparision operator, or an object +- `val` **\[([String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String) \| [Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number))]** The value to compare if the value of key is a string + +Returns **[QueryBuilder](#querybuilder)** The Query Builder object, for chaining + +## where + +Set a 'where' clause + +**Parameters** + +- `key` **([String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String) \| [Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object))** The name of the field and the comparision operator, or an object +- `val` **\[([String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String) \| [Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number))]** The value to compare if the value of key is a string + +Returns **[QueryBuilder](#querybuilder)** The Query Builder object, for chaining + +## orWhere + +Set a 'or where' clause + +**Parameters** + +- `key` **([String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String) \| [Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object))** The name of the field and the comparision operator, or an object +- `val` **\[([String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String) \| [Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number))]** The value to compare if the value of key is a string + +Returns **[QueryBuilder](#querybuilder)** The Query Builder object, for chaining + +## whereIsNull + +Select a field that is Null + +**Parameters** + +- `field` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** The name of the field that has a NULL value + +Returns **[QueryBuilder](#querybuilder)** The Query Builder object, for chaining + +## whereIsNotNull + +Specify that a field IS NOT NULL + +**Parameters** + +- `field` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** The name so the field that is not to be null + +Returns **[QueryBuilder](#querybuilder)** The Query Builder object, for chaining + +## orWhereIsNull + +Field is null prefixed with 'OR' + +**Parameters** + +- `field` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** The name of the field + +Returns **[QueryBuilder](#querybuilder)** The Query Builder object, for chaining + +## orWhereIsNotNull + +Field is not null prefixed with 'OR' + +**Parameters** + +- `field` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** The name of the field + +Returns **[QueryBuilder](#querybuilder)** The Query Builder object, for chaining + +## whereIn + +Set a 'where in' clause + +**Parameters** + +- `key` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** the field to search +- `values` **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)** the array of items to search in + +Returns **[QueryBuilder](#querybuilder)** The Query Builder object, for chaining + +## orWhereIn + +Set a 'or where in' clause + +**Parameters** + +- `key` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** the field to search +- `values` **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)** the array of items to search in + +Returns **[QueryBuilder](#querybuilder)** The Query Builder object, for chaining + +## whereNotIn + +Set a 'where not in' clause + +**Parameters** + +- `key` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** the field to search +- `values` **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)** the array of items to search in + +Returns **[QueryBuilder](#querybuilder)** The Query Builder object, for chaining + +## orWhereNotIn + +Set a 'or where not in' clause + +**Parameters** + +- `key` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** the field to search +- `values` **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)** the array of items to search in + +Returns **[QueryBuilder](#querybuilder)** The Query Builder object, for chaining ## set @@ -431,8 +309,8 @@ Set values for insertion or updating **Parameters** -- `key` **String or Object** The key or object to use -- `val` **[String]** The value if using a scalar key +- `key` **([String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String) \| [Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object))** The key or object to use +- `val` **\[[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)]** The value if using a scalar key **Examples** @@ -444,18 +322,132 @@ query.set('foo', 'bar'); // Set a key, value pair query.set({foo:'bar'}); // Set with an object ``` -Returns **QueryBuilder** The Query Builder object, for chaining +Returns **[QueryBuilder](#querybuilder)** The Query Builder object, for chaining -## truncate +## join -Empties the selected database table +Add a join clause to the query **Parameters** -- `table` **string** the name of the table to truncate -- `callback` **[function]** Optional callback +- `table` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** The table you are joining +- `cond` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** The join condition. +- `type` **\[[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)]** The type of join, which defaults to inner (optional, default `'inner'`) -Returns **void or Promise** Returns a promise if no callback is supplied +Returns **[QueryBuilder](#querybuilder)** The Query Builder object, for chaining + +## groupBy + +Group the results by the selected field(s) + +**Parameters** + +- `field` **([String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String) \| [Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array))** The name of the field to group by + +Returns **[QueryBuilder](#querybuilder)** The Query Builder object, for chaining + +## orderBy + +Order the results by the selected field(s) + +**Parameters** + +- `field` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** The field(s) to order by +- `type` **\[[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)]** The order direction, ASC or DESC (optional, default `'ASC'`) + +Returns **[QueryBuilder](#querybuilder)** The Query Builder object, for chaining + +## limit + +Put a limit on the query + +**Parameters** + +- `limit` **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** The maximum number of rows to fetch +- `offset` **\[[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)]** The row number to start from + +Returns **[QueryBuilder](#querybuilder)** The Query Builder object, for chaining + +## groupStart + +Adds an open paren to the current query for logical grouping + +Returns **[QueryBuilder](#querybuilder)** The Query Builder object, for chaining + +## orGroupStart + +Adds an open paren to the current query for logical grouping, +prefixed with 'OR' + +Returns **[QueryBuilder](#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](#querybuilder)** The Query Builder object, for chaining + +## groupEnd + +Ends a logical grouping started with one of the groupStart methods + +Returns **[QueryBuilder](#querybuilder)** The Query Builder object, for chaining + +## get + +Get the results of the compiled query + +**Parameters** + +- `table` **\[[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)]** The table to select from +- `limit` **\[[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)]** A limit for the query +- `offset` **\[[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)]** An offset for the query + +**Examples** + +```javascript +query.get('table_name').then(promiseCallback); // Get all the rows in the table +``` + +```javascript +query.get('table_name', 5); // Get 5 rows from the table +``` + +```javascript +query.get(); // Get the results of a query generated with other methods +``` + +Returns **[Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)<[Result](#result)>** Promise containing the result of the query + +## insert + +Run the generated insert query + +**Parameters** + +- `table` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** The table to insert into +- `data` **\[[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)]** Data to insert, if not already added with the 'set' method + +Returns **[Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)<[Result](#result)>** Promise containing the result of the query + +## insertBatch + +Insert multiple sets of rows at a time + +**Parameters** + +- `table` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** The table to insert into +- `data` **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)** The array of objects containing data rows to insert + +**Examples** + +```javascript +query.insertBatch('foo',[{id:1,val:'bar'},{id:2,val:'baz'}]) +.then(promiseCallback); +``` + +Returns **[Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)<[Result](#result)>** Promise containing the result of the query ## update @@ -463,64 +455,65 @@ Run the generated update query **Parameters** -- `table` **String** The table to insert into -- `data` **[Object]** Data to insert, if not already added with the 'set' method -- `callback` **[Function]** Callback for handling response from the database +- `table` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** The table to insert into +- `data` **\[[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)]** Data to insert, if not already added with the 'set' method -Returns **void or Promise** If no callback is passed, a promise is returned +Returns **[Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)<[Result](#result)>** Promise containing the result of the query -## where +## delete -Set a 'where' clause +Run the generated delete query **Parameters** -- `key` **String or Object** The name of the field and the comparision operator, or an object -- `val` **[String or Number]** The value to compare if the value of key is a string +- `table` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** The table to insert into +- `where` **\[[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)]** Where clause for delete statement -Returns **QueryBuilder** The Query Builder object, for chaining +Returns **[Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)<[Result](#result)>** Promise containing the result of the query -## whereIn +## getCompiledSelect -Set a 'where in' clause +Return generated select query SQL **Parameters** -- `key` **String** the field to search -- `values` **Array** the array of items to search in +- `table` **\[[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)]** the name of the table to retrieve from +- `reset` **\[[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)]** Whether to reset the query builder so another query can be built (optional, default `true`) -Returns **QueryBuilder** The Query Builder object, for chaining +Returns **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** The compiled sql statement -## whereIsNotNull +## getCompiledInsert -Specify that a field IS NOT NULL +Return generated insert query SQL **Parameters** -- `field` **String** The name so the field that is not to be null +- `table` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** the name of the table to insert into +- `reset` **\[[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)]** Whether to reset the query builder so another query can be built (optional, default `true`) -Returns **QueryBuilder** The Query Builder object, for chaining +Returns **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** The compiled sql statement -## whereIsNull +## getCompiledUpdate -Select a field that is Null +Return generated update query SQL **Parameters** -- `field` **String** The name of the field that has a NULL value +- `table` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** the name of the table to update +- `reset` **\[[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)]** Whether to reset the query builder so another query can be built (optional, default `true`) -Returns **QueryBuilder** The Query Builder object, for chaining +Returns **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** The compiled sql statement -## whereNotIn +## getCompiledDelete -Set a 'where not in' clause +Return generated delete query SQL **Parameters** -- `key` **String** the field to search -- `values` **Array** the array of items to search in +- `table` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** the name of the table to delete from +- `reset` **\[[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)]** Whether to reset the query builder so another query can be built (optional, default `true`) -Returns **QueryBuilder** The Query Builder object, for chaining +Returns **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** The compiled sql statement # Result @@ -528,17 +521,17 @@ Query result object **Parameters** -- `rows` **Array** the data rows of the result -- `columns` **Array** the column names in the result - -## columnCount - -Get the number of columns returned by the query - -Returns **Number** the number of columns in the result +- `rows` **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)** the data rows of the result +- `columns` **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)** the column names in the result ## rowCount Get the number of rows returned by the query -Returns **Number** the number of rows in the result +Returns **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** the number of rows in the result + +## columnCount + +Get the number of columns returned by the query + +Returns **[Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)** the number of columns in the result diff --git a/CHANGELOG.md b/CHANGELOG.md index 94b7191..321bae3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +# 5.0.0 +* Re-added firebird as a database +* Replaced all callback interfaces with promises + ## 4.0.0 * Changed connection setup to just use a config object - the appropriate adapter object is created by the library. * Removed mysql adapter, as mysql2 is very similar and does proper prepared statements @@ -11,4 +15,4 @@ * Added back tests for `node-firebird` adapter. Using this adapter with promises is not currently supported. ## 3.1.0 -* Added support for promises on query execution methods \ No newline at end of file +* Added support for promises on query execution methods diff --git a/README.md b/README.md index 5eaefcc..0d514c2 100755 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ A node query builder for various SQL databases, based on [CodeIgniter](http://ww ### Supported databases +* Firebird (via `node-firebird`) * Mysql (via `mysql2`) * PostgreSQL (via `pg`) * Sqlite (via `dblite`) @@ -38,19 +39,9 @@ const nodeQuery = require('ci-node-query')({ // Get the query builder const query = nodeQuery.getQuery(); -query.select('foo') - .from('bar') - .where('x', 3) - .orWhere({y: 2}) - .join('baz', 'baz.boo = bar.foo', 'left') - .orderBy('x', 'DESC') - .limit(2, 3) - .get(function(err, result) { - // Handle Results Here - }); - // As of version 3.1.0, you can also get promises -var queryPromise = query.select('foo') +// Version 5.0.0 removes all callback interfaces +const queryPromise = query.select('foo') .from('bar') .where('x', 3) .orWhere({y: 2}) @@ -93,5 +84,7 @@ As of version 2, `where` and `having` type methods parse the values passed to lo * Generated documentation is in the docs/ folder * The API is documented in [API.md](./API.md) * The `tests/adapters` folder contains examples of how to set up a connection for the appropriate database library -* The documentation generated for the latest dev build is also [Available](https://github.timshomepage.net/node-query/docs/) +* The documentation generated for the latest dev build is also [Available](https://github.timshomepage.net/node-query/docs/index.html) + +[![js-happiness-style](https://cdn.rawgit.com/JedWatson/happiness/master/badge.svg)](https://github.com/JedWatson/happiness) diff --git a/docs/assets/bass-addons.css b/docs/assets/bass-addons.css new file mode 100644 index 0000000..c27e96d --- /dev/null +++ b/docs/assets/bass-addons.css @@ -0,0 +1,12 @@ +.input { + font-family: inherit; + display: block; + width: 100%; + height: 2rem; + padding: .5rem; + margin-bottom: 1rem; + border: 1px solid #ccc; + font-size: .875rem; + border-radius: 3px; + box-sizing: border-box; +} diff --git a/docs/assets/bass.css b/docs/assets/bass.css old mode 100755 new mode 100644 index 941c260..15e0dc9 --- a/docs/assets/bass.css +++ b/docs/assets/bass.css @@ -1,850 +1,543 @@ -/* +/*! Basscss | http://basscss.com | MIT License */ - Basscss v7.0.3 - Low-level CSS toolkit - http://basscss.com +.h1{ font-size: 2rem } +.h2{ font-size: 1.5rem } +.h3{ font-size: 1.25rem } +.h4{ font-size: 1rem } +.h5{ font-size: .875rem } +.h6{ font-size: .75rem } - 14.88 kB - 3.38 kB Gzipped - 286 Rules - 328 Selectors - 441 Declarations - 95 Properties +.font-family-inherit{ font-family:inherit } +.font-size-inherit{ font-size:inherit } +.text-decoration-none{ text-decoration:none } -*/ +.bold{ font-weight: bold; font-weight: bold } +.regular{ font-weight:normal } +.italic{ font-style:italic } +.caps{ text-transform:uppercase; letter-spacing: .2em; } +.left-align{ text-align:left } +.center{ text-align:center } +.right-align{ text-align:right } +.justify{ text-align:justify } +.nowrap{ white-space:nowrap } +.break-word{ word-wrap:break-word } -body { margin: 0 } -img { max-width: 100% } -svg { max-height: 100% } +.line-height-1{ line-height: 1 } +.line-height-2{ line-height: 1.125 } +.line-height-3{ line-height: 1.25 } +.line-height-4{ line-height: 1.5 } -input, -select, -textarea, -fieldset { - font-family: inherit; - font-size: 1rem; - box-sizing: border-box; - margin-top: 0; - margin-bottom: 0; +.list-style-none{ list-style:none } +.underline{ text-decoration:underline } + +.truncate{ + max-width:100%; + overflow:hidden; + text-overflow:ellipsis; + white-space:nowrap; } -label { - vertical-align: middle; +.list-reset{ + list-style:none; + padding-left:0; } -input[type=text], -input[type=date], -input[type=datetime], -input[type=datetime-local], -input[type=email], -input[type=month], -input[type=number], -input[type=password], -input[type=search], -input[type=tel], -input[type=time], -input[type=url], -input[type=week] { - height: 2.25rem; - padding: .5rem .5rem; - vertical-align: middle; - -webkit-appearance: none; -} +.inline{ display:inline } +.block{ display:block } +.inline-block{ display:inline-block } +.table{ display:table } +.table-cell{ display:table-cell } -select { - line-height: 1.75; - padding: .5rem .5rem; -} - -select:not([multiple]) { - height: 2.25rem; - vertical-align: middle; -} - -textarea { - line-height: 1.75; - padding: .5rem .5rem; -} - -table { - border-collapse: separate; - border-spacing: 0; - max-width: 100%; - width: 100%; -} - -th { - text-align: left; - font-weight: bold; -} - -th, -td { - padding: .25rem 1rem; - line-height: inherit; -} - -th { vertical-align: bottom } -td { vertical-align: top } - -body { - font-family: 'Helvetica Neue', Helvetica, sans-serif; - line-height: 1.5; - font-size: 100%; -} - -h1, h2, h3, h4, h5, h6 { - font-family: 'Helvetica Neue', Helvetica, sans-serif; - font-weight: bold; - line-height: 1.25; - margin-top: 1em; - margin-bottom: .5em; -} - -p { - margin-top: 0; - margin-bottom: 1rem; -} - -dl, ol, ul { - margin-top: 0; - margin-bottom: 1rem; -} - -pre, code, samp { - font-family: 'Source Code Pro', Consolas, monospace; - font-size: inherit; -} - -pre { - margin-top: 0; - margin-bottom: 1rem; - overflow-x: scroll; -} - -h1 { font-size: 2rem } -h2 { font-size: 1.5rem } -h3 { font-size: 1.25rem } -h4 { font-size: 1rem } -h5 { font-size: .875rem } -h6 { font-size: .75rem } - -body { - color: #111; - background-color: #fff; -} - -a { - color: #0074d9; - text-decoration: none; -} - -a:hover { - text-decoration: underline; -} - -pre, code { - background-color: transparent; - border-radius: 3px; -} - -hr { - border: 0; - border-bottom-style: solid; - border-bottom-width: 1px; - border-bottom-color: rgba(0,0,0,.125); -} - -.field { - border-style: solid; - border-width: 1px; - border-color: rgba(0,0,0,.125); - border-radius: 3px; -} - -.field:focus, -.field.is-focused { - outline: none; - border-color: #0074d9; - box-shadow: 0 0 0 2px rgba(0, 116, 217, 0.5); -} - -.field:disabled, -.field.is-disabled { - background-color: rgba(0,0,0,.125); - opacity: .5; -} - -.field:read-only:not(select), -.field.is-read-only { - background-color: rgba(0,0,0,.125); -} - - -.field.is-success { - border-color: #2ecc40; -} - -.field.is-success:focus, -.field.is-success.is-focused { - box-shadow: 0 0 0 2px rgba(46, 204, 64, 0.5); -} - -.field.is-warning { - border-color: #ffdc00; -} - -.field.is-warning:focus, -.field.is-warning.is-focused { - box-shadow: 0 0 0 2px rgba(255, 220, 0, 0.5); -} - -.field:invalid, -.field.is-error { - border-color: #ff4136; -} - -.field:invalid:focus, -.field:invalid.is-focused, -.field.is-error:focus, -.field.is-error.is-focused { - box-shadow: 0 0 0 2px rgba(255, 65, 54, 0.5); -} - -.table-light th, -.table-light td { - border-bottom-width: 1px; - border-bottom-style: solid; - border-bottom-color: rgba(0,0,0,.125); -} - -.table-light tr:last-child td { - border-bottom: 0; -} - -.btn { - font-family: inherit; - font-size: inherit; - font-weight: bold; - text-decoration: none; - cursor: pointer; - display: inline-block; - line-height: 1.125rem; - padding: .5rem 1rem; - margin: 0; - height: auto; - border: 1px solid transparent; - vertical-align: middle; - -webkit-appearance: none; - color: inherit; - background-color: transparent; -} - -.btn:hover { - text-decoration: none; -} - -.btn:focus { - outline: none; - border-color: rgba(0,0,0,.125); - box-shadow: 0 0 0 3px rgba(0,0,0,.25); -} - -::-moz-focus-inner { - border: 0; - padding: 0; -} - -.btn-primary { - color: #fff; - background-color: #0074d9; - border-radius: 3px; -} - -.btn-primary:hover { - box-shadow: inset 0 0 0 20rem rgba(0,0,0,.0625); -} - -.btn-primary:active { - box-shadow: inset 0 0 0 20rem rgba(0,0,0,.125), - inset 0 3px 4px 0 rgba(0,0,0,.25), - 0 0 1px rgba(0,0,0,.125); -} - -.btn-primary:disabled, -.btn-primary.is-disabled { - opacity: .5; -} - -.btn-outline, -.btn-outline:hover { - border-color: currentcolor; -} - -.btn-outline { - border-radius: 3px; -} - -.btn-outline:hover { - box-shadow: inset 0 0 0 20rem rgba(0,0,0,.0625); -} - -.btn-outline:active { - box-shadow: inset 0 0 0 20rem rgba(0,0,0,.125), - inset 0 3px 4px 0 rgba(0,0,0,.25), - 0 0 1px rgba(0,0,0,.125); -} - -.btn-outline:disabled, -.btn-outline.is-disabled { - opacity: .5; -} - -.h1 { font-size: 2rem } -.h2 { font-size: 1.5rem } -.h3 { font-size: 1.25rem } -.h4 { font-size: 1rem } -.h5 { font-size: .875rem } -.h6 { font-size: .75rem } - -.bold { font-weight: bold } -.regular { font-weight: normal } -.italic { font-style: italic } -.caps { text-transform: uppercase; letter-spacing: .2em; } - -.left-align { text-align: left } -.center { text-align: center } -.right-align { text-align: right } -.justify { text-align: justify } - -.nowrap { white-space: nowrap } -.break-word { word-wrap: break-word } - -.truncate { - max-width: 100%; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.list-reset { - list-style: none; - padding-left: 0; -} - -.inline { display: inline } -.block { display: block } -.inline-block { display: inline-block } -.table { display: table } -.table-cell { display: table-cell } - -.overflow-hidden { overflow: hidden } -.overflow-scroll { overflow: scroll } -.overflow-auto { overflow: auto } +.overflow-hidden{ overflow:hidden } +.overflow-scroll{ overflow:scroll } +.overflow-auto{ overflow:auto } .clearfix:before, -.clearfix:after { - content: " "; - display: table +.clearfix:after{ + content:" "; + display:table } -.clearfix:after { clear: both } +.clearfix:after{ clear:both } -.left { float: left } -.right { float: right } +.left{ float:left } +.right{ float:right } -.fit { max-width: 100% } +.fit{ max-width:100% } -.border-box { box-sizing: border-box } +.max-width-1{ max-width: 24rem } +.max-width-2{ max-width: 32rem } +.max-width-3{ max-width: 48rem } +.max-width-4{ max-width: 64rem } -.align-baseline { vertical-align: baseline } -.align-top { vertical-align: top } -.align-middle { vertical-align: middle } -.align-bottom { vertical-align: bottom } +.border-box{ box-sizing:border-box } -.m0 { margin: 0 } -.mt0 { margin-top: 0 } -.mr0 { margin-right: 0 } -.mb0 { margin-bottom: 0 } -.ml0 { margin-left: 0 } +.align-baseline{ vertical-align:baseline } +.align-top{ vertical-align:top } +.align-middle{ vertical-align:middle } +.align-bottom{ vertical-align:bottom } -.m1 { margin: .5rem } -.mt1 { margin-top: .5rem } -.mr1 { margin-right: .5rem } -.mb1 { margin-bottom: .5rem } -.ml1 { margin-left: .5rem } +.m0{ margin:0 } +.mt0{ margin-top:0 } +.mr0{ margin-right:0 } +.mb0{ margin-bottom:0 } +.ml0{ margin-left:0 } +.mx0{ margin-left:0; margin-right:0 } +.my0{ margin-top:0; margin-bottom:0 } -.m2 { margin: 1rem } -.mt2 { margin-top: 1rem } -.mr2 { margin-right: 1rem } -.mb2 { margin-bottom: 1rem } -.ml2 { margin-left: 1rem } +.m1{ margin: .5rem } +.mt1{ margin-top: .5rem } +.mr1{ margin-right: .5rem } +.mb1{ margin-bottom: .5rem } +.ml1{ margin-left: .5rem } +.mx1{ margin-left: .5rem; margin-right: .5rem } +.my1{ margin-top: .5rem; margin-bottom: .5rem } -.m3 { margin: 2rem } -.mt3 { margin-top: 2rem } -.mr3 { margin-right: 2rem } -.mb3 { margin-bottom: 2rem } -.ml3 { margin-left: 2rem } +.m2{ margin: 1rem } +.mt2{ margin-top: 1rem } +.mr2{ margin-right: 1rem } +.mb2{ margin-bottom: 1rem } +.ml2{ margin-left: 1rem } +.mx2{ margin-left: 1rem; margin-right: 1rem } +.my2{ margin-top: 1rem; margin-bottom: 1rem } -.m4 { margin: 4rem } -.mt4 { margin-top: 4rem } -.mr4 { margin-right: 4rem } -.mb4 { margin-bottom: 4rem } -.ml4 { margin-left: 4rem } +.m3{ margin: 2rem } +.mt3{ margin-top: 2rem } +.mr3{ margin-right: 2rem } +.mb3{ margin-bottom: 2rem } +.ml3{ margin-left: 2rem } +.mx3{ margin-left: 2rem; margin-right: 2rem } +.my3{ margin-top: 2rem; margin-bottom: 2rem } -.mxn1 { margin-left: -.5rem; margin-right: -.5rem; } -.mxn2 { margin-left: -1rem; margin-right: -1rem; } -.mxn3 { margin-left: -2rem; margin-right: -2rem; } -.mxn4 { margin-left: -4rem; margin-right: -4rem; } +.m4{ margin: 4rem } +.mt4{ margin-top: 4rem } +.mr4{ margin-right: 4rem } +.mb4{ margin-bottom: 4rem } +.ml4{ margin-left: 4rem } +.mx4{ margin-left: 4rem; margin-right: 4rem } +.my4{ margin-top: 4rem; margin-bottom: 4rem } -.mx-auto { margin-left: auto; margin-right: auto; } -.p0 { padding: 0 } +.mxn1{ margin-left: -.5rem; margin-right: -.5rem; } +.mxn2{ margin-left: -1rem; margin-right: -1rem; } +.mxn3{ margin-left: -2rem; margin-right: -2rem; } +.mxn4{ margin-left: -4rem; margin-right: -4rem; } -.p1 { padding: .5rem } -.py1 { padding-top: .5rem; padding-bottom: .5rem } -.px1 { padding-left: .5rem; padding-right: .5rem } +.ml-auto{ margin-left:auto } +.mr-auto{ margin-right:auto } +.mx-auto{ margin-left:auto; margin-right:auto; } -.p2 { padding: 1rem } -.py2 { padding-top: 1rem; padding-bottom: 1rem } -.px2 { padding-left: 1rem; padding-right: 1rem } +.p0{ padding:0 } +.pt0{ padding-top:0 } +.pr0{ padding-right:0 } +.pb0{ padding-bottom:0 } +.pl0{ padding-left:0 } +.px0{ padding-left:0; padding-right:0 } +.py0{ padding-top:0; padding-bottom:0 } -.p3 { padding: 2rem } -.py3 { padding-top: 2rem; padding-bottom: 2rem } -.px3 { padding-left: 2rem; padding-right: 2rem } +.p1{ padding: .5rem } +.pt1{ padding-top: .5rem } +.pr1{ padding-right: .5rem } +.pb1{ padding-bottom: .5rem } +.pl1{ padding-left: .5rem } +.py1{ padding-top: .5rem; padding-bottom: .5rem } +.px1{ padding-left: .5rem; padding-right: .5rem } -.p4 { padding: 4rem } -.py4 { padding-top: 4rem; padding-bottom: 4rem } -.px4 { padding-left: 4rem; padding-right: 4rem } +.p2{ padding: 1rem } +.pt2{ padding-top: 1rem } +.pr2{ padding-right: 1rem } +.pb2{ padding-bottom: 1rem } +.pl2{ padding-left: 1rem } +.py2{ padding-top: 1rem; padding-bottom: 1rem } +.px2{ padding-left: 1rem; padding-right: 1rem } -.relative { position: relative } -.absolute { position: absolute } -.fixed { position: fixed } +.p3{ padding: 2rem } +.pt3{ padding-top: 2rem } +.pr3{ padding-right: 2rem } +.pb3{ padding-bottom: 2rem } +.pl3{ padding-left: 2rem } +.py3{ padding-top: 2rem; padding-bottom: 2rem } +.px3{ padding-left: 2rem; padding-right: 2rem } -.top-0 { top: 0 } -.right-0 { right: 0 } -.bottom-0 { bottom: 0 } -.left-0 { left: 0 } +.p4{ padding: 4rem } +.pt4{ padding-top: 4rem } +.pr4{ padding-right: 4rem } +.pb4{ padding-bottom: 4rem } +.pl4{ padding-left: 4rem } +.py4{ padding-top: 4rem; padding-bottom: 4rem } +.px4{ padding-left: 4rem; padding-right: 4rem } -.z1 { z-index: 1 } -.z2 { z-index: 2 } -.z3 { z-index: 3 } -.z4 { z-index: 4 } - -.sm-show, .md-show, .lg-show { - display: none !important +.col{ + float:left; + box-sizing:border-box; } -@media (min-width: 40em) { - .sm-show { display: block !important } +.col-right{ + float:right; + box-sizing:border-box; } -@media (min-width: 52em) { - .md-show { display: block !important } +.col-1{ + width:8.33333%; } -@media (min-width: 64em) { - .lg-show { display: block !important } +.col-2{ + width:16.66667%; } - -@media (min-width: 40em) { - .sm-hide { display: none !important } +.col-3{ + width:25%; } -@media (min-width: 52em) { - .md-hide { display: none !important } +.col-4{ + width:33.33333%; } -@media (min-width: 64em) { - .lg-hide { display: none !important } +.col-5{ + width:41.66667%; } -.display-none { display: none !important } - -.hide { - position: absolute !important; - height: 1px; - width: 1px; - overflow: hidden; - clip: rect(1px, 1px, 1px, 1px); +.col-6{ + width:50%; } -.container { - max-width: 64em; - margin-left: auto; - margin-right: auto; -} -.col { - float: left; - box-sizing: border-box; +.col-7{ + width:58.33333%; } -.col-right { - float: right; - box-sizing: border-box; +.col-8{ + width:66.66667%; } -.col-1 { - width: 8.33333%; +.col-9{ + width:75%; } -.col-2 { - width: 16.66667%; +.col-10{ + width:83.33333%; } -.col-3 { - width: 25%; +.col-11{ + width:91.66667%; } -.col-4 { - width: 33.33333%; +.col-12{ + width:100%; } +@media (min-width: 40em){ -.col-5 { - width: 41.66667%; -} - -.col-6 { - width: 50%; -} - -.col-7 { - width: 58.33333%; -} - -.col-8 { - width: 66.66667%; -} - -.col-9 { - width: 75%; -} - -.col-10 { - width: 83.33333%; -} - -.col-11 { - width: 91.66667%; -} - -.col-12 { - width: 100%; -} -@media (min-width: 40em) { - - .sm-col { - float: left; - box-sizing: border-box; + .sm-col{ + float:left; + box-sizing:border-box; } - .sm-col-right { - float: right; - box-sizing: border-box; + .sm-col-right{ + float:right; + box-sizing:border-box; } - .sm-col-1 { - width: 8.33333%; + .sm-col-1{ + width:8.33333%; } - .sm-col-2 { - width: 16.66667%; + .sm-col-2{ + width:16.66667%; } - .sm-col-3 { - width: 25%; + .sm-col-3{ + width:25%; } - .sm-col-4 { - width: 33.33333%; + .sm-col-4{ + width:33.33333%; } - .sm-col-5 { - width: 41.66667%; + .sm-col-5{ + width:41.66667%; } - .sm-col-6 { - width: 50%; + .sm-col-6{ + width:50%; } - .sm-col-7 { - width: 58.33333%; + .sm-col-7{ + width:58.33333%; } - .sm-col-8 { - width: 66.66667%; + .sm-col-8{ + width:66.66667%; } - .sm-col-9 { - width: 75%; + .sm-col-9{ + width:75%; } - .sm-col-10 { - width: 83.33333%; + .sm-col-10{ + width:83.33333%; } - .sm-col-11 { - width: 91.66667%; + .sm-col-11{ + width:91.66667%; } - .sm-col-12 { - width: 100%; + .sm-col-12{ + width:100%; } } -@media (min-width: 52em) { +@media (min-width: 52em){ - .md-col { - float: left; - box-sizing: border-box; + .md-col{ + float:left; + box-sizing:border-box; } - .md-col-right { - float: right; - box-sizing: border-box; + .md-col-right{ + float:right; + box-sizing:border-box; } - .md-col-1 { - width: 8.33333%; + .md-col-1{ + width:8.33333%; } - .md-col-2 { - width: 16.66667%; + .md-col-2{ + width:16.66667%; } - .md-col-3 { - width: 25%; + .md-col-3{ + width:25%; } - .md-col-4 { - width: 33.33333%; + .md-col-4{ + width:33.33333%; } - .md-col-5 { - width: 41.66667%; + .md-col-5{ + width:41.66667%; } - .md-col-6 { - width: 50%; + .md-col-6{ + width:50%; } - .md-col-7 { - width: 58.33333%; + .md-col-7{ + width:58.33333%; } - .md-col-8 { - width: 66.66667%; + .md-col-8{ + width:66.66667%; } - .md-col-9 { - width: 75%; + .md-col-9{ + width:75%; } - .md-col-10 { - width: 83.33333%; + .md-col-10{ + width:83.33333%; } - .md-col-11 { - width: 91.66667%; + .md-col-11{ + width:91.66667%; } - .md-col-12 { - width: 100%; + .md-col-12{ + width:100%; } } -@media (min-width: 64em) { +@media (min-width: 64em){ - .lg-col { - float: left; - box-sizing: border-box; + .lg-col{ + float:left; + box-sizing:border-box; } - .lg-col-right { - float: right; - box-sizing: border-box; + .lg-col-right{ + float:right; + box-sizing:border-box; } - .lg-col-1 { - width: 8.33333%; + .lg-col-1{ + width:8.33333%; } - .lg-col-2 { - width: 16.66667%; + .lg-col-2{ + width:16.66667%; } - .lg-col-3 { - width: 25%; + .lg-col-3{ + width:25%; } - .lg-col-4 { - width: 33.33333%; + .lg-col-4{ + width:33.33333%; } - .lg-col-5 { - width: 41.66667%; + .lg-col-5{ + width:41.66667%; } - .lg-col-6 { - width: 50%; + .lg-col-6{ + width:50%; } - .lg-col-7 { - width: 58.33333%; + .lg-col-7{ + width:58.33333%; } - .lg-col-8 { - width: 66.66667%; + .lg-col-8{ + width:66.66667%; } - .lg-col-9 { - width: 75%; + .lg-col-9{ + width:75%; } - .lg-col-10 { - width: 83.33333%; + .lg-col-10{ + width:83.33333%; } - .lg-col-11 { - width: 91.66667%; + .lg-col-11{ + width:91.66667%; } - .lg-col-12 { - width: 100%; + .lg-col-12{ + width:100%; } } +.flex{ display:-webkit-box; display:-webkit-flex; display:-ms-flexbox; display:flex } -.flex { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex } - -.flex-column { -webkit-box-orient: vertical; -webkit-box-direction: normal; -webkit-flex-direction: column; -ms-flex-direction: column; flex-direction: column } -.flex-wrap { -webkit-flex-wrap: wrap; -ms-flex-wrap: wrap; flex-wrap: wrap } - -.flex-center { -webkit-box-align: center; -webkit-align-items: center; -ms-flex-align: center; align-items: center } -.flex-baseline { -webkit-box-align: baseline; -webkit-align-items: baseline; -ms-flex-align: baseline; align-items: baseline } -.flex-stretch { -webkit-box-align: stretch; -webkit-align-items: stretch; -ms-flex-align: stretch; align-items: stretch } -.flex-start { -webkit-box-align: start; -webkit-align-items: flex-start; -ms-flex-align: start; align-items: flex-start } -.flex-end { -webkit-box-align: end; -webkit-align-items: flex-end; -ms-flex-align: end; align-items: flex-end } - -.flex-justify { -webkit-box-pack: justify; -webkit-justify-content: space-between; -ms-flex-pack: justify; justify-content: space-between } - -.flex-auto { - -webkit-box-flex: 1; - -webkit-flex: 1 1 auto; - -ms-flex: 1 1 auto; - flex: 1 1 auto; - min-width: 0; - min-height: 0; -} -.flex-grow { -webkit-box-flex: 1; -webkit-flex: 1 0 auto; -ms-flex: 1 0 auto; flex: 1 0 auto } -.flex-none { -webkit-box-flex: 0; -webkit-flex: none; -ms-flex: none; flex: none } - -.flex-first { -webkit-box-ordinal-group: 0; -webkit-order: -1; -ms-flex-order: -1; order: -1 } -.flex-last { -webkit-box-ordinal-group: 100000; -webkit-order: 99999; -ms-flex-order: 99999; order: 99999 } -@media (min-width: 40em) { - .sm-flex { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex } -} -@media (min-width: 52em) { - .md-flex { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex } -} -@media (min-width: 64em) { - .lg-flex { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex } +@media (min-width: 40em){ + .sm-flex{ display:-webkit-box; display:-webkit-flex; display:-ms-flexbox; display:flex } } -.border { - border-style: solid; +@media (min-width: 52em){ + .md-flex{ display:-webkit-box; display:-webkit-flex; display:-ms-flexbox; display:flex } +} + +@media (min-width: 64em){ + .lg-flex{ display:-webkit-box; display:-webkit-flex; display:-ms-flexbox; display:flex } +} + +.flex-column{ -webkit-box-orient:vertical; -webkit-box-direction:normal; -webkit-flex-direction:column; -ms-flex-direction:column; flex-direction:column } +.flex-wrap{ -webkit-flex-wrap:wrap; -ms-flex-wrap:wrap; flex-wrap:wrap } + +.items-start{ -webkit-box-align:start; -webkit-align-items:flex-start; -ms-flex-align:start; -ms-grid-row-align:flex-start; align-items:flex-start } +.items-end{ -webkit-box-align:end; -webkit-align-items:flex-end; -ms-flex-align:end; -ms-grid-row-align:flex-end; align-items:flex-end } +.items-center{ -webkit-box-align:center; -webkit-align-items:center; -ms-flex-align:center; -ms-grid-row-align:center; align-items:center } +.items-baseline{ -webkit-box-align:baseline; -webkit-align-items:baseline; -ms-flex-align:baseline; -ms-grid-row-align:baseline; align-items:baseline } +.items-stretch{ -webkit-box-align:stretch; -webkit-align-items:stretch; -ms-flex-align:stretch; -ms-grid-row-align:stretch; align-items:stretch } + +.self-start{ -webkit-align-self:flex-start; -ms-flex-item-align:start; align-self:flex-start } +.self-end{ -webkit-align-self:flex-end; -ms-flex-item-align:end; align-self:flex-end } +.self-center{ -webkit-align-self:center; -ms-flex-item-align:center; align-self:center } +.self-baseline{ -webkit-align-self:baseline; -ms-flex-item-align:baseline; align-self:baseline } +.self-stretch{ -webkit-align-self:stretch; -ms-flex-item-align:stretch; align-self:stretch } + +.justify-start{ -webkit-box-pack:start; -webkit-justify-content:flex-start; -ms-flex-pack:start; justify-content:flex-start } +.justify-end{ -webkit-box-pack:end; -webkit-justify-content:flex-end; -ms-flex-pack:end; justify-content:flex-end } +.justify-center{ -webkit-box-pack:center; -webkit-justify-content:center; -ms-flex-pack:center; justify-content:center } +.justify-between{ -webkit-box-pack:justify; -webkit-justify-content:space-between; -ms-flex-pack:justify; justify-content:space-between } +.justify-around{ -webkit-justify-content:space-around; -ms-flex-pack:distribute; justify-content:space-around } + +.content-start{ -webkit-align-content:flex-start; -ms-flex-line-pack:start; align-content:flex-start } +.content-end{ -webkit-align-content:flex-end; -ms-flex-line-pack:end; align-content:flex-end } +.content-center{ -webkit-align-content:center; -ms-flex-line-pack:center; align-content:center } +.content-between{ -webkit-align-content:space-between; -ms-flex-line-pack:justify; align-content:space-between } +.content-around{ -webkit-align-content:space-around; -ms-flex-line-pack:distribute; align-content:space-around } +.content-stretch{ -webkit-align-content:stretch; -ms-flex-line-pack:stretch; align-content:stretch } +.flex-auto{ + -webkit-box-flex:1; + -webkit-flex:1 1 auto; + -ms-flex:1 1 auto; + flex:1 1 auto; + min-width:0; + min-height:0; +} +.flex-none{ -webkit-box-flex:0; -webkit-flex:none; -ms-flex:none; flex:none } + +.order-0{ -webkit-box-ordinal-group:1; -webkit-order:0; -ms-flex-order:0; order:0 } +.order-1{ -webkit-box-ordinal-group:2; -webkit-order:1; -ms-flex-order:1; order:1 } +.order-2{ -webkit-box-ordinal-group:3; -webkit-order:2; -ms-flex-order:2; order:2 } +.order-3{ -webkit-box-ordinal-group:4; -webkit-order:3; -ms-flex-order:3; order:3 } +.order-last{ -webkit-box-ordinal-group:100000; -webkit-order:99999; -ms-flex-order:99999; order:99999 } + +.relative{ position:relative } +.absolute{ position:absolute } +.fixed{ position:fixed } + +.top-0{ top:0 } +.right-0{ right:0 } +.bottom-0{ bottom:0 } +.left-0{ left:0 } + +.z1{ z-index: 1 } +.z2{ z-index: 2 } +.z3{ z-index: 3 } +.z4{ z-index: 4 } + +.border{ + border-style:solid; border-width: 1px; - border-color: rgba(0,0,0,.125); } -.border-top { - border-top-style: solid; +.border-top{ + border-top-style:solid; border-top-width: 1px; - border-top-color: rgba(0,0,0,.125); } -.border-right { - border-right-style: solid; +.border-right{ + border-right-style:solid; border-right-width: 1px; - border-right-color: rgba(0,0,0,.125); } -.border-bottom { - border-bottom-style: solid; +.border-bottom{ + border-bottom-style:solid; border-bottom-width: 1px; - border-bottom-color: rgba(0,0,0,.125); } -.border-left { - border-left-style: solid; +.border-left{ + border-left-style:solid; border-left-width: 1px; - border-left-color: rgba(0,0,0,.125); } -.border-none { border: 0 } +.border-none{ border:0 } -.rounded { border-radius: 3px } -.circle { border-radius: 50% } +.rounded{ border-radius: 3px } +.circle{ border-radius:50% } -.rounded-top { border-radius: 3px 3px 0 0 } -.rounded-right { border-radius: 0 3px 3px 0 } -.rounded-bottom { border-radius: 0 0 3px 3px } -.rounded-left { border-radius: 3px 0 0 3px } +.rounded-top{ border-radius: 3px 3px 0 0 } +.rounded-right{ border-radius: 0 3px 3px 0 } +.rounded-bottom{ border-radius: 0 0 3px 3px } +.rounded-left{ border-radius: 3px 0 0 3px } -.not-rounded { border-radius: 0 } +.not-rounded{ border-radius:0 } -.black { color: #111 } -.gray { color: #aaa } -.silver { color: #ddd } -.white { color: #fff } +.hide{ + position:absolute !important; + height:1px; + width:1px; + overflow:hidden; + clip:rect(1px, 1px, 1px, 1px); +} -.aqua { color: #7fdbff } -.blue { color: #0074d9 } -.navy { color: #001f3f } -.teal { color: #39cccc } -.green { color: #2ecc40 } -.olive { color: #3d9970 } -.lime { color: #01ff70 } +@media (max-width: 40em){ + .xs-hide{ display:none !important } +} -.yellow { color: #ffdc00 } -.orange { color: #ff851b } -.red { color: #ff4136 } -.fuchsia { color: #f012be } -.purple { color: #b10dc9 } -.maroon { color: #85144b } +@media (min-width: 40em) and (max-width: 52em){ + .sm-hide{ display:none !important } +} -.color-inherit { color: inherit } -.muted { opacity: .5 } +@media (min-width: 52em) and (max-width: 64em){ + .md-hide{ display:none !important } +} -.bg-black { background-color: #111 } -.bg-gray { background-color: #aaa } -.bg-silver { background-color: #ddd } -.bg-white { background-color: #fff } - -.bg-aqua { background-color: #7fdbff } -.bg-blue { background-color: #0074d9 } -.bg-navy { background-color: #001f3f } -.bg-teal { background-color: #39cccc } -.bg-green { background-color: #2ecc40 } -.bg-olive { background-color: #3d9970 } -.bg-lime { background-color: #01ff70 } - -.bg-yellow { background-color: #ffdc00 } -.bg-orange { background-color: #ff851b } -.bg-red { background-color: #ff4136 } -.bg-fuchsia { background-color: #f012be } -.bg-purple { background-color: #b10dc9 } -.bg-maroon { background-color: #85144b } - -.bg-darken-1 { background-color: rgba(0,0,0,.0625) } -.bg-darken-2 { background-color: rgba(0,0,0,.125) } -.bg-darken-3 { background-color: rgba(0,0,0,.25) } -.bg-darken-4 { background-color: rgba(0,0,0,.5) } - -.bg-lighten-1 { background-color: rgba(255,255,255,.0625) } -.bg-lighten-2 { background-color: rgba(255,255,255,.125) } -.bg-lighten-3 { background-color: rgba(255,255,255,.25) } -.bg-lighten-4 { background-color: rgba(255,255,255,.5) } +@media (min-width: 64em){ + .lg-hide{ display:none !important } +} +.display-none{ display:none !important } diff --git a/docs/assets/fonts/EOT/SourceCodePro-Bold.eot b/docs/assets/fonts/EOT/SourceCodePro-Bold.eot new file mode 100755 index 0000000..d24cc39 Binary files /dev/null and b/docs/assets/fonts/EOT/SourceCodePro-Bold.eot differ diff --git a/docs/assets/fonts/EOT/SourceCodePro-Regular.eot b/docs/assets/fonts/EOT/SourceCodePro-Regular.eot new file mode 100755 index 0000000..09e9473 Binary files /dev/null and b/docs/assets/fonts/EOT/SourceCodePro-Regular.eot differ diff --git a/docs/assets/fonts/LICENSE.txt b/docs/assets/fonts/LICENSE.txt new file mode 100755 index 0000000..d154618 --- /dev/null +++ b/docs/assets/fonts/LICENSE.txt @@ -0,0 +1,93 @@ +Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries. + +This Font Software is licensed under the SIL Open Font License, Version 1.1. + +This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/docs/assets/fonts/OTF/SourceCodePro-Bold.otf b/docs/assets/fonts/OTF/SourceCodePro-Bold.otf new file mode 100755 index 0000000..f4e576c Binary files /dev/null and b/docs/assets/fonts/OTF/SourceCodePro-Bold.otf differ diff --git a/docs/assets/fonts/OTF/SourceCodePro-Regular.otf b/docs/assets/fonts/OTF/SourceCodePro-Regular.otf new file mode 100755 index 0000000..4e3b9d0 Binary files /dev/null and b/docs/assets/fonts/OTF/SourceCodePro-Regular.otf differ diff --git a/docs/assets/fonts/TTF/SourceCodePro-Bold.ttf b/docs/assets/fonts/TTF/SourceCodePro-Bold.ttf new file mode 100755 index 0000000..e0c576f Binary files /dev/null and b/docs/assets/fonts/TTF/SourceCodePro-Bold.ttf differ diff --git a/docs/assets/fonts/TTF/SourceCodePro-Regular.ttf b/docs/assets/fonts/TTF/SourceCodePro-Regular.ttf new file mode 100755 index 0000000..437f472 Binary files /dev/null and b/docs/assets/fonts/TTF/SourceCodePro-Regular.ttf differ diff --git a/docs/assets/fonts/WOFF/OTF/SourceCodePro-Bold.otf.woff b/docs/assets/fonts/WOFF/OTF/SourceCodePro-Bold.otf.woff new file mode 100755 index 0000000..cf96099 Binary files /dev/null and b/docs/assets/fonts/WOFF/OTF/SourceCodePro-Bold.otf.woff differ diff --git a/docs/assets/fonts/WOFF/OTF/SourceCodePro-Regular.otf.woff b/docs/assets/fonts/WOFF/OTF/SourceCodePro-Regular.otf.woff new file mode 100755 index 0000000..395436e Binary files /dev/null and b/docs/assets/fonts/WOFF/OTF/SourceCodePro-Regular.otf.woff differ diff --git a/docs/assets/fonts/WOFF/TTF/SourceCodePro-Bold.ttf.woff b/docs/assets/fonts/WOFF/TTF/SourceCodePro-Bold.ttf.woff new file mode 100755 index 0000000..c65ba84 Binary files /dev/null and b/docs/assets/fonts/WOFF/TTF/SourceCodePro-Bold.ttf.woff differ diff --git a/docs/assets/fonts/WOFF/TTF/SourceCodePro-Regular.ttf.woff b/docs/assets/fonts/WOFF/TTF/SourceCodePro-Regular.ttf.woff new file mode 100755 index 0000000..0af792a Binary files /dev/null and b/docs/assets/fonts/WOFF/TTF/SourceCodePro-Regular.ttf.woff differ diff --git a/docs/assets/fonts/WOFF2/OTF/SourceCodePro-Bold.otf.woff2 b/docs/assets/fonts/WOFF2/OTF/SourceCodePro-Bold.otf.woff2 new file mode 100755 index 0000000..cbe3835 Binary files /dev/null and b/docs/assets/fonts/WOFF2/OTF/SourceCodePro-Bold.otf.woff2 differ diff --git a/docs/assets/fonts/WOFF2/OTF/SourceCodePro-Regular.otf.woff2 b/docs/assets/fonts/WOFF2/OTF/SourceCodePro-Regular.otf.woff2 new file mode 100755 index 0000000..65cd591 Binary files /dev/null and b/docs/assets/fonts/WOFF2/OTF/SourceCodePro-Regular.otf.woff2 differ diff --git a/docs/assets/fonts/WOFF2/TTF/SourceCodePro-Bold.ttf.woff2 b/docs/assets/fonts/WOFF2/TTF/SourceCodePro-Bold.ttf.woff2 new file mode 100755 index 0000000..b78d523 Binary files /dev/null and b/docs/assets/fonts/WOFF2/TTF/SourceCodePro-Bold.ttf.woff2 differ diff --git a/docs/assets/fonts/WOFF2/TTF/SourceCodePro-Regular.ttf.woff2 b/docs/assets/fonts/WOFF2/TTF/SourceCodePro-Regular.ttf.woff2 new file mode 100755 index 0000000..18d2199 Binary files /dev/null and b/docs/assets/fonts/WOFF2/TTF/SourceCodePro-Regular.ttf.woff2 differ diff --git a/docs/assets/fonts/source-code-pro.css b/docs/assets/fonts/source-code-pro.css new file mode 100755 index 0000000..3abb4f0 --- /dev/null +++ b/docs/assets/fonts/source-code-pro.css @@ -0,0 +1,23 @@ +@font-face{ + font-family: 'Source Code Pro'; + font-weight: 400; + font-style: normal; + font-stretch: normal; + src: url('EOT/SourceCodePro-Regular.eot') format('embedded-opentype'), + url('WOFF2/TTF/SourceCodePro-Regular.ttf.woff2') format('woff2'), + url('WOFF/OTF/SourceCodePro-Regular.otf.woff') format('woff'), + url('OTF/SourceCodePro-Regular.otf') format('opentype'), + url('TTF/SourceCodePro-Regular.ttf') format('truetype'); +} + +@font-face{ + font-family: 'Source Code Pro'; + font-weight: 700; + font-style: normal; + font-stretch: normal; + src: url('EOT/SourceCodePro-Bold.eot') format('embedded-opentype'), + url('WOFF2/TTF/SourceCodePro-Bold.ttf.woff2') format('woff2'), + url('WOFF/OTF/SourceCodePro-Bold.otf.woff') format('woff'), + url('OTF/SourceCodePro-Bold.otf') format('opentype'), + url('TTF/SourceCodePro-Bold.ttf') format('truetype'); +} diff --git a/docs/assets/site.js b/docs/assets/site.js index 2cb0fb3..559c65e 100644 --- a/docs/assets/site.js +++ b/docs/assets/site.js @@ -2,39 +2,107 @@ // add anchor links to headers anchors.options.placement = 'left'; -anchors.add().remove('.no-anchor'); +anchors.add('h3'); // Filter UI -var tocElements = document.getElementById('toc').getElementsByTagName('a'); -document.getElementById('filter-input').addEventListener('keyup', function(e) { +var tocElements = document.getElementById('toc') + .getElementsByTagName('li'); - var i, element; +document.getElementById('filter-input') + .addEventListener('keyup', function (e) { - // enter key - if (e.keyCode === 13) { - // go to the first displayed item in the toc - for (i = 0; i < tocElements.length; i++) { - element = tocElements[i]; - if (!element.classList.contains('hide')) { - location.replace(element.href); - return e.preventDefault(); + var i, element, children; + + // enter key + if (e.keyCode === 13) { + // go to the first displayed item in the toc + for (i = 0; i < tocElements.length; i++) { + element = tocElements[i]; + if (!element.classList.contains('display-none')) { + location.replace(element.firstChild.href); + return e.preventDefault(); + } } } - } - var match = function() { return true; }, - value = this.value.toLowerCase(); + var match = function () { + return true; + }; - if (!value.match(/^\s*$/)) { - match = function(text) { return text.toLowerCase().indexOf(value) !== -1; }; - } + var value = this.value.toLowerCase(); - for (i = 0; i < tocElements.length; i++) { - element = tocElements[i]; - if (match(element.innerHTML)) { - element.classList.remove('hide'); - } else { - element.classList.add('hide'); + if (!value.match(/^\s*$/)) { + match = function (element) { + return element.firstChild.innerHTML.toLowerCase().indexOf(value) !== -1; + }; } + + for (i = 0; i < tocElements.length; i++) { + element = tocElements[i]; + children = Array.from(element.getElementsByTagName('li')); + if (match(element) || children.some(match)) { + element.classList.remove('display-none'); + } else { + element.classList.add('display-none'); + } + } + }); + +var toggles = document.getElementsByClassName('toggle-step-sibling'); +for (var i = 0; i < toggles.length; i++) { + toggles[i].addEventListener('click', toggleStepSibling); +} + +function toggleStepSibling() { + var stepSibling = this.parentNode.parentNode.parentNode.getElementsByClassName('toggle-target')[0]; + var klass = 'display-none'; + if (stepSibling.classList.contains(klass)) { + stepSibling.classList.remove(klass); + stepSibling.innerHTML = 'â–¾'; + } else { + stepSibling.classList.add(klass); + stepSibling.innerHTML = 'â–¸'; } +} + +var items = document.getElementsByClassName('toggle-sibling'); +for (var j = 0; j < items.length; j++) { + items[j].addEventListener('click', toggleSibling); +} + +function toggleSibling() { + var stepSibling = this.parentNode.getElementsByClassName('toggle-target')[0]; + var icon = this.getElementsByClassName('icon')[0]; + var klass = 'display-none'; + if (stepSibling.classList.contains(klass)) { + stepSibling.classList.remove(klass); + icon.innerHTML = 'â–¾'; + } else { + stepSibling.classList.add(klass); + icon.innerHTML = 'â–¸'; + } +} + +function showHashTarget(targetId) { + var hashTarget = document.getElementById(targetId); + // new target is hidden + if (hashTarget && hashTarget.offsetHeight === 0 && + hashTarget.parentNode.parentNode.classList.contains('display-none')) { + hashTarget.parentNode.parentNode.classList.remove('display-none'); + } +} + +window.addEventListener('hashchange', function() { + showHashTarget(location.hash.substring(1)); }); + +showHashTarget(location.hash.substring(1)); + +var toclinks = document.getElementsByClassName('pre-open'); +for (var k = 0; k < toclinks.length; k++) { + toclinks[k].addEventListener('mousedown', preOpen, false); +} + +function preOpen() { + showHashTarget(this.hash.substring(1)); +} diff --git a/docs/assets/style.css b/docs/assets/style.css index d32c748..d7e56e0 100644 --- a/docs/assets/style.css +++ b/docs/assets/style.css @@ -1,68 +1,55 @@ -.documentation a { +.documentation { + font-family: Helvetica, sans-serif; + color: #666; + line-height: 1.5; + background: #f5f5f5; +} + +.black { + color: #666; +} + +.bg-white { + background-color: #fff; +} + +h4 { + margin: 20px 0 10px 0; +} + +.documentation h3 { + color: #000; +} + +.border-bottom { + border-color: #ddd; +} + +a { color: #1184CE; + text-decoration: none; } -.documentation .suppress-p-margin p { - margin:0; +.documentation a[href]:hover { + text-decoration: underline; } -.force-inline, .force-inline p { - display: inline; - color: #222; +a:hover { + cursor: pointer; } -.container-small { - max-width: 58rem; - margin-left: auto; - margin-right: auto; +.py1-ul li { + padding: 5px 0; } .max-height-100 { max-height: 100%; } -.fade { - opacity:0.50; -} - -.button-indent { - padding: .25rem 1.5rem; - font-size: 90%; -} - -.section-indent { - border-left: 2px solid #eee; -} - -.bg-cloudy { - background: #fafafa; -} - -.force-inline * { - display:inline; -} - section:target h3 { font-weight:700; } -.documentation, -.documentation h1, -.documentation h2, -.documentation h3, -.documentation h4, -.documentation h5, -.documentation h6 { - font-family: 'Source Sans Pro', Helvetica, sans-serif; -} - -.documentation pre, -.documentation code, -.documentation samp { - font-family: 'Source Code Pro', monospace; - font-size: 90%; -} - .documentation td, .documentation th { padding: .25rem .25rem; @@ -75,12 +62,9 @@ h4:hover .anchorjs-link { opacity: 1; } -.collapsible .collapser { - display:none; -} - -.collapsible:target .collapser { - display: block; +.fix-3 { + width: 25%; + max-width: 244px; } .fix-3 { @@ -93,3 +77,60 @@ h4:hover .anchorjs-link { margin-left: 25%; } } + +.pre, pre, code, .code { + font-family: Source Code Pro,Menlo,Consolas,Liberation Mono,monospace; + font-size: 14px; +} + +.fill-light { + background: #F9F9F9; +} + +.width2 { + width: 1rem; +} + +.input { + font-family: inherit; + display: block; + width: 100%; + height: 2rem; + padding: .5rem; + margin-bottom: 1rem; + border: 1px solid #ccc; + font-size: .875rem; + border-radius: 3px; + box-sizing: border-box; +} + +table { + border-collapse: collapse; +} + +.prose table th, +.prose table td { + text-align: left; + padding:8px; + border:1px solid #ddd; +} + +.prose table th:nth-child(1) { border-right: none; } +.prose table th:nth-child(2) { border-left: none; } + +.prose table { + border:1px solid #ddd; +} + +.prose-big { + font-size: 18px; + line-height: 30px; +} + +.quiet { + opacity: 0.7; +} + +.minishadow { + box-shadow: 2px 2px 10px #f3f3f3; +} diff --git a/docs/index.html b/docs/index.html index 9d2aa12..aec3a2a 100644 --- a/docs/index.html +++ b/docs/index.html @@ -4,2174 +4,3702 @@ | Documentation - -
+
-
+

- - NodeQuery - - - #constructor + +
+
-
-
-

- NodeQuery(config) -

+ + +
+ + +
+ +

+ NodeQuery +

+ + +
+ +

Class for connection management

-

Parameters

-
    -
  • object config - : -
    -

    connection parameters

    + +
    new NodeQuery(config: object)
    + + + + + + + + + + +
    Parameters
    +
    + +
    +
    + config (object) connection parameters
    -
  • -
-

Instance members

-
- - - #constructor(config) - -
-

Constructor

- -
-
-
-
-

- constructor(config) -

-

Constructor

- -

Parameters

-
    -
  • object config - : -
    -

    connection parameters

    - -
    -
  • -
-

Examples

-
let nodeQuery = require('ci-node-query')({
-            	driver: 'mysql',
-            	connection: {
-            		host: 'localhost',
-            		user: 'root',
-            		password: '',
-            		database: 'mysql'
-            	}
-            });
let nodeQuery = require('ci-node-query')({
-            	driver: 'sqlite',
-            	connection: ':memory:'
-            });
-
-
+
- - -
-
-

- getQuery -

-

Return an existing query builder instance

- -

Returns

- QueryBuilder - : -
-

The Query Builder object

- -
-
-
+ + + + + + + + + + + +
Instance Members
+
+ +
+
+
+ â–¸ + constructor(config)
+
+
-

- QueryBuilder(Driver, Adapter) -

+ +
+
+ +
+
+
+ â–¸ + getQuery() +
+
+ +
+ +
+ + + + +
+ + + + +
+ + +
+ +

+ QueryBuilder +

+ + +
+ +

Main object that builds SQL queries.

-

Parameters

-
    -
  • Driver Driver - : -
    -

    The syntax driver for the database

    + +
    new QueryBuilder(Driver: Driver, Adapter: Adapter)
    + + +

    + Extends + + QueryBuilderBase + +

    + + + + + + + + + +
    Parameters
    +
    + +
    +
    + Driver (Driver) The syntax driver for the database
    -
  • -
  • Adapter Adapter - : -
    -

    The database module adapter for running queries

    - -
    -
  • -
-

Instance members

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

Run the generated delete query

- -
-
-
-
-

- 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 or Promise - : -
-

If no callback is passed, a promise is returned

- -
-
-
+
-
- - - #end - -
-

Closes the database connection for the current adapter

+ +
+
+ Adapter (Adapter) The database module adapter for running queries -
-
-
-
-

- end -

-

Closes the database connection for the current adapter

- -

Returns

- void - -
- -
-
+
- - -
-
-

- 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

- -
-

Examples

-
query.from('tableName');
query.from('tableName t'); // Select the table with an alias
-
-
-
-
- - - #get([table], [limit], [offset], [callback]) - -
-

Get the results of the compiled query

+ -
-
-
-
-

- 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 or Promise - : -
-

If no callback is passed, a promise is returned

- -
-

Examples

-
query.get('table_name').then(promiseCallback); // Get all the rows in the table
query.get('table_name', 5, callback); // Get 5 rows from the table
query.get(callback); // Get the results of a query generated with other methods
-
-
-
-
- - - #getCompiledDelete(table, [reset]) - -
-

Return generated delete query SQL

+ -
-
-
-
-

- 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

+ -
-
-
-
-

- 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

+ -
-
-
-
-

- 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

+ -
-
-
-
-

- 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

- -
-
-
+ +
Instance Members
+
+ +
+
+
+ â–¸ + query(sql, [params])
-
- - - #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

+

Run an arbitrary sql query. Run as a prepared statement.

-
-
-
-
-

- 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]) - - - -
-
-

- 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);
query.insertBatch('foo',[{id:1,val:'bar'},{id:2,val:'baz'}])
-            .then(promiseCallback);
-
-
-
-
- - - #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

+ +
Parameters
+
+ +
+
+ sql (string) The sql to execute -
- -
-
-

- 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

+ +
+
+ params ([Array]) The query parameters -
- -
-
-

- 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, -prefixed with 'OR'

+ -
-
-
-
-

- 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

+ + +
Returns
+
Promise: + Promise with result of query -
- -
-
-

- 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

+ + + -
-
-
-
-

- 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, -prefixed with 'OR NOT'

+ -
-
-
-
-

- 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, values) - -
-

Set a 'or where in' clause

+ -
-
-
-
-

- orWhereIn(key, values) -

-

Set a 'or where in' clause

- -

Parameters

-
    -
  • String key - : -
    -

    the field to search

    - -
    -
  • -
  • Array values - : -
    -

    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, values) - -
-

Set a 'or where not in' clause

- -
-
-
-
-

- orWhereNotIn(key, values) -

-

Set a 'or where not in' clause

- -

Parameters

-
    -
  • String key - : -
    -

    the field to search

    - -
    -
  • -
  • Array values - : -
    -

    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

- -
-
-
-
-
- - - #query(sql, [params], [callback]) - -
-

Run an arbitrary sql query. Run as a prepared statement.

- -
-
-
-
-

- query(sql, [params], [callback]) -

-

Run an arbitrary sql query. Run as a prepared statement.

- -

Parameters

-
    -
  • string sql - : -
    -

    The sql to execute

    - -
    -
  • -
  • [array] params - : -
    -

    The query parameters

    - -
    -
  • -
  • [function] callback - : -
    -

    Optional callback

    - -
    -
  • -
-

Returns

- void or Promise - : -
-

Returns a promise if no callback is supplied

- -
-
-
-
-
- - - #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

- -
-

Examples

-
query.select('foo, bar'); // Select multiple fields with a string
query.select(['foo', 'bar']); // Select multiple fileds with an array
-
-
-
-
- - - #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

- -
-

Examples

-
query.set('foo', 'bar'); // Set a key, value pair
query.set({foo:'bar'}); // Set with an object
-
-
-
-
- - - #truncate(table, [callback]) - -
-

Empties the selected database table

- -
-
-
-
-

- truncate(table, [callback]) -

-

Empties the selected database table

- -

Parameters

-
    -
  • string table - : -
    -

    the name of the table to truncate

    - -
    -
  • -
  • [function] callback - : -
    -

    Optional callback

    - -
    -
  • -
-

Returns

- void or Promise - : -
-

Returns a promise if no callback is supplied

- -
-
-
-
-
- - - #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, values) - -
-

Set a 'where in' clause

- -
-
-
-
-

- whereIn(key, values) -

-

Set a 'where in' clause

- -

Parameters

-
    -
  • String key - : -
    -

    the field to search

    - -
    -
  • -
  • Array values - : -
    -

    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, values) - -
-

Set a 'where not in' clause

- -
-
-
-
-

- whereNotIn(key, values) -

-

Set a 'where not in' clause

- -

Parameters

-
    -
  • String key - : -
    -

    the field to search

    - -
    -
  • -
  • Array values - : -
    -

    the array of items to search in

    - -
    -
  • -
-

Returns

- QueryBuilder - : -
-

The Query Builder object, for chaining

- -
-
-
-
+
-
-

- Result(rows, columns) -

+ +
+
+ +
+
+
+ â–¸ + resetQuery() +
+
+ +
+ +
+
+
+ â–¸ + truncate(table) +
+
+ +
+ +
+
+
+ â–¸ + end() +
+
+ +
+ +
+
+
+ â–¸ + select(fields) +
+
+ +
+ +
+
+
+ â–¸ + from(tableName) +
+
+ +
+ +
+
+
+ â–¸ + like(field, val, [pos]) +
+
+ +
+ +
+
+
+ â–¸ + notLike(field, val, [pos]) +
+
+ +
+ +
+
+
+ â–¸ + orLike(field, val, [pos]) +
+
+ +
+ +
+
+
+ â–¸ + orNotLike(field, val, [pos]) +
+
+ +
+ +
+
+
+ â–¸ + having(key, [val]) +
+
+ +
+ +
+
+
+ â–¸ + orHaving(key, [val]) +
+
+ +
+ +
+
+
+ â–¸ + where(key, [val]) +
+
+ +
+ +
+
+
+ â–¸ + orWhere(key, [val]) +
+
+ +
+ +
+
+
+ â–¸ + whereIsNull(field) +
+
+ +
+ +
+
+
+ â–¸ + whereIsNotNull(field) +
+
+ +
+ +
+
+
+ â–¸ + orWhereIsNull(field) +
+
+ +
+ +
+
+
+ â–¸ + orWhereIsNotNull(field) +
+
+ +
+ +
+
+
+ â–¸ + whereIn(key, values) +
+
+ +
+ +
+
+
+ â–¸ + orWhereIn(key, values) +
+
+ +
+ +
+
+
+ â–¸ + whereNotIn(key, values) +
+
+ +
+ +
+
+
+ â–¸ + orWhereNotIn(key, values) +
+
+ +
+ +
+
+
+ â–¸ + set(key, [val]) +
+
+ +
+ +
+
+
+ â–¸ + join(table, cond, [type]) +
+
+ +
+ +
+
+
+ â–¸ + groupBy(field) +
+
+ +
+ +
+
+
+ â–¸ + orderBy(field, [type]) +
+
+ +
+ +
+
+
+ â–¸ + limit(limit, [offset]) +
+
+ +
+ +
+
+
+ â–¸ + groupStart() +
+
+ +
+ +
+
+
+ â–¸ + orGroupStart() +
+
+ +
+ +
+
+
+ â–¸ + orNotGroupStart() +
+
+ +
+ +
+
+
+ â–¸ + groupEnd() +
+
+ +
+ +
+
+
+ â–¸ + get([table], [limit], [offset]) +
+
+ +
+ +
+
+
+ â–¸ + insert(table, [data]) +
+
+ +
+ +
+
+
+ â–¸ + insertBatch(table, data) +
+
+ +
+ +
+
+
+ â–¸ + update(table, [data]) +
+
+ +
+ +
+
+
+ â–¸ + delete(table, [where]) +
+
+ +
+ +
+
+
+ â–¸ + getCompiledSelect([table], [reset]) +
+
+ +
+ +
+
+
+ â–¸ + getCompiledInsert(table, [reset]) +
+
+ +
+ +
+
+
+ â–¸ + getCompiledUpdate(table, [reset]) +
+
+ +
+ +
+
+
+ â–¸ + getCompiledDelete(table, [reset]) +
+
+ +
+ +
+ + + + + + + + + +
+ + +
+ +

+ Result +

+ + +
+ +

Query result object

-

Parameters

-
    -
  • Array rows - : -
    -

    the data rows of the result

    + +
    new Result(rows: Array, columns: Array)
    + + + + + + + + + + +
    Parameters
    +
    + +
    +
    + rows (Array) the data rows of the result
    -
  • -
  • Array columns - : -
    -

    the column names in the result

    - -
    -
  • -
-

Instance members

-
- - - #columnCount - -
-

Get the number of columns returned by the query

- -
-
-
-
-

- columnCount -

-

Get the number of columns returned by the query

- -

Returns

- Number - : -
-

the number of columns in the result

- -
-
-
+
-
- - - #rowCount - -
-

Get the number of rows returned by the query

+ +
+
+ columns (Array) the column names in the result -
- -
-
-

- rowCount -

-

Get the number of rows returned by the query

- -

Returns

- Number - : -
-

the number of rows in the result

- -
-
+
+ +
+ + + + + + + + + + + + + +
Instance Members
+
+ +
+
+
+ â–¸ + rowCount() +
+
+ + +
+
+ +
+
+
+ â–¸ + columnCount()
+
+ +
+ +
+ + + + +
+ + +
diff --git a/gulpfile.js b/gulpfile.js deleted file mode 100644 index f44752f..0000000 --- a/gulpfile.js +++ /dev/null @@ -1,87 +0,0 @@ -'use strict'; - -const documentation = require('gulp-documentation'), - eslint = require('gulp-eslint'), - gulp = require('gulp'), - istanbul = require('gulp-istanbul'), - jscs = require('gulp-jscs'), - mocha = require('gulp-mocha'), - pipe = require('gulp-pipe'), - sloc = require('gulp-sloc'); - -const SRC_FILES = ['lib/**/*.js']; -const TEST_FILES = [ - 'test/*_test.js', - 'test/adapters/*_test.js' -]; - -const MOCHA_OPTIONS = { - ui: 'tdd', - bail: true, - reporter: 'dot', - timeout: 10000, -}; - -gulp.task('lint', () => { - pipe(gulp.src(SRC_FILES), [ - eslint(), - eslint.format(), - eslint.failAfterError() - ]); - pipe(gulp.src(SRC_FILES), [ - jscs(), - jscs.reporter() - ]); -}); - -gulp.task('lint-tests', ['lint'], () => { - pipe(gulp.src(['test/**/*.js']), [ - eslint(), - eslint.format(), - eslint.failAfterError() - ]); - pipe(gulp.src(['test/**/*.js']), [ - jscs(), - jscs.reporter() - ]); -}); - -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/*.js']) - .pipe(documentation({format: 'html'})) - .pipe(gulp.dest('docs')); - gulp.src(['lib/*.js']) - .pipe(documentation({format: 'md'})) - .pipe(gulp.dest('.')); -}); - -gulp.task('mocha', ['lint-tests', 'sloc'], () => { - return gulp.src(TEST_FILES) - .pipe(mocha(MOCHA_OPTIONS)) - .once('error', () => { - process.exit(1); - }) - .once('end', () => { - process.exit(); - }); -}); - -gulp.task('test', ['test-sloc', 'lint-tests'], function(cb) { - return pipe(gulp.src(SRC_FILES), [ - istanbul(), - istanbul.hookRequire() - ]).on('finish', () => { - pipe(gulp.src(TEST_FILES), [ - mocha(MOCHA_OPTIONS), - istanbul.writeReports({ - dir: './coverage', - reporters: ['clover', 'lcov', 'lcovonly', 'html', 'text'], - }), - ]); - }); -}); - -gulp.task('default', ['lint', 'sloc', 'docs', 'test']); \ No newline at end of file diff --git a/lib/Adapter.js b/lib/Adapter.js index 8ac77ac..e0c36ed 100755 --- a/lib/Adapter.js +++ b/lib/Adapter.js @@ -1,20 +1,19 @@ - 'use strict'; /** * Class that wraps database connection libraries * * @private - * @param {Object} instance - The connection object + * @param {Promise} instance - The connection object */ class Adapter { /** * Invoke an adapter * * @constructor - * @param {Object} instance - The connection object + * @param {Promise} instance - Promise holding connection object */ - constructor(instance) { + constructor (instance) { this.instance = instance; } @@ -23,10 +22,9 @@ 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|Promise} - returns a promise if no callback is passed + * @return {Promise} - returns a promise if no callback is passed */ - execute(/*sql, params, callback*/) { + execute (sql, params) { throw new Error('Correct adapter not defined for query execution'); } @@ -36,7 +34,7 @@ class Adapter { * @param {*} originalResult - the original result object from the driver * @return {Result} - the new result object */ - transformResult(originalResult) { + transformResult (originalResult) { throw new Error('Result transformer method not defined for current adapter'); } @@ -44,9 +42,9 @@ class Adapter { * Close the current database connection * @return {void} */ - close() { - this.instance.end(); + close () { + this.instance.then(conn => conn.end()); } } -module.exports = Adapter; \ No newline at end of file +module.exports = Adapter; diff --git a/lib/Driver.js b/lib/Driver.js index cac0cd1..058224a 100755 --- a/lib/Driver.js +++ b/lib/Driver.js @@ -7,7 +7,7 @@ const helpers = require('./helpers'); * * @private */ -let Driver = { +const Driver = { identifierStartChar: '"', identifierEndChar: '"', tablePrefix: null, @@ -20,9 +20,9 @@ let Driver = { * @return {String} - The quoted sql fragment * @private */ - _quote(str) { - return (helpers.isString(str) - && ! (str.startsWith(Driver.identifierStartChar) || str.endsWith(Driver.identifierEndChar)) + _quote (str) { + return (helpers.isString(str) && + !(str.startsWith(Driver.identifierStartChar) || str.endsWith(Driver.identifierEndChar)) ) ? `${Driver.identifierStartChar}${str}${Driver.identifierEndChar}` : str; @@ -30,17 +30,16 @@ let Driver = { /** * Set the limit clause - + * @private * @param {String} sql - SQL statement to modify * @param {Number} limit - Maximum number of rows to fetch * @param {Number} [offset] - Number of rows to skip * @return {String} - Modified SQL statement */ - limit(sql, limit, offset) { - sql += ` LIMIT ${limit}`; + limit (sql, limit, offset) { + sql += ` LIMIT ${limit}`; - if (helpers.isNumber(offset)) - { + if (helpers.isNumber(offset)) { sql += ` OFFSET ${offset}`; } @@ -50,10 +49,11 @@ let Driver = { /** * Quote database table name, and set prefix * + * @private * @param {String} table - Table name to quote * @return {String} - Quoted table name */ - quoteTable(table) { + quoteTable (table) { // Quote after prefix return Driver.quoteIdentifiers(table); }, @@ -61,25 +61,24 @@ let Driver = { /** * Use the driver's escape character to quote identifiers * + * @private * @param {String|Array} str - String or array of strings to quote identifiers * @return {String|Array} - Quoted identifier(s) */ - quoteIdentifiers(str) { + quoteIdentifiers (str) { let hiers, raw; let pattern = new RegExp( - `${Driver.identifierStartChar}(` - + '([a-zA-Z0-9_]+)' + '(\((.*?)\))' - + `)${Driver.identifierEndChar}`, 'ig'); + `${Driver.identifierStartChar}(` + + '([a-zA-Z0-9_]+)' + '(((.*?)))' + + `)${Driver.identifierEndChar}`, 'ig'); // Recurse for arrays of identifiiers - if (Array.isArray(str)) - { + if (Array.isArray(str)) { return str.map(Driver.quoteIdentifiers); } // Handle commas - if (str.includes(',')) - { + if (str.includes(',')) { let parts = str.split(',').map(helpers.stringTrim); str = parts.map(Driver.quoteIdentifiers).join(','); } @@ -89,8 +88,7 @@ let Driver = { raw = hiers.join('.'); // Fix functions - if (raw.includes('(') && raw.includes(')')) - { + if (raw.includes('(') && raw.includes(')')) { let funcs = pattern.exec(raw); // Unquote the function @@ -107,10 +105,11 @@ let Driver = { /** * Generate SQL to truncate the passed table * + * @private * @param {String} table - Table to truncate * @return {String} - Truncation SQL */ - truncate(table) { + truncate (table) { let sql = (Driver.hasTruncate) ? 'TRUNCATE ' : 'DELETE FROM '; @@ -123,17 +122,15 @@ let Driver = { /** * Generate SQL to insert a group of rows * + * @private * @param {String} table - The table to insert to * @param {Array} [data] - The array of object containing data to insert * @return {String} - Query and data to insert */ - insertBatch(table, data) { - let vals = [], - fields = Object.keys(data[0]), - sql = '', - params = [], - paramString = '', - paramList = []; + insertBatch (table, data) { + const vals = []; + const fields = Object.keys(data[0]); + let sql = ''; // Get the data values to insert, so they can // be parameterized @@ -150,17 +147,17 @@ let Driver = { sql += `INSERT INTO ${table} (${Driver.quoteIdentifiers(fields).join(',')}) VALUES `; // Create placeholder groups - params = Array(fields.length).fill('?'); - paramString = `(${params.join(',')})`; - paramList = Array(data.length).fill(paramString); + let params = Array(fields.length).fill('?'); + let paramString = `(${params.join(',')})`; + let paramList = Array(data.length).fill(paramString); sql += paramList.join(','); return { sql: sql, - values: vals, + values: vals }; - }, + } }; -module.exports = Driver; \ No newline at end of file +module.exports = Driver; diff --git a/lib/NodeQuery.js b/lib/NodeQuery.js index 4cedd6c..e64d696 100755 --- a/lib/NodeQuery.js +++ b/lib/NodeQuery.js @@ -1,6 +1,5 @@ 'use strict'; -const helpers = require('./helpers'); const QueryBuilder = require('./QueryBuilder'); // Map config driver name to code class name @@ -15,6 +14,8 @@ const dbDriverMap = new Map([ ['pg', 'Pg'], ['sqlite3', 'Sqlite'], ['sqlite', 'Sqlite'], + ['sqlserver', 'MSSQLServer'], + ['mssql', 'MSSQLServer'] ]); /** @@ -23,7 +24,6 @@ const dbDriverMap = new Map([ * @param {object} config - connection parameters */ class NodeQuery { - /** * Constructor * @@ -42,20 +42,20 @@ class NodeQuery { * connection: ':memory:' * }); */ - constructor(config) { + constructor (config) { this.instance = null; - if (config != null) { + if (config !== undefined) { let drivername = dbDriverMap.get(config.driver); - if (! drivername) { + if (!drivername) { throw new Error(`Selected driver (${config.driver}) does not exist!`); } - let driver = require(`./drivers/${drivername}`); - let $adapter = require(`./adapters/${drivername}`); + const driver = require(`./drivers/${drivername}`); + const Adapter = require(`./adapters/${drivername}`); - let adapter = new $adapter(config.connection); + let adapter = Adapter(config); this.instance = new QueryBuilder(driver, adapter); } } @@ -65,7 +65,7 @@ class NodeQuery { * * @return {QueryBuilder} - The Query Builder object */ - getQuery() { + getQuery () { if (this.instance == null) { throw new Error('No Query Builder instance to return'); } @@ -74,4 +74,4 @@ class NodeQuery { } } -module.exports = (config => new NodeQuery(config)); \ No newline at end of file +module.exports = config => new NodeQuery(config); diff --git a/lib/QueryBuilder.js b/lib/QueryBuilder.js index 216ef2c..268fd10 100755 --- a/lib/QueryBuilder.js +++ b/lib/QueryBuilder.js @@ -1,309 +1,16 @@ 'use strict'; -const getArgs = require('getargs'); const helpers = require('./helpers'); -const State = require('./State'); -const QueryParser = require('./QueryParser'); - -class QueryBuilderBase { - /** - * @private - * @constructor - * @param {Driver} Driver - The syntax driver for the database - * @param {Adapter} Adapter - The database module adapter for running queries - */ - constructor(Driver, Adapter) { - this.driver = Driver; - this.adapter = Adapter; - this.parser = new QueryParser(this.driver); - this.state = new State(); - } - - /** - * Complete the sql building based on the type provided - * - * @private - * @param {String} type - Type of SQL query - * @param {String} table - The table to run the query on - * @return {String} - The compiled sql - */ - _compile(type, table) { - // Put together the basic query - let sql = this._compileType(type, table); - - // Set each subClause - ['queryMap', 'groupString', 'orderString', 'havingMap'].forEach(clause => { - let param = this.state[clause]; - - if (! helpers.isScalar(param)) { - Object.keys(param).forEach(part => { - sql += param[part].conjunction + param[part].string; - }); - } else { - sql += param; - } - }); - - // Append the limit, if it exists - if (helpers.isNumber(this.state.limit)) { - sql = this.driver.limit(sql, this.state.limit, this.state.offset); - } - - return sql; - } - - _compileType(type, table) { - let sql = ''; - - switch (type) { - case 'insert': - let params = Array(this.state.setArrayKeys.length).fill('?'); - - sql = `INSERT INTO ${table} (`; - sql += this.state.setArrayKeys.join(','); - sql += `) VALUES (${params.join(',')})`; - break; - - case 'update': - sql = `UPDATE ${table} SET ${this.state.setString}`; - break; - - case 'delete': - sql = `DELETE FROM ${table}`; - break; - - default: - sql = `SELECT * FROM ${this.state.fromString}`; - - // Set the select string - if (this.state.selectString.length > 0) { - // Replace the star with the selected fields - sql = sql.replace('*', this.state.selectString); - } - - break; - } - - return sql; - } - - _like(field, val, pos, like, conj) { - field = this.driver.quoteIdentifiers(field); - - like = `${field} ${like} ?`; - - if (pos === 'before') { - val = `%${val}`; - } else if (pos === 'after') { - val = `${val}%`; - } else { - val = `%${val}%`; - } - - conj = (this.state.queryMap.length < 1) ? ' WHERE ' : ` ${conj} `; - this._appendMap(conj, like, 'like'); - - this.state.whereValues.push(val); - } - - /** - * Append a clause to the query map - * - * @private - * @param {String} conjunction - linking keyword for the clause - * @param {String} string - pre-compiled sql fragment - * @param {String} type - type of sql clause - * @return {void} - */ - _appendMap(conjunction, string, type) { - this.state.queryMap.push({ - type: type, - conjunction: conjunction, - string: string, - }); - } - - /** - * Handle key/value pairs in an object the same way as individual arguments, - * when appending to state - * - * @private - * @return {Array} - modified state array - */ - _mixedSet(/* $letName, $valType, $key, [$val] */) { - const argPattern = '$letName:string, $valType:string, $key:object|string|number, [$val]'; - let args = getArgs(argPattern, arguments); - - let obj = {}; - - if (helpers.isScalar(args.$key) && !helpers.isUndefined(args.$val)) { - // Convert key/val pair to a simple object - obj[args.$key] = args.$val; - } else if (helpers.isScalar(args.$key) && helpers.isUndefined(args.$val)) { - // If just a string for the key, and no value, create a simple object with duplicate key/val - obj[args.$key] = args.$key; - } else { - obj = args.$key; - } - - Object.keys(obj).forEach(k => { - // If a single value for the return - if (['key', 'value'].indexOf(args.$valType) !== -1) { - let pushVal = (args.$valType === 'key') ? k : obj[k]; - this.state[args.$letName].push(pushVal); - } else { - this.state[args.$letName][k] = obj[k]; - } - }); - - return this.state[args.$letName]; - } - - _whereMixedSet(/*key, val*/) { - let args = getArgs('key:string|object, [val]', arguments); - - this.state.whereMap = []; - this.state.rawWhereValues = []; - - this._mixedSet('whereMap', 'both', args.key, args.val); - this._mixedSet('rawWhereValues', 'value', args.key, args.val); - } - - _fixConjunction(conj) { - let lastItem = this.state.queryMap[this.state.queryMap.length - 1]; - let conjunctionList = helpers.arrayPluck(this.state.queryMap, 'conjunction'); - - if (this.state.queryMap.length === 0 || (! helpers.regexInArray(conjunctionList, /^ ?WHERE/i))) { - conj = ' WHERE '; - } else if (lastItem.type === 'groupStart') { - conj = ''; - } else { - conj = ` ${conj} `; - } - - return conj; - } - - _where(key, val, defaultConj) { - // Normalize key and value and insert into this.state.whereMap - this._whereMixedSet(key, val); - - // Parse the where condition to account for operators, - // functions, identifiers, and literal values - this.state = this.parser.parseWhere(this.driver, this.state); - - this.state.whereMap.forEach(clause => { - let conj = this._fixConjunction(defaultConj); - this._appendMap(conj, clause, 'where'); - }); - - this.state.whereMap = {}; - } - - _whereNull(field, stmt, conj) { - field = this.driver.quoteIdentifiers(field); - let item = `${field} ${stmt}`; - - this._appendMap(this._fixConjunction(conj), item, 'whereNull'); - } - - _having(/*key, val, conj*/) { - let args = getArgs('key:string|object, [val]:string|number, [conj]:string', arguments); - args.conj = args.conj || 'AND'; - args.val = args.val || null; - - // Normalize key/val and put in state.whereMap - this._whereMixedSet(args.key, args.val); - - // Parse the having condition to account for operators, - // functions, identifiers, and literal values - this.state = this.parser.parseWhere(this.driver, this.state); - - this.state.whereMap.forEach(clause => { - // Put in the having map - this.state.havingMap.push({ - conjunction: (this.state.havingMap.length > 0) ? ` ${args.conj} ` : ' HAVING ', - string: clause, - }); - }); - - // Clear the where Map - this.state.whereMap = {}; - - } - - _whereIn(/*key, val, inClause, conj*/) { - let args = getArgs('key:string, val:array, inClause:string, conj:string', arguments); - - args.key = this.driver.quoteIdentifiers(args.key); - let params = Array(args.val.length); - params.fill('?'); - - args.val.forEach(value => { - this.state.whereValues.push(value); - }); - - args.conj = (this.state.queryMap.length > 0) ? ` ${args.conj} ` : ' WHERE '; - let str = `${args.key} ${args.inClause} (${params.join(',')}) `; - - this._appendMap(args.conj, str, 'whereIn'); - } - - _run(type, table, callback, sql, vals) { - - if (! sql) { - sql = this._compile(type, table); - } - - if (! vals) { - vals = this.state.values.concat(this.state.whereValues); - } - - // Reset the state so another query can be built - this._resetState(); - - // Pass the sql and values to the adapter to run on the database - if (callback) { - return this.query(sql, vals, callback); - } else { - return this.query(sql, vals); - } - } - - _getCompile(type, table, reset) { - reset = reset || false; - - let sql = this._compile(type, table); - - if (reset) { - this._resetState(); - } - - return sql; - } - - _resetState() { - this.state = new State(); - } -} +const QueryBuilderBase = require('./QueryBuilderBase'); /** * Main object that builds SQL queries. * * @param {Driver} Driver - The syntax driver for the database * @param {Adapter} Adapter - The database module adapter for running queries + * @extends QueryBuilderBase */ class QueryBuilder extends QueryBuilderBase { - /** - * @private - * @constructor - * @param {Driver} Driver - The syntax driver for the database - * @param {Adapter} Adapter - The database module adapter for running queries - */ - constructor(Driver, Adapter) { - super(Driver, Adapter); - } - // ---------------------------------------------------------------------------- // ! Miscellaneous Methods // ---------------------------------------------------------------------------- @@ -312,12 +19,11 @@ class QueryBuilder extends QueryBuilderBase { * Run an arbitrary sql query. Run as a prepared statement. * * @param {string} sql - The sql to execute - * @param {array} [params] - The query parameters - * @param {function} [callback] - Optional callback - * @return {void|Promise} - Returns a promise if no callback is supplied + * @param {Array} [params] - The query parameters + * @return {Promise} - Promise with result of query */ - query(/*sql:string, [params]:array, [callback]:function*/) { - return this.adapter.execute.apply(this.adapter, arguments); + query (sql, params) { + return this.adapter.execute(sql, params); } /** @@ -325,7 +31,7 @@ class QueryBuilder extends QueryBuilderBase { * * @return {void} */ - resetQuery() { + resetQuery () { this._resetState(); } @@ -335,7 +41,7 @@ class QueryBuilder extends QueryBuilderBase { * @private * @return {Object} - The State object */ - getState() { + getState () { return this.state; } @@ -343,15 +49,10 @@ class QueryBuilder extends QueryBuilderBase { * Empties the selected database table * * @param {string} table - the name of the table to truncate - * @param {function} [callback] - Optional callback * @return {void|Promise} - Returns a promise if no callback is supplied */ - truncate(/*table:string, [callback]:function*/) { - getArgs('table:string, [callback]:function', arguments); - let args = [].slice.apply(arguments); - let sql = this.driver.truncate(args.shift()); - args.unshift(sql); - return this.query.apply(this, args); + truncate (table) { + return this.query(this.driver.truncate(table)); } /** @@ -359,7 +60,7 @@ class QueryBuilder extends QueryBuilderBase { * * @return {void} */ - end() { + end () { this.adapter.close(); } @@ -375,8 +76,7 @@ class QueryBuilder extends QueryBuilderBase { * @example query.select(['foo', 'bar']); // Select multiple fileds with an array * @return {QueryBuilder} - The Query Builder object, for chaining */ - select(fields) { - + select (fields) { // Split/trim fields by comma fields = (Array.isArray(fields)) ? fields @@ -384,7 +84,7 @@ class QueryBuilder extends QueryBuilderBase { // Split on 'As' fields.forEach((field, index) => { - if (field.match(/as/i)) { + if (/as/i.test(field)) { fields[index] = field.split(/ as /i).map(helpers.stringTrim); } }); @@ -411,7 +111,7 @@ class QueryBuilder extends QueryBuilderBase { * @example query.from('tableName t'); // Select the table with an alias * @return {QueryBuilder} - The Query Builder object, for chaining */ - from(tableName) { + from (tableName) { // Split identifiers on spaces let identArray = tableName.trim().split(' ').map(helpers.stringTrim); @@ -433,7 +133,7 @@ class QueryBuilder extends QueryBuilderBase { * @param {String} [pos=both] - The placement of the wildcard character(s): before, after, or both * @return {QueryBuilder} - The Query Builder object, for chaining */ - like(field, val, pos) { + like (field, val, pos) { this._like(field, val, pos, ' LIKE ', 'AND'); return this; } @@ -446,7 +146,7 @@ class QueryBuilder extends QueryBuilderBase { * @param {String} [pos=both] - The placement of the wildcard character(s): before, after, or both * @return {QueryBuilder} - The Query Builder object, for chaining */ - notLike(field, val, pos) { + notLike (field, val, pos) { this._like(field, val, pos, ' NOT LIKE ', 'AND'); return this; } @@ -459,7 +159,7 @@ class QueryBuilder extends QueryBuilderBase { * @param {String} [pos=both] - The placement of the wildcard character(s): before, after, or both * @return {QueryBuilder} - The Query Builder object, for chaining */ - orLike(field, val, pos) { + orLike (field, val, pos) { this._like(field, val, pos, ' LIKE ', 'OR'); return this; } @@ -472,7 +172,7 @@ class QueryBuilder extends QueryBuilderBase { * @param {String} [pos=both] - The placement of the wildcard character(s): before, after, or both * @return {QueryBuilder} - The Query Builder object, for chaining */ - orNotLike(field, val, pos) { + orNotLike (field, val, pos) { this._like(field, val, pos, ' NOT LIKE ', 'OR'); return this; } @@ -484,10 +184,8 @@ class QueryBuilder extends QueryBuilderBase { * @param {String|Number} [val] - The value to compare if the value of key is a string * @return {QueryBuilder} - The Query Builder object, for chaining */ - having(/*key, [val]*/) { - let args = getArgs('key:string|object, [val]:string|number', arguments); - - this._having(args.key, args.val, 'AND'); + having (key, val = null) { + this._having(key, val, 'AND'); return this; } @@ -498,10 +196,8 @@ class QueryBuilder extends QueryBuilderBase { * @param {String|Number} [val] - The value to compare if the value of key is a string * @return {QueryBuilder} - The Query Builder object, for chaining */ - orHaving(/*key, [val]*/) { - let args = getArgs('key:string|object, [val]:string|number', arguments); - - this._having(args.key, args.val, 'OR'); + orHaving (key, val = null) { + this._having(key, val, 'OR'); return this; } @@ -512,7 +208,7 @@ class QueryBuilder extends QueryBuilderBase { * @param {String|Number} [val] - The value to compare if the value of key is a string * @return {QueryBuilder} - The Query Builder object, for chaining */ - where(key, val) { + where (key, val) { this._where(key, val, 'AND'); return this; } @@ -524,7 +220,7 @@ class QueryBuilder extends QueryBuilderBase { * @param {String|Number} [val] - The value to compare if the value of key is a string * @return {QueryBuilder} - The Query Builder object, for chaining */ - orWhere(key, val) { + orWhere (key, val) { this._where(key, val, 'OR'); return this; } @@ -535,7 +231,7 @@ class QueryBuilder extends QueryBuilderBase { * @param {String} field - The name of the field that has a NULL value * @return {QueryBuilder} - The Query Builder object, for chaining */ - whereIsNull(field) { + whereIsNull (field) { this._whereNull(field, 'IS NULL', 'AND'); return this; } @@ -546,7 +242,7 @@ class QueryBuilder extends QueryBuilderBase { * @param {String} field - The name so the field that is not to be null * @return {QueryBuilder} - The Query Builder object, for chaining */ - whereIsNotNull(field) { + whereIsNotNull (field) { this._whereNull(field, 'IS NOT NULL', 'AND'); return this; } @@ -557,7 +253,7 @@ class QueryBuilder extends QueryBuilderBase { * @param {String} field - The name of the field * @return {QueryBuilder} - The Query Builder object, for chaining */ - orWhereIsNull(field) { + orWhereIsNull (field) { this._whereNull(field, 'IS NULL', 'OR'); return this; } @@ -568,7 +264,7 @@ class QueryBuilder extends QueryBuilderBase { * @param {String} field - The name of the field * @return {QueryBuilder} - The Query Builder object, for chaining */ - orWhereIsNotNull(field) { + orWhereIsNotNull (field) { this._whereNull(field, 'IS NOT NULL', 'OR'); return this; } @@ -580,7 +276,7 @@ class QueryBuilder extends QueryBuilderBase { * @param {Array} values - the array of items to search in * @return {QueryBuilder} - The Query Builder object, for chaining */ - whereIn(key, values) { + whereIn (key, values) { this._whereIn(key, values, 'IN', 'AND'); return this; } @@ -592,7 +288,7 @@ class QueryBuilder extends QueryBuilderBase { * @param {Array} values - the array of items to search in * @return {QueryBuilder} - The Query Builder object, for chaining */ - orWhereIn(key, values) { + orWhereIn (key, values) { this._whereIn(key, values, 'IN', 'OR'); return this; } @@ -604,7 +300,7 @@ class QueryBuilder extends QueryBuilderBase { * @param {Array} values - the array of items to search in * @return {QueryBuilder} - The Query Builder object, for chaining */ - whereNotIn(key, values) { + whereNotIn (key, values) { this._whereIn(key, values, 'NOT IN', 'AND'); return this; } @@ -616,7 +312,7 @@ class QueryBuilder extends QueryBuilderBase { * @param {Array} values - the array of items to search in * @return {QueryBuilder} - The Query Builder object, for chaining */ - orWhereNotIn(key, values) { + orWhereNotIn (key, values) { this._whereIn(key, values, 'NOT IN', 'OR'); return this; } @@ -630,12 +326,10 @@ class QueryBuilder extends QueryBuilderBase { * @example query.set({foo:'bar'}); // Set with an object * @return {QueryBuilder} - The Query Builder object, for chaining */ - set(/* $key, [$val] */) { - let args = getArgs('$key, [$val]', arguments); - + set (key, val) { // Set the appropriate state variables - this._mixedSet('setArrayKeys', 'key', args.$key, args.$val); - this._mixedSet('values', 'value', args.$key, args.$val); + this._mixedSet('setArrayKeys', 'key', key, val); + this._mixedSet('values', 'value', key, val); // Use the keys of the array to make the insert/update string // and escape the field names @@ -656,7 +350,7 @@ class QueryBuilder extends QueryBuilderBase { * @param {String} [type='inner'] - The type of join, which defaults to inner * @return {QueryBuilder} - The Query Builder object, for chaining */ - join(table, cond, type) { + join (table, cond, type) { type = type || 'inner'; // Prefix/quote table name @@ -681,8 +375,8 @@ class QueryBuilder extends QueryBuilderBase { * @param {String|Array} field - The name of the field to group by * @return {QueryBuilder} - The Query Builder object, for chaining */ - groupBy(field) { - if (! helpers.isScalar(field)) { + groupBy (field) { + if (!helpers.isScalar(field)) { let newGroupArray = field.map(this.driver.quoteIdentifiers); this.state.groupArray = this.state.groupArray.concat(newGroupArray); } else { @@ -701,7 +395,7 @@ class QueryBuilder extends QueryBuilderBase { * @param {String} [type='ASC'] - The order direction, ASC or DESC * @return {QueryBuilder} - The Query Builder object, for chaining */ - orderBy(field, type) { + orderBy (field, type) { type = type || 'ASC'; // Set the fields for later manipulation @@ -729,7 +423,7 @@ class QueryBuilder extends QueryBuilderBase { * @param {Number} [offset] - The row number to start from * @return {QueryBuilder} - The Query Builder object, for chaining */ - limit(limit, offset) { + limit (limit, offset) { this.state.limit = limit; this.state.offset = offset || null; @@ -741,7 +435,7 @@ class QueryBuilder extends QueryBuilderBase { * * @return {QueryBuilder} - The Query Builder object, for chaining */ - groupStart() { + groupStart () { let conj = (this.state.queryMap.length < 1) ? ' WHERE ' : ' AND '; this._appendMap(conj, '(', 'groupStart'); @@ -754,7 +448,7 @@ class QueryBuilder extends QueryBuilderBase { * * @return {QueryBuilder} - The Query Builder object, for chaining */ - orGroupStart() { + orGroupStart () { this._appendMap('', ' OR (', 'groupStart'); return this; @@ -766,7 +460,7 @@ class QueryBuilder extends QueryBuilderBase { * * @return {QueryBuilder} - The Query Builder object, for chaining */ - orNotGroupStart() { + orNotGroupStart () { this._appendMap('', ' OR NOT (', 'groupStart'); return this; @@ -777,7 +471,7 @@ class QueryBuilder extends QueryBuilderBase { * * @return {QueryBuilder} - The Query Builder object, for chaining */ - groupEnd() { + groupEnd () { this._appendMap('', ')', 'groupEnd'); return this; @@ -793,26 +487,22 @@ class QueryBuilder extends QueryBuilderBase { * @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 * @example query.get('table_name').then(promiseCallback); // Get all the rows in the table - * @example query.get('table_name', 5, callback); // Get 5 rows from the table - * @example query.get(callback); // Get the results of a query generated with other methods + * @example query.get('table_name', 5); // Get 5 rows from the table + * @example query.get(); // Get the results of a query generated with other methods * @return {void|Promise} - If no callback is passed, a promise is returned */ - get(/* [table], [limit], [offset], [callback] */) { - const argPattern = '[table]:string, [limit]:number, [offset]:number, [callback]:function'; - let args = getArgs(argPattern, arguments); - - if (args.table) { - this.from(args.table); + get (table, limit, offset) { + if (table) { + this.from(table); } - if (args.limit) { - this.limit(args.limit, args.offset); + if (limit) { + this.limit(limit, offset); } // Run the query - return this._run('get', args.table, args.callback); + return this._run('get', table); } /** @@ -820,18 +510,15 @@ class QueryBuilder extends QueryBuilderBase { * * @param {String} table - The table to insert into * @param {Object} [data] - Data to insert, if not already added with the 'set' method - * @param {Function} [callback] - Callback for handling response from the database - * @return {void|Promise} - If no callback is passed, a promise is returned + * @return {Promise} - If no callback is passed, a promise is returned */ - insert(/* table, data, callback */) { - let args = getArgs('table:string, [data]:object, [callback]:function', arguments); - - if (args.data) { - this.set(args.data); + insert (table, data) { + if (data) { + this.set(data); } // Run the query - return this._run('insert', this.driver.quoteTable(args.table), args.callback); + return this._run('insert', this.driver.quoteTable(table)); } /** @@ -839,18 +526,15 @@ class QueryBuilder extends QueryBuilderBase { * * @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 - * @example query.insertBatch('foo',[{id:1,val:'bar'},{id:2,val:'baz'}], callbackFunction); * @example query.insertBatch('foo',[{id:1,val:'bar'},{id:2,val:'baz'}]) *.then(promiseCallback); - * @return {void|Promise} - If no callback is passed, a promise is returned + * @return {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 batch = this.driver.insertBatch(args.table, args.data); + insertBatch (table, data) { + let batch = this.driver.insertBatch(table, data); // Run the query - return this._run('', '', args.callback, batch.sql, batch.values); + return this.query(batch.sql, batch.values); } /** @@ -858,18 +542,15 @@ class QueryBuilder extends QueryBuilderBase { * * @param {String} table - The table to insert into * @param {Object} [data] - Data to insert, if not already added with the 'set' method - * @param {Function} [callback] - Callback for handling response from the database - * @return {void|Promise} - If no callback is passed, a promise is returned + * @return {Promise} - If no callback is passed, a promise is returned */ - update(/*table, data, callback*/) { - let args = getArgs('table:string, [data]:object, [callback]:function', arguments); - - if (args.data) { - this.set(args.data); + update (table, data) { + if (data) { + this.set(data); } // Run the query - return this._run('update', this.driver.quoteTable(args.table), args.callback); + return this._run('update', this.driver.quoteTable(table)); } /** @@ -877,18 +558,15 @@ class QueryBuilder extends QueryBuilderBase { * * @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|Promise} - If no callback is passed, a promise is returned + * @return {Promise} - If no callback is passed, a promise is returned */ - delete(/*table, [where], [callback]*/) { - let args = getArgs('table:string, [where]:object, [callback]:function', arguments); - - if (args.where) { - this.where(args.where); + delete (table, where) { + if (where) { + this.where(where); } // Run the query - return this._run('delete', this.driver.quoteTable(args.table), args.callback); + return this._run('delete', this.driver.quoteTable(table)); } // ------------------------------------------------------------------------ @@ -902,13 +580,12 @@ class QueryBuilder extends QueryBuilderBase { * @param {Boolean} [reset=true] - Whether to reset the query builder so another query can be built * @return {String} - The compiled sql statement */ - getCompiledSelect(/*table, reset*/) { - let args = getArgs('[table]:string, [reset]:boolean', arguments); - if (args.table) { - this.from(args.table); + getCompiledSelect (table, reset = true) { + if (table) { + this.from(table); } - return this._getCompile('get', args.table, args.reset); + return this._getCompile('get', table, reset); } /** @@ -918,7 +595,7 @@ class QueryBuilder extends QueryBuilderBase { * @param {Boolean} [reset=true] - Whether to reset the query builder so another query can be built * @return {String} - The compiled sql statement */ - getCompiledInsert(table, reset) { + getCompiledInsert (table, reset) { return this._getCompile('insert', this.driver.quoteTable(table), reset); } @@ -929,7 +606,7 @@ class QueryBuilder extends QueryBuilderBase { * @param {Boolean} [reset=true] - Whether to reset the query builder so another query can be built * @return {String} - The compiled sql statement */ - getCompiledUpdate(table, reset) { + getCompiledUpdate (table, reset) { return this._getCompile('update', this.driver.quoteTable(table), reset); } @@ -940,9 +617,9 @@ class QueryBuilder extends QueryBuilderBase { * @param {Boolean} [reset=true] - Whether to reset the query builder so another query can be built * @return {String} - The compiled sql statement */ - getCompiledDelete(table, reset) { + getCompiledDelete (table, reset) { return this._getCompile('delete', this.driver.quoteTable(table), reset); } } -module.exports = QueryBuilder; \ No newline at end of file +module.exports = QueryBuilder; diff --git a/lib/QueryBuilderBase.js b/lib/QueryBuilderBase.js new file mode 100644 index 0000000..e0f3332 --- /dev/null +++ b/lib/QueryBuilderBase.js @@ -0,0 +1,272 @@ +'use strict'; + +const helpers = require('./helpers'); +const QueryParser = require('./QueryParser'); +const State = require('./State'); + +class QueryBuilderBase { + /** + * @private + * @constructor + * @param {Driver} Driver - The syntax driver for the database + * @param {Adapter} Adapter - The database module adapter for running queries + */ + constructor (Driver, Adapter) { + this.driver = Driver; + this.adapter = Adapter; + this.parser = new QueryParser(this.driver); + this.state = new State(); + } + + /** + * Complete the sql building based on the type provided + * + * @private + * @param {String} type - Type of SQL query + * @param {String} table - The table to run the query on + * @return {String} - The compiled sql + */ + _compile (type, table) { + // Put together the basic query + let sql = this._compileType(type, table); + + // Set each subClause + ['queryMap', 'groupString', 'orderString', 'havingMap'].forEach(clause => { + let param = this.state[clause]; + + if (!helpers.isScalar(param)) { + Object.keys(param).forEach(part => { + sql += param[part].conjunction + param[part].string; + }); + } else { + sql += param; + } + }); + + // Append the limit, if it exists + if (helpers.isNumber(this.state.limit)) { + sql = this.driver.limit(sql, this.state.limit, this.state.offset); + } + + return sql; + } + + _compileType (type, table) { + let sql = ''; + + switch (type) { + case 'insert': + let params = Array(this.state.setArrayKeys.length).fill('?'); + + sql = `INSERT INTO ${table} (`; + sql += this.state.setArrayKeys.join(','); + sql += `) VALUES (${params.join(',')})`; + break; + + case 'update': + sql = `UPDATE ${table} SET ${this.state.setString}`; + break; + + case 'delete': + sql = `DELETE FROM ${table}`; + break; + + default: + sql = `SELECT * FROM ${this.state.fromString}`; + + // Set the select string + if (this.state.selectString.length > 0) { + // Replace the star with the selected fields + sql = sql.replace('*', this.state.selectString); + } + + break; + } + + return sql; + } + + _like (field, val, pos, like, conj) { + field = this.driver.quoteIdentifiers(field); + + like = `${field} ${like} ?`; + + if (pos === 'before') { + val = `%${val}`; + } else if (pos === 'after') { + val = `${val}%`; + } else { + val = `%${val}%`; + } + + conj = (this.state.queryMap.length < 1) ? ' WHERE ' : ` ${conj} `; + this._appendMap(conj, like, 'like'); + + this.state.whereValues.push(val); + } + + /** + * Append a clause to the query map + * + * @private + * @param {String} conjunction - linking keyword for the clause + * @param {String} string - pre-compiled sql fragment + * @param {String} type - type of sql clause + * @return {void} + */ + _appendMap (conjunction, string, type) { + this.state.queryMap.push({ + type: type, + conjunction: conjunction, + string: string + }); + } + + /** + * Handle key/value pairs in an object the same way as individual arguments, + * when appending to state + * + * @private + * @return {Array} - modified state array + */ + _mixedSet (letName, valType, key, val) { + let obj = {}; + + if (helpers.isScalar(key) && !helpers.isUndefined(val)) { + // Convert key/val pair to a simple object + obj[key] = val; + } else if (helpers.isScalar(key) && helpers.isUndefined(val)) { + // If just a string for the key, and no value, create a simple object with duplicate key/val + obj[key] = key; + } else { + obj = key; + } + + Object.keys(obj).forEach(k => { + // If a single value for the return + if (['key', 'value'].indexOf(valType) !== -1) { + let pushVal = (valType === 'key') ? k : obj[k]; + this.state[letName].push(pushVal); + } else { + this.state[letName][k] = obj[k]; + } + }); + + return this.state[letName]; + } + + _whereMixedSet (key, val) { + this.state.whereMap = []; + this.state.rawWhereValues = []; + + this._mixedSet('whereMap', 'both', key, val); + this._mixedSet('rawWhereValues', 'value', key, val); + } + + _fixConjunction (conj) { + let lastItem = this.state.queryMap[this.state.queryMap.length - 1]; + let conjunctionList = helpers.arrayPluck(this.state.queryMap, 'conjunction'); + + if (this.state.queryMap.length === 0 || (!helpers.regexInArray(conjunctionList, /^ ?WHERE/i))) { + conj = ' WHERE '; + } else if (lastItem.type === 'groupStart') { + conj = ''; + } else { + conj = ` ${conj} `; + } + + return conj; + } + + _where (key, val, defaultConj) { + // Normalize key and value and insert into this.state.whereMap + this._whereMixedSet(key, val); + + // Parse the where condition to account for operators, + // functions, identifiers, and literal values + this.state = this.parser.parseWhere(this.driver, this.state); + + this.state.whereMap.forEach(clause => { + let conj = this._fixConjunction(defaultConj); + this._appendMap(conj, clause, 'where'); + }); + + this.state.whereMap = {}; + } + + _whereNull (field, stmt, conj) { + field = this.driver.quoteIdentifiers(field); + let item = `${field} ${stmt}`; + + this._appendMap(this._fixConjunction(conj), item, 'whereNull'); + } + + _having (key, val = null, conj = 'AND') { + // Normalize key/val and put in state.whereMap + this._whereMixedSet(key, val); + + // Parse the having condition to account for operators, + // functions, identifiers, and literal values + this.state = this.parser.parseWhere(this.driver, this.state); + + this.state.whereMap.forEach(clause => { + // Put in the having map + this.state.havingMap.push({ + conjunction: (this.state.havingMap.length > 0) ? ` ${conj} ` : ' HAVING ', + string: clause + }); + }); + + // Clear the where Map + this.state.whereMap = {}; + } + + _whereIn (key, val, inClause, conj) { + key = this.driver.quoteIdentifiers(key); + let params = Array(val.length); + params.fill('?'); + + val.forEach(value => { + this.state.whereValues.push(value); + }); + + conj = (this.state.queryMap.length > 0) ? ` ${conj} ` : ' WHERE '; + let str = `${key} ${inClause} (${params.join(',')}) `; + + this._appendMap(conj, str, 'whereIn'); + } + + _run (type, table, sql, vals) { + if (!sql) { + sql = this._compile(type, table); + } + + if (!vals) { + vals = this.state.values.concat(this.state.whereValues); + } + + // Reset the state so another query can be built + this._resetState(); + + // Pass the sql and values to the adapter to run on the database + return this.query(sql, vals); + } + + _getCompile (type, table, reset) { + reset = reset || false; + + let sql = this._compile(type, table); + + if (reset) { + this._resetState(); + } + + return sql; + } + + _resetState () { + this.state = new State(); + } +} + +module.exports = QueryBuilderBase; diff --git a/lib/QueryParser.js b/lib/QueryParser.js index 02a3cd3..e162385 100644 --- a/lib/QueryParser.js +++ b/lib/QueryParser.js @@ -18,13 +18,13 @@ class QueryParser { * @param {Driver} driver - The driver object for the database in use * @return {void} */ - constructor(driver) { + constructor (driver) { this.driver = driver; const matchPatterns = { function: /([a-z0-9_]+\((.*)\))/i, - operator: /\!=?|\=|\+|&&?|~|\|\|?|\^|\/|<>|>=?|<=?|\-|%|OR|AND|NOT|XOR/ig, - literal: /([0-9]+)|'(.*?)'|true|false/ig, + operator: /!=?|=|\+|&&?|~|\|\|?|\^|\/|<>|>=?|<=?|-|%|OR|AND|NOT|XOR/ig, + literal: /([0-9]+)|'(.*?)'|true|false/ig }; // Full pattern for identifiers @@ -55,7 +55,7 @@ class QueryParser { * @param {Array} array - Set of possible matches * @return {Array|null} - Filtered set of possible matches */ - filterMatches(array) { + filterMatches (array) { let output = []; // Return non-array matches @@ -76,7 +76,7 @@ class QueryParser { * @param {String} string - the string to check * @return {Array|null} - List of operators */ - hasOperator(string) { + hasOperator (string) { return this.filterMatches(string.match(this.matchPatterns.operator)); } @@ -86,13 +86,13 @@ class QueryParser { * @param {String} sql - Join sql to parse * @return {Object} - Join condition components */ - parseJoin(sql) { + parseJoin (sql) { let matches = {}; let output = { functions: [], identifiers: [], operators: [], - literals: [], + literals: [] }; // Get clause components @@ -118,12 +118,12 @@ class QueryParser { * @param {String} condition - The join condition to evalate * @return {String} - The parsed/escaped join condition */ - compileJoin(condition) { + compileJoin (condition) { let parts = this.parseJoin(condition); // Quote the identifiers parts.combined.forEach((part, i) => { - if (parts.identifiers.indexOf(part) !== -1 && ! helpers.isNumber(part)) { + if (parts.identifiers.indexOf(part) !== -1 && !helpers.isNumber(part)) { parts.combined[i] = this.driver.quoteIdentifiers(part); } }); @@ -138,7 +138,7 @@ class QueryParser { * @param {State} state - Query Builder state object * @return {String} - The parsed/escaped where condition */ - parseWhere(driver, state) { + parseWhere (driver, state) { let whereMap = state.whereMap; let whereValues = state.rawWhereValues; @@ -151,7 +151,7 @@ class QueryParser { let fullClause = ''; // Add an explicit = sign where one is inferred - if (! this.hasOperator(key)) { + if (!this.hasOperator(key)) { fullClause = `${key} = ${whereMap[key]}`; } else if (whereMap[key] === key) { fullClause = key; @@ -176,7 +176,7 @@ class QueryParser { if (identIndex !== -1) { parts.identifiers.splice(identIndex, 1); - if (! inOutputArray) { + if (!inOutputArray) { outputValues.push(value); inOutputArray = true; } @@ -187,7 +187,7 @@ class QueryParser { if (litIndex !== -1) { parts.literals.splice(litIndex, 1); - if (! inOutputArray) { + if (!inOutputArray) { outputValues.push(value); inOutputArray = true; } @@ -246,4 +246,4 @@ class QueryParser { } } -module.exports = QueryParser; \ No newline at end of file +module.exports = QueryParser; diff --git a/lib/Result.js b/lib/Result.js index 7f0b185..4fb79a3 100644 --- a/lib/Result.js +++ b/lib/Result.js @@ -16,9 +16,9 @@ class Result { * @param {Array} [rows] - the data rows of the result * @param {Array} [columns] - the column names in the result */ - constructor(rows, columns) { - this._rows = (rows == null) ? [] : rows; - this._columns = (columns == null) ? [] : columns; + constructor (rows=[], columns=[]) { + this._rows = rows; + this._columns = columns; // If columns aren't explicitly given, // get the list from the first row's keys @@ -37,7 +37,7 @@ class Result { * @private * @return {Array} - the data rows of the result */ - get rows() { + get rows () { return this._rows; } @@ -48,7 +48,7 @@ class Result { * @param {Array} rows - the data rows of the result * @return {void} */ - set rows(rows) { + set rows (rows) { this._rows = rows; } @@ -58,7 +58,7 @@ class Result { * @private * @return {Array} - the column names in the result */ - get columns() { + get columns () { return this._columns; } @@ -69,7 +69,7 @@ class Result { * @param {Array} cols - the array of columns for the current result * @return {void} */ - set columns(cols) { + set columns (cols) { this._columns = cols; } @@ -78,7 +78,7 @@ class Result { * * @return {Number} - the number of rows in the result */ - rowCount() { + rowCount () { return this._rows.length; } @@ -87,9 +87,9 @@ class Result { * * @return {Number} - the number of columns in the result */ - columnCount() { + columnCount () { return this._columns.length; } } -module.exports = Result; \ No newline at end of file +module.exports = Result; diff --git a/lib/State.js b/lib/State.js index 66cae2b..eec885c 100644 --- a/lib/State.js +++ b/lib/State.js @@ -5,7 +5,7 @@ * @private */ class State { - constructor() { + constructor () { // Arrays/maps this.queryMap = []; this.values = []; @@ -31,5 +31,3 @@ class State { } module.exports = State; - -// End of module State \ No newline at end of file diff --git a/lib/adapters/Firebird/index.js b/lib/adapters/Firebird/index.js new file mode 100644 index 0000000..1d93c17 --- /dev/null +++ b/lib/adapters/Firebird/index.js @@ -0,0 +1,8 @@ +'use strict'; + +const NodeFirebird = require('./node-firebird'); + +module.exports = config => { + return new NodeFirebird(config.connection); +}; + diff --git a/lib/adapters/Firebird/node-firebird.js b/lib/adapters/Firebird/node-firebird.js new file mode 100644 index 0000000..e62e7d5 --- /dev/null +++ b/lib/adapters/Firebird/node-firebird.js @@ -0,0 +1,61 @@ +'use strict'; + +const Adapter = require('../../Adapter'); +const Result = require('../../Result'); +const fb = require('node-firebird'); + +class Firebird extends Adapter { + constructor (config) { + super({}); + this.instance = new Promise((resolve, reject) => { + fb.attach(config, (err, instance) => { + if (err) { + return reject(err); + } + + return resolve(instance); + }); + }); + } + + /** + * Run the sql query as a prepared statement + * + * @param {String} sql - The sql with placeholders + * @param {Array} params - The values to insert into the query + * @return {Promise} - Returns a promise if no callback is provided + */ + execute (sql, params) { + return this.instance.then(conn => { + return new Promise((resolve, reject) => { + conn.query(sql, params, (err, result) => { + if (err) { + return reject(err); + } + + return resolve(this.transformResult(result)); + }); + }); + }); + } + + /** + * Transform the adapter's result into a standard format + * + * @param {*} originalResult - the original result object from the driver + * @return {Result} - the new result object + */ + transformResult (originalResult) { + return new Result(originalResult); + } + + /** + * Close the current database connection + * @return {void} + */ + close () { + this.instance.then(conn => conn.detach()); + } +} + +module.exports = Firebird; diff --git a/lib/adapters/MSSQLServer/index.js b/lib/adapters/MSSQLServer/index.js new file mode 100644 index 0000000..e69de29 diff --git a/lib/adapters/MariaDB/index.js b/lib/adapters/MariaDB/index.js new file mode 100644 index 0000000..f351d4d --- /dev/null +++ b/lib/adapters/MariaDB/index.js @@ -0,0 +1,3 @@ +'use strict'; + +module.exports = require('../Mysql'); diff --git a/lib/adapters/Mysql.js b/lib/adapters/Mysql.js deleted file mode 100644 index 817013b..0000000 --- a/lib/adapters/Mysql.js +++ /dev/null @@ -1,71 +0,0 @@ -'use strict'; - -const Adapter = require('../Adapter'); -const Result = require('../Result'); -const helpers = require('../helpers'); -const getArgs = require('getargs'); -const mysql2 = require('mysql2'); - -class Mysql extends Adapter { - - constructor(config) { - let instance = mysql2.createConnection(config); - instance.connect(err => { - if (err) { - throw new Error(err); - } - }); - super(instance); - } - - /** - * Transform the adapter's result into a standard format - * - * @param {*} result - original driver result object - * @return {Result} - standard result object - */ - transformResult(result) { - // For insert and update queries, the result object - // works differently. Just apply the properties of - // this special result to the standard result object. - if (helpers.type(result) === 'object') { - let r = new Result(); - - Object.keys(result).forEach(key => { - r[key] = result[key]; - }); - - return r; - } - - return new Result(result); - } - - /** - * Run the sql query as a prepared statement - * - * @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|Promise} - Returns a promise if no callback is provided - */ - execute(/*sql, params, callback*/) { - let args = getArgs('sql:string, [params]:array, [callback]:function', arguments); - - if (! args.callback) { - return new Promise((resolve, reject) => { - this.instance.execute(args.sql, args.params, (err, result) => - (err) - ? reject(err) - : resolve(this.transformResult(result)) - ); - }); - } - - return this.instance.execute(args.sql, args.params, (err, rows) => - args.callback(err, this.transformResult(rows)) - ); - } -} - -module.exports = Mysql; \ No newline at end of file diff --git a/lib/adapters/Mysql/index.js b/lib/adapters/Mysql/index.js new file mode 100644 index 0000000..985c3f4 --- /dev/null +++ b/lib/adapters/Mysql/index.js @@ -0,0 +1,7 @@ +'use strict'; + +const Mysql2 = require('./mysql2'); + +module.exports = config => { + return new Mysql2(config.connection); +}; diff --git a/lib/adapters/Mysql/mysql2.js b/lib/adapters/Mysql/mysql2.js new file mode 100644 index 0000000..438c3f3 --- /dev/null +++ b/lib/adapters/Mysql/mysql2.js @@ -0,0 +1,52 @@ +'use strict'; + +const Adapter = require('../../Adapter'); +const Result = require('../../Result'); +const helpers = require('../../helpers'); +const mysql2 = require('mysql2/promise'); + +class Mysql extends Adapter { + + constructor (config) { + const instance = mysql2.createConnection(config); + super(instance); + } + + /** + * Transform the adapter's result into a standard format + * + * @param {*} result - original driver result object + * @return {Result} - standard result object + */ + transformResult (result) { + // For insert and update queries, the result object + // works differently. Just apply the properties of + // this special result to the standard result object. + if (helpers.type(result) === 'object') { + let r = new Result(); + + Object.keys(result).forEach(key => { + r[key] = result[key]; + }); + + return r; + } + + return new Result(result); + } + + /** + * Run the sql query as a prepared statement + * + * @param {String} sql - The sql with placeholders + * @param {Array|undefined} params - The values to insert into the query + * @return {Promise} Result of query + */ + execute (sql, params) { + return this.instance + .then(conn => conn.execute(sql, params)) + .then(result => this.transformResult(result)); + } +} + +module.exports = Mysql; diff --git a/lib/adapters/Pg.js b/lib/adapters/Pg/Pg.js similarity index 62% rename from lib/adapters/Pg.js rename to lib/adapters/Pg/Pg.js index 996737c..b1f37a8 100644 --- a/lib/adapters/Pg.js +++ b/lib/adapters/Pg/Pg.js @@ -1,15 +1,13 @@ 'use strict'; -const Adapter = require('../Adapter'); -const Result = require('../Result'); -const getArgs = require('getargs'); -const helpers = require('../helpers'); +const Adapter = require('../../Adapter'); +const Result = require('../../Result'); +const helpers = require('../../helpers'); const pg = require('pg'); const url = require('url'); class Pg extends Adapter { - - constructor(config) { + constructor (config) { let instance = null; let connectionString = ''; if (helpers.isObject(config)) { @@ -23,7 +21,7 @@ class Pg extends Adapter { slashes: true, host: `${host}:${port}`, auth: `${user}${password}`, - pathname: config.database, + pathname: config.database }; connectionString = url.format(conn); @@ -32,16 +30,14 @@ class Pg extends Adapter { } if (connectionString !== '') { - let connected = false; - instance = new pg.Client(connectionString); - - instance.connect(err => { - connected = true; - + let conn = new pg.Client(connectionString); + conn.connect(err => { if (err) { throw new Error(err); } }); + + instance = Promise.resolve(conn); } super(instance); @@ -53,13 +49,15 @@ class Pg extends Adapter { * @param {*} result - original driver result object * @return {Result} - standard result object */ - transformResult(result) { + transformResult (result) { if (result == null) { return new Result(); } let cols = []; - result.fields.forEach(field => cols = field.name); + result.fields.forEach(field => { + cols = field.name; + }); return new Result(result.rows, cols); } @@ -69,34 +67,26 @@ 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|Promise} - Returns a promise if no callback is provided */ - execute(/*sql, params, callback*/) { - let args = getArgs('sql:string, [params]:array, [callback]:function', arguments); - + execute (sql, params) { // Replace question marks with numbered placeholders, because this adapter is different... let count = 0; - args.sql = args.sql.replace(/\?/g, () => { + sql = sql.replace(/\?/g, () => { count++; return `$${count}`; }); - if (! args.callback) { + return this.instance.then(conn => { return new Promise((resolve, reject) => { - this.instance.query(args.sql, args.params, (err, result) => + conn.query(sql, params, (err, result) => (err) ? reject(err) : resolve(this.transformResult(result)) ); }); - } - - return this.instance.query(args.sql, args.params, (err, origResult) => { - let result = this.transformResult(origResult); - return args.callback(err, result); }); } } -module.exports = Pg; \ No newline at end of file +module.exports = Pg; diff --git a/lib/adapters/Pg/index.js b/lib/adapters/Pg/index.js new file mode 100644 index 0000000..c20cdb3 --- /dev/null +++ b/lib/adapters/Pg/index.js @@ -0,0 +1,7 @@ +'use strict'; + +const Pg = require('./Pg'); + +module.exports = config => { + return new Pg(config.connection); +}; diff --git a/lib/adapters/Sqlite.js b/lib/adapters/Sqlite.js deleted file mode 100644 index 19e4a1a..0000000 --- a/lib/adapters/Sqlite.js +++ /dev/null @@ -1,64 +0,0 @@ -'use strict'; - -const Adapter = require('../Adapter'); -const Result = require('../Result'); -const getArgs = require('getargs'); -const helpers = require('../helpers'); -const dbliteAdapter = require('dblite'); - -class Sqlite extends Adapter { - constructor(config) { - let file = (helpers.isString(config)) ? config : config.file; - super(dbliteAdapter(file)); - - // Stop the stupid 'bye bye' message being output - this.instance.on('close', () => {}); - } - - /** - * Transform the adapter's result into a standard format - * - * @param {*} result - original driver result object - * @return {Result} - standard result object - */ - transformResult(result) { - return new Result(result); - } - - /** - * Run the sql query as a prepared statement - * - * @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|Promise} - Returns a promise if no callback is provided - */ - execute(/*sql, params, callback*/) { - let args = getArgs('sql:string, [params]:array, [callback]:function', arguments); - - if (! args.callback) { - return new Promise((resolve, reject) => { - this.instance.query(args.sql, args.params, (err, result) => - (err) - ? reject(err) - : resolve(this.transformResult(result)) - ); - }); - } - - return this.instance.query(args.sql, args.params, (err, res) => { - args.callback(err, this.transformResult(res)); - }); - } - - /** - * Close the current database connection - - * @return {void} - */ - close() { - this.instance.close(); - } -} - -module.exports = Sqlite; diff --git a/lib/adapters/Sqlite/dblite.js b/lib/adapters/Sqlite/dblite.js new file mode 100644 index 0000000..f62bd2c --- /dev/null +++ b/lib/adapters/Sqlite/dblite.js @@ -0,0 +1,67 @@ +'use strict'; + +const Adapter = require('../../Adapter'); +const Result = require('../../Result'); +const helpers = require('../../helpers'); +const dbliteAdapter = require('dblite'); + +class SqliteDblite extends Adapter { + constructor (config) { + let file = (helpers.isString(config)) ? config : config.file; + + const instance = new Promise((resolve, reject) => { + let conn = dbliteAdapter(file); + + // Stop the stupid 'bye bye' message being output + conn.on('close', () => {}); + + conn.on('error', err => { + reject(err); + }); + + // Make sure to actually pass on the connection! + return resolve(conn); + }); + + super(instance); + } + + /** + * Run the sql query as a prepared statement + * + * @param {String} sql - The sql with placeholders + * @param {Array} params - The values to insert into the query + * @return {Promise} - Returns a promise if no callback is provided + */ + execute (sql, params) { + return this.instance.then(conn => new Promise((resolve, reject) => { + return conn.query(sql, params, (err, rows) => { + if (err) { + return reject(err); + } + return resolve(this.transformResult(rows)); + }); + })); + } + + /** + * Transform the adapter's result into a standard format + * + * @param {*} originalResult - the original result object from the driver + * @return {Result} - the new result object + */ + transformResult (originalResult) { + return new Result(originalResult); + } + + /** + * Close the current database connection + * + * @return {void} + */ + close () { + this.instance.then(conn => conn.close()); + } +} + +module.exports = SqliteDblite; diff --git a/lib/adapters/Sqlite/index.js b/lib/adapters/Sqlite/index.js new file mode 100644 index 0000000..b4c891c --- /dev/null +++ b/lib/adapters/Sqlite/index.js @@ -0,0 +1,10 @@ +'use strict'; + +module.exports = config => { + const Implementation = (config.adapter && config.adapter === 'dblite') + ? require('./dblite') + : require('./sqlite3'); + + return new Implementation(config.connection); +}; + diff --git a/lib/adapters/Sqlite/sqlite3.js b/lib/adapters/Sqlite/sqlite3.js new file mode 100644 index 0000000..b80bc00 --- /dev/null +++ b/lib/adapters/Sqlite/sqlite3.js @@ -0,0 +1,63 @@ +'use strict'; + +const Adapter = require('../../Adapter'); +const Result = require('../../Result'); +const helpers = require('../../helpers'); +const sqlite3 = require('sqlite3').verbose(); + +class SqliteSqlite3 extends Adapter { + constructor (config) { + let file = (helpers.isString(config)) ? config : config.file; + + const instance = new Promise((resolve, reject) => { + let conn = new sqlite3.Database(file, sqlite3.OPEN_READWRITE | sqlite3.OPEN_CREATE, err => { + if (err) { + reject(err); + } + }); + + conn.on('open', resolve(conn)); + }); + + super(instance); + } + + /** + * Transform the adapter's result into a standard format + * + * @param {*} result - original driver result object + * @return {Result} - standard result object + */ + transformResult (result) { + return new Result(result); + } + + /** + * Run the sql query as a prepared statement + * + * @param {String} sql - The sql with placeholders + * @param {Array} params - The values to insert into the query + * @return {Promise} - Returns a promise if no callback is provided + */ + execute (sql, params) { + return this.instance.then(conn => new Promise((resolve, reject) => { + conn.all(sql, params, (err, rows) => { + if (err) { + return reject(err); + } + return resolve(this.transformResult(rows)); + }); + })); + } + + /** + * Close the current database connection + + * @return {void} + */ + close () { + this.instance.then(conn => conn.close()); + } +} + +module.exports = SqliteSqlite3; diff --git a/lib/drivers/Firebird.js b/lib/drivers/Firebird.js new file mode 100644 index 0000000..2c91827 --- /dev/null +++ b/lib/drivers/Firebird.js @@ -0,0 +1,45 @@ +'use strict'; + +let helpers = require('../helpers'); + +/** + * Driver for Firebird databases + * + * @module drivers/firebird + */ +module.exports = (() => { + delete require.cache[require.resolve('../Driver')]; + let driver = require('../Driver'); + + driver.hasTruncate = false; + + /** + * Set the limit clause + + * @param {String} origSql - SQL statement to modify + * @param {Number} limit - Maximum number of rows to fetch + * @param {Number|null} offset - Number of rows to skip + * @return {String} - Modified SQL statement + */ + driver.limit = (origSql, limit, offset) => { + let sql = `FIRST ${limit}`; + + if (helpers.isNumber(offset)) { + sql += ` SKIP ${offset}`; + } + + return origSql.replace(/SELECT/i, `SELECT ${sql}`); + }; + + /** + * SQL to insert a group of rows + * + * @return {void} + * @throws {Error} + */ + driver.insertBatch = () => { + throw new Error('Not Implemented'); + }; + + return driver; +})(); diff --git a/lib/drivers/MariaDB.js b/lib/drivers/MariaDB.js new file mode 100644 index 0000000..8d6df4e --- /dev/null +++ b/lib/drivers/MariaDB.js @@ -0,0 +1,8 @@ +'use strict'; + +/** + * Driver for MariaDB databases + * + * @module drivers/MariaDB + */ +module.exports = require('./Mysql'); diff --git a/lib/drivers/Mysql.js b/lib/drivers/Mysql.js index ce3590a..0e91273 100755 --- a/lib/drivers/Mysql.js +++ b/lib/drivers/Mysql.js @@ -3,32 +3,31 @@ /** * Driver for MySQL databases * - * @module drivers/mysql + * @module drivers/Mysql */ module.exports = (() => { delete require.cache[require.resolve('../Driver')]; - let driver = require('../Driver'), - helpers = require('../helpers'); + const driver = require('../Driver'); + const helpers = require('../helpers'); driver.identifierStartChar = '`'; driver.identifierEndChar = '`'; /** * Set the limit clause - + * * @param {String} sql - SQL statement to modify * @param {Number} limit - Maximum number of rows to fetch * @param {Number|null} offset - Number of rows to skip * @return {String} - Modified SQL statement */ driver.limit = (sql, limit, offset) => { - if (! helpers.isNumber(offset)) { - return sql += ` LIMIT ${limit}`; - } + sql += (helpers.isNumber(offset)) + ? ` LIMIT ${offset},${limit}` + : ` LIMIT ${limit}`; - return sql += ` LIMIT ${offset},${limit}`; + return sql; }; return driver; - -})(); \ No newline at end of file +})(); diff --git a/lib/drivers/Pg.js b/lib/drivers/Pg.js index 725b94c..61adf4f 100755 --- a/lib/drivers/Pg.js +++ b/lib/drivers/Pg.js @@ -3,11 +3,11 @@ /** * Driver for PostgreSQL databases * - * @module drivers/pg + * @module drivers/Pg */ module.exports = (() => { delete require.cache[require.resolve('../Driver')]; let driver = require('../Driver'); return driver; -})(); \ No newline at end of file +})(); diff --git a/lib/drivers/Sqlite.js b/lib/drivers/Sqlite.js index f771128..0ae0c1a 100644 --- a/lib/drivers/Sqlite.js +++ b/lib/drivers/Sqlite.js @@ -1,9 +1,9 @@ 'use strict'; /** - * Driver for Sqlite databases + * Driver for SQLite databases * - * @module drivers/sqlite + * @module drivers/Sqlite */ module.exports = (() => { delete require.cache[require.resolve('../Driver')]; @@ -21,15 +21,12 @@ module.exports = (() => { * @return {String} - The generated sql statement */ driver.insertBatch = (table, data) => { - // Get the data values to insert, so they can // be parameterized - let sql = '', - vals = [], - cols = [], - fields = [], - first = data.shift(); + let sql = ''; + let first = data.shift(); + let vals = []; data.forEach(obj => { let row = []; Object.keys(obj).forEach(key => { @@ -42,7 +39,8 @@ module.exports = (() => { // Get the field names from the keys of the first // object to be inserted - fields = Object.keys(first); + let fields = Object.keys(first); + let cols = []; fields.forEach(key => { cols.push(`'${driver._quote(first[key])}' AS ${driver.quoteIdentifiers(key)}`); }); @@ -56,9 +54,9 @@ module.exports = (() => { return { sql: sql, - values: null, + values: undefined }; }; return driver; -})(); \ No newline at end of file +})(); diff --git a/lib/helpers.js b/lib/helpers.js index 76e2adf..afb08d4 100755 --- a/lib/helpers.js +++ b/lib/helpers.js @@ -63,7 +63,7 @@ let helpers = { } arr.forEach(obj => { - if (! helpers.isUndefined(obj[key])) { + if (!helpers.isUndefined(obj[key])) { output.push(obj[key]); } }); @@ -80,13 +80,12 @@ let helpers = { */ regexInArray: (arr, pattern) => { // Empty case(s) - if (! helpers.isArray(arr) || arr.length === 0) { + if (!helpers.isArray(arr) || arr.length === 0) { return false; } - let i, l = arr.length; - - for (i = 0; i < l; i++) { + const l = arr.length; + for (let i = 0; i < l; i++) { // Short circuit if any items match if (pattern.test(arr[i])) { return true; @@ -105,7 +104,7 @@ let helpers = { str += ''; let first = str.charAt(0).toUpperCase(); return first + str.substr(1); - }, + } }; // Define an 'is' method for each type @@ -120,7 +119,7 @@ let types = [ 'Function', 'RegExp', 'NaN', - 'Infinite', + 'Infinite' ]; types.forEach(t => { /** @@ -134,13 +133,13 @@ types.forEach(t => { * @param {mixed} o - The object to check its type * @return {Boolean} - If the type matches */ - helpers[`is${t}`] = function (o) { + helpers[`is${t}`] = function (o) { if (t.toLowerCase() === 'infinite') { t = 'infinity'; } - return helpers.type(o) === t.toLowerCase(); - }; + return helpers.type(o) === t.toLowerCase(); + }; }); -module.exports = helpers; \ No newline at end of file +module.exports = helpers; diff --git a/package.json b/package.json index 1190477..531487a 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,10 @@ { "name": "ci-node-query", - "version": "4.0.0", + "version": "5.0.0", "description": "A query builder for node based on the one in CodeIgniter", "author": "Timothy J Warren ", "engines": { - "node": ">=4.0.0" + "node": ">=6.0.0" }, "files": [ "lib/" @@ -21,13 +21,14 @@ }, "keywords": [ "codeigniter", - "mysql2", + "mariadb", + "mysql", "query builder", - "pg", "postgres", - "sqlite3", + "postgresql", + "sql", "sqlite", - "dblite" + "sqlite3" ], "bugs": { "url": "https://git.timshomepage.net/timw4mail/node-query/issues" @@ -38,31 +39,38 @@ "getargs": "~0.0.8", "glob": "^7.0.3", "mysql2": "^1.0.0-rc.1", - "pg": "^4.5.1", + "node-firebird": "^0.7.5", + "pg": "^6.0.0", "require-reload": "~0.2.2", + "sqlite3": "^3.1.8", "xregexp": "^3.0.0" }, "devDependencies": { "chai": "^3.5.0", - "chai-as-promised": "^5.2.0", - "documentation": "", - "eslint": "^2.4.0", - "glob": "~6.0.4", - "gulp": "~3.9.0", - "gulp-documentation": "^2.2.0", - "gulp-eslint": "^2.0.0", - "gulp-istanbul": "^0.10.3", - "gulp-jscs": "^3.0.2", - "gulp-mocha": "^2.2.0", - "gulp-pipe": "^1.0.4", - "gulp-sloc": "~1.0.4", + "chai-as-promised": "^6.0.0", + "documentation": "latest", + "eslint": "^3.5.0", + "globstar": "^1.0.0", + "happiness": "^7.1.2", "istanbul": "~0.4.2", - "mocha": "^2.4.5" + "mocha": "^3.0.0", + "npm-run-all": "^3.0.0", + "nsp": "^2.2.1" }, "license": "MIT", "scripts": { - "predocs": "documentation build -f md -o API.md lib/*.js", - "docs": "documentation build -f html -o docs lib/*.js", - "test": "gulp test" + "audit": "nsp check", + "build": "npm-run-all --parallel lint:src lint:tests docs coverage", + "coverage": "istanbul cover ./node_modules/mocha/bin/_mocha", + "default": "npm-run-all --parallel audit lint:src lint:tests && npm run test", + "predocs": "globstar -- documentation build -f md -o API.md \"lib/*.js\"", + "docs": "globstar -- documentation build -f html -o docs \"lib/*.js\"", + "happy": "happiness \"lib/**/*.js\" \"test/**/*.js\"", + "happy:src": "happiness \"lib/**/*.js\"", + "happy:tests": "happiness \"test/**/*.js\"", + "lint": "npm-run-all lint:tests lint:src && happy", + "lint:src": "eslint ./lib", + "lint:tests": "eslint ./test", + "test": "mocha -R spec" } } diff --git a/sonar-project.properties b/sonar-project.properties deleted file mode 100644 index 75a4c46..0000000 --- a/sonar-project.properties +++ /dev/null @@ -1,5 +0,0 @@ -sonar.projectKey=node-query -sonar.projectName=NodeJS Query Builder -sonar.projectVersion=3.1.0 -sonar.sources=lib -sonar.javascript.lcov.reportPath=coverage/lcov.info \ No newline at end of file diff --git a/test/FB_TEST_DB.FDB b/test/FB_TEST_DB.FDB new file mode 100755 index 0000000..f68f70a Binary files /dev/null and b/test/FB_TEST_DB.FDB differ diff --git a/test/adapters/00node-firebird_test.js b/test/adapters/00node-firebird_test.js new file mode 100644 index 0000000..6a87c52 --- /dev/null +++ b/test/adapters/00node-firebird_test.js @@ -0,0 +1,38 @@ +/* eslint-env node, mocha */ +'use strict'; + +// Load the test base +const path = require('path'); +const reload = require('require-reload')(require); +const testBase = reload('../base'); +const expect = testBase.expect; +const testRunner = testBase.promiseTestRunner; + +// Skip on CI +if (!(process.env.CI || process.env.TRAVIS)) { + // Load the test config file + let adapterName = 'node-firebird'; + const config = reload('../config.json')[adapterName]; + config.connection.database = path.join(__dirname, config.connection.database); + let nodeQuery = reload('../../lib/NodeQuery')(config); + + let qb = nodeQuery.getQuery(); + + suite('Firebird adapter 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'); + }); + + testRunner(qb); + + suiteTeardown(() => { + qb.end(); + }); + }); +} diff --git a/test/adapters/dblite_test.js b/test/adapters/dblite_test.js index f90ae30..690fc3e 100644 --- a/test/adapters/dblite_test.js +++ b/test/adapters/dblite_test.js @@ -1,14 +1,12 @@ +/* eslint-env node, mocha */ 'use strict'; // Load the test base const reload = require('require-reload')(require); reload.emptyCache(); -const fs = require('fs'); const testBase = reload('../base'); -const expect = testBase.expect; -const promiseTestRunner = testBase.promiseTestRunner; -const testRunner = testBase.testRunner; -let tests = reload('../base/tests'); +const expect = testBase.expect; +const testRunner = testBase.promiseTestRunner; // Load the test config file const config = testBase.config; @@ -20,63 +18,17 @@ let qb = nodeQuery.getQuery(); suite('Dblite adapter tests -', () => { suiteSetup(done => { // Set up the sqlite database - fs.readFile(`${__dirname}/../sql/sqlite.sql`, 'utf8', (err, data) => { - if (err) { - return done(err); - } + const createTest = 'CREATE TABLE IF NOT EXISTS "create_test" ("id" INTEGER PRIMARY KEY, "key" TEXT, "val" TEXT);'; + const createJoin = 'CREATE TABLE IF NOT EXISTS "create_join" ("id" INTEGER PRIMARY KEY, "key" TEXT, "val" TEXT);'; - qb.query(data, () => done()); - }); - }); - - /*--------------------------------------------------------------------------- - Callback Tests - ---------------------------------------------------------------------------*/ - - testRunner(qb, (err, result, done) => { - expect(err).is.not.ok; - expect(result.rows).is.an('array'); - expect(result.columns).is.an('array'); - expect(result.rowCount()).to.not.be.undefined; - expect(result.columnCount()).to.not.be.undefined; - done(); - }); - 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; + qb.query(createTest) + .then(() => qb.query(createJoin)) + .then(() => { 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 => { - expect(err).is.not.ok; - return done(); - }); - }); - - /*--------------------------------------------------------------------------- - Promise Tests - ---------------------------------------------------------------------------*/ - promiseTestRunner(qb); + testRunner(qb); test('Promise - Select with function and argument in WHERE clause', () => { let promise = qb.select('id') .from('create_test') @@ -90,21 +42,19 @@ suite('Dblite adapter tests -', () => { { id: 544, key: 3, - val: new Buffer('7'), + val: Buffer.from('7') }, { id: 89, key: 34, - val: new Buffer('10 o\'clock'), + val: Buffer.from('10 o\'clock') }, { id: 48, key: 403, - val: new Buffer('97'), - }, + val: Buffer.from('97') + } ]; - let promise = qb.query(qb.driver.truncate('create_test')).then( - () => qb.insertBatch('create_test', data) - ); + let promise = qb.insertBatch('create_test', data); expect(promise).to.be.fulfilled; }); suiteTeardown(() => { diff --git a/test/adapters/mysql2_test.js b/test/adapters/mysql2_test.js index c325be1..ea87a72 100644 --- a/test/adapters/mysql2_test.js +++ b/test/adapters/mysql2_test.js @@ -1,12 +1,12 @@ +/* eslint-env node, mocha */ 'use strict'; // Load the test base const reload = require('require-reload')(require); reload.emptyCache(); const testBase = reload('../base'); -const expect = testBase.expect; -const promiseTestRunner = testBase.promiseTestRunner; -const testRunner = testBase.testRunner; +const expect = testBase.expect; +const testRunner = testBase.promiseTestRunner; // Load the test config file let adapterName = 'mysql2'; @@ -17,61 +17,12 @@ let nodeQuery = reload('../../lib/NodeQuery')(config); let qb = nodeQuery.getQuery(); suite('Mysql2 adapter tests -', () => { - - suiteSetup(done => qb.truncate('create_test').then(() => done())); - test('nodeQuery.getQuery = nodeQuery.init', () => { expect(nodeQuery.getQuery()) .to.be.deep.equal(qb); }); - //-------------------------------------------------------------------------- - // Callback Tests - //-------------------------------------------------------------------------- - testRunner(qb, (err, result, done) => { - expect(err).is.not.ok; - expect(result.rows).is.an('array'); - expect(result.columns).is.an('array'); - expect(result.rowCount()).to.not.be.undefined; - expect(result.columnCount()).to.not.be.undefined; - 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('Callback - Test Insert Batch', done => { - let data = [ - { - id: 5441, - key: 3, - val: new Buffer('7'), - }, { - id: 891, - key: 34, - val: new Buffer('10 o\'clock'), - }, { - id: 481, - key: 403, - val: new Buffer('97'), - }, - ]; - - qb.insertBatch('create_test', data, (err, res) => { - expect(err).is.not.ok; - return done(err); - }); - }); - - //--------------------------------------------------------------------------- - // Promise Tests - //--------------------------------------------------------------------------- - promiseTestRunner(qb); + testRunner(qb); test('Promise - Select with function and argument in WHERE clause', () => { let promise = qb.select('id') .from('create_test') @@ -80,28 +31,31 @@ suite('Mysql2 adapter tests -', () => { return expect(promise).to.be.fulfilled; }); - + test('Test Truncate', () => { + let promise = qb.truncate('create_test'); + return expect(promise).to.be.fullfilled; + }); test('Test Insert Batch', () => { let data = [ { id: 5442, key: 4, - val: new Buffer('7'), + val: Buffer.from('7') }, { id: 892, key: 35, - val: new Buffer('10 o\'clock'), + val: Buffer.from('10 o\'clock') }, { id: 482, key: 404, - val: 97, - }, + val: 97 + } ]; return expect(qb.insertBatch('create_test', data)).to.be.fulfilled; }); - suiteTeardown(() => { + /* suiteTeardown(() => { qb.end(); - }); + }); */ }); diff --git a/test/adapters/pg_test.js b/test/adapters/pg_test.js index c701685..3a6d3e6 100644 --- a/test/adapters/pg_test.js +++ b/test/adapters/pg_test.js @@ -1,12 +1,12 @@ +/* eslint-env node, mocha */ 'use strict'; // Load the test base const reload = require('require-reload')(require); reload.emptyCache(); const testBase = reload('../base'); -const expect = testBase.expect; -const promiseTestRunner = testBase.promiseTestRunner; -const testRunner = testBase.testRunner; +const expect = testBase.expect; +const testRunner = testBase.promiseTestRunner; // Load the test config file let adapterName = 'pg'; @@ -32,53 +32,18 @@ suite('Pg adapter tests -', () => { return expect(qb2).to.be.ok; }); - //-------------------------------------------------------------------------- - // Callback Tests - //-------------------------------------------------------------------------- - testRunner(qb, (err, result, done) => { - expect(err).is.not.ok; - expect(result.rows).is.an('array'); - expect(result.rowCount()).to.not.be.undefined; - expect(result.columnCount()).to.not.be.undefined; - 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(rows).is.ok; - expect(err).is.not.ok; - return done(err); - }); - }); - test('Callback - Test Insert Batch', done => { - let data = [ - { - id: 5441, - key: 3, - val: new Buffer('7'), - }, { - id: 891, - key: 34, - val: new Buffer('10 o\'clock'), - }, { - id: 481, - key: 403, - val: new Buffer('97'), - }, - ]; - - qb.insertBatch('create_test', data, (err, res) => { - expect(err).is.not.ok; - return done(err); - }); + test('Test Connection Error', done => { + try { + reload('../../lib/NodeQuery')({}); + done(true); + } catch (e) { + expect(e).to.be.ok; + expect(e).is.an('Error'); + done(); + } }); - //-------------------------------------------------------------------------- - // Promise Tests - //-------------------------------------------------------------------------- - promiseTestRunner(qb); + testRunner(qb); test('Promise - Select with function and argument in WHERE clause', () => { let promise = qb.select('id') .from('create_test') @@ -87,21 +52,25 @@ suite('Pg adapter tests -', () => { return expect(promise).to.be.fulfilled; }); + test('Promise - Test Truncate', () => { + let promise = qb.truncate('create_test'); + return expect(promise).to.be.fulfilled; + }); test('Promise - Test Insert Batch', () => { let data = [ { id: 544, key: 3, - val: new Buffer('7'), + val: Buffer.from('7') }, { id: 89, key: 34, - val: new Buffer('10 o\'clock'), + val: Buffer.from('10 o\'clock') }, { id: 48, key: 403, - val: new Buffer('97'), - }, + val: Buffer.from('97') + } ]; let promise = qb.insertBatch('create_test', data); @@ -111,4 +80,4 @@ suite('Pg adapter tests -', () => { qb.end(); qb2.end(); }); -}); \ No newline at end of file +}); diff --git a/test/adapters/sqlite3_test.js b/test/adapters/sqlite3_test.js new file mode 100644 index 0000000..e86f1fc --- /dev/null +++ b/test/adapters/sqlite3_test.js @@ -0,0 +1,63 @@ +/* eslint-env node, mocha */ +'use strict'; + +// Load the test base +const reload = require('require-reload')(require); +reload.emptyCache(); +const testBase = reload('../base'); +const expect = testBase.expect; +const testRunner = testBase.promiseTestRunner; + +// Load the test config file +const config = testBase.config; + +// Set up the query builder object +let nodeQuery = require('../../lib/NodeQuery')(config.sqlite3); +let qb = nodeQuery.getQuery(); + +suite('Sqlite3 adapter tests -', () => { + suiteSetup(done => { + // Set up the sqlite database + const createTest = 'CREATE TABLE IF NOT EXISTS "create_test" ("id" INTEGER PRIMARY KEY, "key" TEXT, "val" TEXT);'; + const createJoin = 'CREATE TABLE IF NOT EXISTS "create_join" ("id" INTEGER PRIMARY KEY, "key" TEXT, "val" TEXT);'; + + qb.query(createTest) + .then(() => qb.query(createJoin)) + .then(() => { + return done(); + }); + }); + + testRunner(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; + }); + test('Promise - Test Insert Batch', () => { + let data = [ + { + id: 544, + key: 3, + val: Buffer.from('7') + }, { + id: 89, + key: 34, + val: Buffer.from('10 o\'clock') + }, { + id: 48, + key: 403, + val: Buffer.from('97') + } + ]; + + let promise = qb.insertBatch('create_test', data); + expect(promise).to.be.fulfilled; + }); + suiteTeardown(() => { + qb.end(); + }); +}); diff --git a/test/base.js b/test/base.js index 4e29ca7..e25750a 100644 --- a/test/base.js +++ b/test/base.js @@ -11,6 +11,5 @@ module.exports = { config: require(configFile), expect: chai.expect, tests: require('./base/tests'), - testRunner: require('./base/adapterCallbackTestRunner'), - promiseTestRunner: require('./base/adapterPromiseTestRunner'), -}; \ No newline at end of file + promiseTestRunner: require('./base/adapterPromiseTestRunner') +}; diff --git a/test/base/adapterCallbackTestRunner.js b/test/base/adapterCallbackTestRunner.js deleted file mode 100644 index 9304d7b..0000000 --- a/test/base/adapterCallbackTestRunner.js +++ /dev/null @@ -1,230 +0,0 @@ -'use strict'; - -// jscs:disable -// 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, rows, 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 -', () => { - suiteSetup(() => qb.truncate('create_test')); - 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, rows, done); - }); - }); - test('Callback - Test Insert Object', done => { - qb.insert('create_test', { - id: 587, - key: 1, - val: new Buffer('2'), - }, (err, rows) => { - return callback(err, rows, 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, rows, 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, rows, 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, rows, done); - }); - }); - test('Callback - Test delete', done => { - qb.delete('create_test', {id: 5}, (err, rows) => { - return callback(err, rows, done); - }); - }); - test('Callback - Delete with where', done => { - qb.where('id', 5) - .delete('create_test', (err, rows) => { - return callback(err, rows, done); - }); - }); - test('Callback - Delete multiple where values', done => { - qb.delete('create_test', { - id: 5, - key: 'gogle', - }, (err, rows) => { - return callback(err, rows, 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, rows, 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, rows, 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, rows, 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, rows, done); - }); - }); - }); - suite('Get compiled tests -', () => { - test('select', () => { - let sql = qb.select('id') - .from('create_test') - .getCompiledSelect(true); - - return expect(helpers.isString(sql)).to.be.true; - }); - test('select from', () => { - let sql = qb.select('id') - .getCompiledSelect('create_test', true); - - return expect(helpers.isString(sql)).to.be.true; - }); - test('insert', () => { - let sql = qb.set('id', 3) - .getCompiledInsert('create_test'); - - return expect(helpers.isString(sql)).to.be.true; - }); - test('update', () => { - let sql = qb.set('id', 3) - .where('id', 5) - .getCompiledUpdate('create_test'); - - return expect(helpers.isString(sql)).to.be.true; - }); - test('delete', () => { - let sql = qb.where('id', 5) - .getCompiledDelete('create_test'); - - return 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 index 4ee2dc5..58c391b 100644 --- a/test/base/adapterPromiseTestRunner.js +++ b/test/base/adapterPromiseTestRunner.js @@ -1,6 +1,6 @@ +/* eslint-env node, mocha */ 'use strict'; -// jscs:disable // Load the test base const chai = require('chai'); const chaiAsPromised = require('chai-as-promised'); @@ -8,39 +8,39 @@ chai.use(chaiAsPromised); const expect = chai.expect; const reload = require('require-reload')(require); -let tests = reload('../base/tests'); +const tests = reload('../base/tests'); -let helpers = reload('../../lib/helpers'), - State = reload('../../lib/State'); - -module.exports = function promiseTestRunner(qb) { +module.exports = function promiseTestRunner (qb) { Object.keys(tests).forEach(suiteName => { suite(suiteName, () => { let currentSuite = tests[suiteName]; Object.keys(currentSuite).forEach(testDesc => { - test(`Promise - ${testDesc}`, () => { - let methodObj = currentSuite[testDesc]; - let methodNames = Object.keys(methodObj); - let lastMethodIndex = methodNames[methodNames.length - 1]; + test(testDesc, done => { + const methodObj = currentSuite[testDesc]; + const methodNames = Object.keys(methodObj); let results = []; methodNames.forEach(name => { - let args = methodObj[name], - method = qb[name]; + const args = methodObj[name]; + const method = qb[name]; if (args[0] === 'multiple') { args.shift(); args.forEach(argSet => { results.push(method.apply(qb, argSet)); }); - } else { results.push(method.apply(qb, args)); } }); let promise = results.pop(); - return expect(promise).to.be.fulfilled; + promise.then(result => { + expect(result.rows).is.an('array'); + expect(result.rowCount()).to.not.be.undefined; + expect(result.columnCount()).to.not.be.undefined; + return done(); + }).catch(e => done(e)); }); }); }); @@ -54,7 +54,7 @@ module.exports = function promiseTestRunner(qb) { test('Promise - Test Insert', () => { let promise = qb.set('id', 98) .set('key', '84') - .set('val', new Buffer('120')) + .set('val', Buffer.from('120')) .insert('create_test'); return expect(promise).to.be.fulfilled; @@ -63,7 +63,7 @@ module.exports = function promiseTestRunner(qb) { let promise = qb.insert('create_test', { id: 587, key: 1, - val: new Buffer('2'), + val: Buffer.from('2') }); return expect(promise).to.be.fulfilled; @@ -73,7 +73,7 @@ module.exports = function promiseTestRunner(qb) { .update('create_test', { id: 7, key: 'gogle', - val: new Buffer('non-word'), + val: Buffer.from('non-word') }); return expect(promise).to.be.fulfilled; @@ -82,7 +82,7 @@ module.exports = function promiseTestRunner(qb) { let object = { id: 22, key: 'gogle', - val: new Buffer('non-word'), + val: Buffer.from('non-word') }; let promise = qb.set(object) @@ -95,7 +95,7 @@ module.exports = function promiseTestRunner(qb) { let promise = qb.where('id', 36) .set('id', 36) .set('key', 'gogle') - .set('val', new Buffer('non-word')) + .set('val', Buffer.from('non-word')) .update('create_test'); return expect(promise).to.be.fulfilled; @@ -113,7 +113,7 @@ module.exports = function promiseTestRunner(qb) { test('Promise - Delete multiple where values', () => { let promise = qb.delete('create_test', { id: 5, - key: 'gogle', + key: 'gogle' }); return expect(promise).to.be.fulfilled; @@ -176,4 +176,4 @@ module.exports = function promiseTestRunner(qb) { return expect(promise).to.be.fulfilled; }); }); -}; \ No newline at end of file +}; diff --git a/test/base/tests.js b/test/base/tests.js index d4e3e3d..8583a8d 100644 --- a/test/base/tests.js +++ b/test/base/tests.js @@ -1,26 +1,25 @@ 'use strict'; -// jscs:disable module.exports = { 'Get tests -': { 'Get with function': { select: ['id, COUNT(id) as count'], from: ['create_test'], groupBy: ['id'], - get: [], + get: [] }, 'Basic select all get': { - get: ['create_test'], + get: ['create_test'] }, 'Basic select all with from': { from: ['create_test'], - get: [], + get: [] }, 'Get with limit': { - get: ['create_test', 2], + get: ['create_test', 2] }, 'Get with limit and offset': { - get: ['create_test', 2, 1], + get: ['create_test', 2, 1] }, 'Get with having': { select: ['id'], @@ -30,9 +29,9 @@ module.exports = { 'multiple', [{'id >': 1}], ['id !=', 3], - ['id', 900], + ['id', 900] ], - get: [], + get: [] }, 'Get with orHaving': { select: ['id'], @@ -40,8 +39,8 @@ module.exports = { groupBy: ['id'], having: [{'id >': 1}], orHaving: ['id !=', 3], - get: [], - }, + get: [] + } }, 'Select tests -': { 'Select where get': { @@ -49,94 +48,94 @@ module.exports = { where: [ 'multiple', ['id >', 1], - ['id <', 900], + ['id <', 900] ], - get: ['create_test', 2, 1], + get: ['create_test', 2, 1] }, 'Select where get 2': { select: ['id, key as k, val'], where: ['id !=', 1], - get: ['create_test', 2, 1], + get: ['create_test', 2, 1] }, 'Multi Order By': { from: ['create_test'], orderBy: ['id, key'], - get: [], + get: [] }, 'Select get': { select: ['id, key as k, val'], - get: ['create_test', 2, 1], + get: ['create_test', 2, 1] }, 'Select from get': { select: ['id, key as k, val'], from: ['create_test ct'], where: ['id >', 1], - get: [], + get: [] }, 'Select from limit get': { select: ['id, key as k, val'], from: ['create_test ct'], where: ['id >', 1], limit: [3], - get: [], + get: [] }, 'Select where IS NOT NULL': { select: ['id', 'key as k', 'val'], from: ['create_test ct'], whereIsNotNull: ['id'], - get: [], + get: [] }, 'Select where IS NULL': { select: ['id', 'key as k', 'val'], from: ['create_test ct'], whereIsNull: ['id'], - get: [], + get: [] }, 'Select where OR IS NOT NULL': { select: ['id', 'key as k', 'val'], from: ['create_test ct'], whereIsNull: ['id'], orWhereIsNotNull: ['id'], - get: [], + get: [] }, 'Select where OR IS NULL': { select: ['id', 'key as k', 'val'], from: ['create_test ct'], where: ['id', 3], orWhereIsNull: ['id'], - get: [], + get: [] }, 'Select with string where value': { select: ['id', 'key as k', 'val'], from: ['create_test ct'], where: ['id > 3'], - get: [], - }, + get: [] + } }, 'Where in tests -': { 'Where in': { from: ['create_test'], whereIn: ['id', [0, 6, 56, 563, 341]], - get: [], + get: [] }, 'Or Where in': { from: ['create_test'], where: ['key', 'false'], orWhereIn: ['id', [0, 6, 56, 563, 341]], - get: [], + get: [] }, 'Where Not in': { from: ['create_test'], where: ['key', 'false'], whereNotIn: ['id', [0, 6, 56, 563, 341]], - get: [], + get: [] }, 'Or Where Not in': { from: ['create_test'], where: ['key', 'false'], orWhereNotIn: ['id', [0, 6, 56, 563, 341]], - get: [], - }, + get: [] + } }, 'Query modifier tests -': { 'Order By': { @@ -145,15 +144,15 @@ module.exports = { where: [ 'multiple', ['id >', 0], - ['id <', 9000], + ['id <', 9000] ], orderBy: [ 'multiple', ['id', 'DESC'], - ['k', 'ASC'], + ['k', 'ASC'] ], limit: [5, 2], - get: [], + get: [] }, 'Group By': { select: ['id, key as k, val'], @@ -161,20 +160,20 @@ module.exports = { where: [ 'multiple', ['id >', 0], - ['id <', 9000], + ['id <', 9000] ], groupBy: [ 'multiple', ['k'], - [['id', 'val']], + [['id', 'val']] ], orderBy: [ 'multiple', ['id', 'DESC'], - ['k', 'ASC'], + ['k', 'ASC'] ], limit: [5, 2], - get: [], + get: [] }, 'Or Where': { select: ['id, key as k, val'], @@ -182,55 +181,55 @@ module.exports = { where: [' id ', 1], orWhere: ['key > ', 0], limit: [2, 1], - get: [], + get: [] }, Like: { from: ['create_test'], like: ['key', 'og'], - get: [], + get: [] }, 'Or Like': { from: ['create_test'], like: ['key', 'og'], orLike: ['key', 'val'], - get: [], + get: [] }, 'Not Like': { from: ['create_test'], like: ['key', 'og', 'before'], notLike: ['key', 'val'], - get: [], + get: [] }, 'Or Not Like': { from: ['create_test'], like: ['key', 'og', 'before'], orNotLike: ['key', 'val'], - get: [], + get: [] }, 'Like Before': { from: ['create_test'], like: ['key', 'og', 'before'], - get: [], + get: [] }, 'Like After': { from: ['create_test'], like: ['key', 'og', 'after'], - get: [], + get: [] }, 'Basic Join': { from: ['create_test ct'], join: ['create_join cj', 'cj.id=ct.id'], - get: [], + get: [] }, 'Left Join': { from: ['create_test ct'], join: ['create_join cj', 'cj.id=ct.id', 'left'], - get: [], + get: [] }, 'Inner Join': { from: ['create_test ct'], join: ['create_join cj', 'cj.id=ct.id', 'inner'], - get: [], + get: [] }, 'Join with multiple where values': { from: ['create_test ct'], @@ -238,10 +237,10 @@ module.exports = { where: [ { 'ct.id < ': 3, - 'ct.key ': 'foo', - }, + 'ct.key ': 'foo' + } ], - get: [], - }, - }, -}; \ No newline at end of file + get: [] + } + } +}; diff --git a/test/base_test.js b/test/base_test.js index 883813c..bcaab45 100644 --- a/test/base_test.js +++ b/test/base_test.js @@ -1,10 +1,11 @@ +/* eslint-env node, mocha */ 'use strict'; -let expect = require('chai').expect, - reload = require('require-reload')(require), - glob = require('glob'), - nodeQuery = reload('../lib/NodeQuery')(), - Adapter = reload('../lib/Adapter'); +const expect = require('chai').expect; +const reload = require('require-reload')(require); +const glob = require('glob'); +const nodeQuery = reload('../lib/NodeQuery')(); +const Adapter = reload('../lib/Adapter'); suite('Base tests -', () => { suite('Sanity check', () => { @@ -31,7 +32,7 @@ suite('Base tests -', () => { test('Invalid driver type', () => { expect(() => { reload('../lib/NodeQuery')({ - driver: 'Foo', + driver: 'Foo' }); }).to.throw(Error, 'Selected driver (Foo) does not exist!'); }); @@ -49,4 +50,4 @@ suite('Base tests -', () => { a.transformResult([]); }).to.throw(Error, 'Result transformer method not defined for current adapter'); }); -}); \ No newline at end of file +}); diff --git a/test/config-ci.json b/test/config-ci.json new file mode 100644 index 0000000..398a4fe --- /dev/null +++ b/test/config-ci.json @@ -0,0 +1,39 @@ +{ + "mysql2": { + "driver": "mysql", + "connection": { + "host": "mysql2", + "user": "test", + "password": "test", + "database": "test" + } + }, + "pg": { + "driver": "pg", + "connection": "postgres://test:test@posgres/test" + }, + "pg-object": { + "driver": "pg", + "connection": { + "database": "test" + } + }, + "dblite": { + "adapter": "dblite", + "driver": "sqlite", + "connection": ":memory:" + }, + "sqlite3": { + "driver": "sqlite", + "connection": ":memory:" + }, + "node-firebird": { + "driver": "firebird", + "connection": { + "host": "127.0.0.1", + "database": "/../FB_TEST_DB.FDB", + "user": "SYSDBA", + "password": "masterkey" + } + } +} diff --git a/test/docker_install.sh b/test/docker_install.sh new file mode 100644 index 0000000..7009d6b --- /dev/null +++ b/test/docker_install.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +# We need to install dependencies only for Docker +[[ ! -e /.dockerenv ]] && [[ ! -e /.dockerinit ]] && exit 0 + +# Where am I? +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +set -xe + +# Install sqlite3 +apt-get update -yqq +apt-get install sqlite3 libsqlite3-dev -yqq + +# Replace test config with docker config file +mv "$DIR/config-ci.json" "$DIR/config.json" \ No newline at end of file diff --git a/test/helpers_test.js b/test/helpers_test.js index 2e5a0b6..f44d4e2 100644 --- a/test/helpers_test.js +++ b/test/helpers_test.js @@ -1,9 +1,9 @@ +/* eslint-env node, mocha */ 'use strict'; -let chai = require('chai'), - assert = chai.assert, - expect = chai.expect, - should = chai.should(); +const chai = require('chai'); +const assert = chai.assert; +const expect = chai.expect; let helpers = require('../lib/helpers'); @@ -35,7 +35,7 @@ suite('Helper Module Tests -', () => { 'Function', 'RegExp', 'NaN', - 'Infinite', + 'Infinite' ]; types.forEach(type => { @@ -48,7 +48,7 @@ suite('Helper Module Tests -', () => { let trueCases = { 'Strings are scalar': 'foo', 'Booleans are scalar': true, - 'Numbers are scalar': 545, + 'Numbers are scalar': 545 }; Object.keys(trueCases).forEach(desc => { test(desc, () => { @@ -58,7 +58,7 @@ suite('Helper Module Tests -', () => { let falseCases = { 'Arrays are not scalar': [], - 'Objects are not scalar': [], + 'Objects are not scalar': [] }; Object.keys(falseCases).forEach(desc => { test(desc, () => { @@ -95,14 +95,14 @@ suite('Helper Module Tests -', () => { suite('arrayPluck -', () => { let orig = [ { - foo: 1, + foo: 1 }, { foo: 2, - bar: 10, + bar: 10 }, { foo: 3, - bar: 15, - }, + bar: 15 + } ]; test('Finding members in all objects', () => { @@ -121,11 +121,11 @@ suite('Helper Module Tests -', () => { let cases = [ { 'Dollar sign is not in any of the array items': /\$/, - 'None of the numbers in the array match /5/': /5/, + 'None of the numbers in the array match /5/': /5/ }, { '\' string \' matches /^ ?string/': /^ ?string/, - '\'apple\' matches /APPLE/i': /APPLE/i, - }, + '\'apple\' matches /APPLE/i': /APPLE/i + } ]; [0, 1].forEach(i => { diff --git a/test/mocha.opts b/test/mocha.opts index 8c8ffe8..4065ed0 100644 --- a/test/mocha.opts +++ b/test/mocha.opts @@ -3,7 +3,7 @@ --recursive --reporter nyan --slow 200 ---timeout 10000 +--timeout 25000 --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 f4d50e1..7e10dcb 100644 --- a/test/query-parser_test.js +++ b/test/query-parser_test.js @@ -1,56 +1,51 @@ +/* eslint-env node, mocha */ 'use strict'; -let expect = require('chai').expect; +const expect = require('chai').expect; // Use the base driver as a mock for testing -let getArgs = require('getargs'); -let helpers = require('../lib/helpers'); -let driver = require('../lib/Driver'); +const helpers = require('../lib/helpers'); +const driver = require('../lib/Driver'); -let P = require('../lib/QueryParser'); +const P = require('../lib/QueryParser'); let parser = new P(driver); -let State = require('../lib/State'); +const State = require('../lib/State'); // Simulate query builder state let state = new State(); -let mixedSet = function mixedSet(/* $letName, $valType, $key, [$val] */) { - const argPattern = '$letName:string, $valType:string, $key:object|string|number, [$val]'; - let args = getArgs(argPattern, arguments); - +let mixedSet = function mixedSet (letName, valType, key, val) { let obj = {}; - if (helpers.isScalar(args.$key) && !helpers.isUndefined(args.$val)) { + if (helpers.isScalar(key) && !helpers.isUndefined(val)) { // Convert key/val pair to a simple object - obj[args.$key] = args.$val; - } else if (helpers.isScalar(args.$key) && helpers.isUndefined(args.$val)) { + obj[key] = val; + } else if (helpers.isScalar(key) && helpers.isUndefined(val)) { // If just a string for the key, and no value, create a simple object with duplicate key/val - obj[args.$key] = args.$key; + obj[key] = key; } else { - obj = args.$key; + obj = 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); + if (['key', 'value'].indexOf(valType) !== -1) { + let pushVal = (valType === 'key') ? k : obj[k]; + state[letName].push(pushVal); } else { - state[args.$letName][k] = obj[k]; + state[letName][k] = obj[k]; } }); - return state[args.$letName]; + return state[letName]; }; -let whereMock = function () { - let args = getArgs('key:string|object, [val]', arguments); - +let whereMock = function (key, val) { state.whereMap = []; state.whereValues = []; - mixedSet('rawWhereValues', 'value', args.key, args.val); - mixedSet('whereMap', 'both', args.key, args.val); + mixedSet('rawWhereValues', 'value', key, val); + mixedSet('whereMap', 'both', key, val); }; // ----------------------------------------------------------------------------- @@ -86,7 +81,7 @@ suite('Query Parser Tests', () => { }); test('Has function key/val object', () => { whereMock({ - 'time <': 'SUM(FOO(BAR(\'x\')))', + 'time <': 'SUM(FOO(BAR(\'x\')))' }); parser.parseWhere(driver, state); expect(state.whereMap) @@ -94,7 +89,7 @@ suite('Query Parser Tests', () => { }); test('Has literal value', () => { whereMock({ - foo: 3, + foo: 3 }); parser.parseWhere(driver, state); expect(state.whereMap) @@ -105,7 +100,7 @@ suite('Query Parser Tests', () => { test('Has multiple literal values', () => { whereMock({ foo: 3, - bar: 5, + bar: 5 }); parser.parseWhere(driver, state); expect(state.whereMap) @@ -119,20 +114,20 @@ suite('Query Parser Tests', () => { { desc: 'Simple equals condition', join: 'table1.field1=table2.field2', - expected: ['table1.field1', '=', 'table2.field2'], + expected: ['table1.field1', '=', 'table2.field2'] }, { desc: 'Db.table.field condition', join: 'db1.table1.field1!=db2.table2.field2', - expected: ['db1.table1.field1', '!=', 'db2.table2.field2'], + expected: ['db1.table1.field1', '!=', 'db2.table2.field2'] }, { desc: 'Underscore in identifier', join: 'table_1.field1 = tab_le2.field_2', - expected: ['table_1.field1', '=', 'tab_le2.field_2'], + expected: ['table_1.field1', '=', 'tab_le2.field_2'] }, { desc: 'Function in condition', join: 'table1.field1 > SUM(3+6)', - expected: ['table1.field1', '>', 'SUM(3+6)'], - }, + expected: ['table1.field1', '>', 'SUM(3+6)'] + } ]; data.forEach(datum => { @@ -147,20 +142,20 @@ suite('Query Parser Tests', () => { { desc: 'Simple equals condition', clause: 'table1.field1=table2.field2', - expected: '"table1"."field1" = "table2"."field2"', + expected: '"table1"."field1" = "table2"."field2"' }, { desc: 'Db.table.field condition', clause: 'db1.table1.field1!=db2.table2.field2', - expected: '"db1"."table1"."field1" != "db2"."table2"."field2"', + expected: '"db1"."table1"."field1" != "db2"."table2"."field2"' }, { desc: 'Underscore in identifier', clause: 'table_1.field1 = tab_le2.field_2', - expected: '"table_1"."field1" = "tab_le2"."field_2"', + expected: '"table_1"."field1" = "tab_le2"."field_2"' }, { desc: 'Function in condition', clause: 'table1.field1 > SUM(3+6)', - expected: '"table1"."field1" > SUM(3+6)', - }, + expected: '"table1"."field1" > SUM(3+6)' + } ]; data.forEach(datum => { @@ -170,4 +165,4 @@ suite('Query Parser Tests', () => { }); }); }); -}); \ No newline at end of file +}); diff --git a/test/sql/mssql.sql b/test/sql/mssql.sql new file mode 100644 index 0000000..562c9b0 --- /dev/null +++ b/test/sql/mssql.sql @@ -0,0 +1,15 @@ +DROP TABLE IF EXISTS [create_join]; +DROP TABLE IF EXISTS [create_test]; + +-- Table create_join +CREATE TABLE [create_join] ( + [id] INTEGER, + [key] VARCHAR(255), + [val] NVARCHAR(2048) +); + +CREATE TABLE [create_test] ( + [id] INTEGER, + [key] VARCHAR(255), + [val] NVARCHAR(2048) +); \ No newline at end of file diff --git a/test/sql/mysql.sql b/test/sql/mysql.sql index d856f1b..5b06c28 100644 --- a/test/sql/mysql.sql +++ b/test/sql/mysql.sql @@ -1,11 +1,10 @@ -- sample data to test MySQL - -- -- Table structure for table `create_join` -- -DROP TABLE IF EXISTS `create_join`; +DROP TABLE IF EXISTS `create_join` CASCADE; CREATE TABLE `create_join` ( `id` int(10) NOT NULL, `key` text, @@ -18,7 +17,7 @@ ALTER TABLE `create_join` -- Table structure for table `create_test` -- -DROP TABLE IF EXISTS `create_test`; +DROP TABLE IF EXISTS `create_test` CASCADE; CREATE TABLE `create_test` ( `id` int(10) NOT NULL, `key` text, diff --git a/test/sql/pgsql.sql b/test/sql/pgsql.sql index ab30e87..09ad873 100644 --- a/test/sql/pgsql.sql +++ b/test/sql/pgsql.sql @@ -1,13 +1,15 @@ -- sample data to test PostgreSQL INFORMATION_SCHEMA +DROP TABLE IF EXISTS "create_join" CASCADE; +DROP TABLE IF EXISTS "create_test" CASCADE; -- Table create_join -CREATE TABLE create_join ( +CREATE TABLE "create_join" ( id integer NOT NULL, key text, val text ); -CREATE TABLE create_test ( +CREATE TABLE "create_test" ( id integer NOT NULL, key text, val text