diff --git a/.travis.yml b/.travis.yml index b22fa3e..0f60068 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,11 +4,10 @@ sudo: false node_js: - "iojs" - "node" + - "5.1" + - "5.0" - "4.1" - "4.0" - - "0.12" - - "0.11" - - "0.10" before_script: - npm install -g gulp @@ -18,4 +17,8 @@ before_script: - mysql -v -uroot test < ./tests/sql/mysql.sql - psql test postgres -f ./tests/sql/pgsql.sql -script: gulp \ No newline at end of file +script: gulp + +matrix: + allow_failures: + - node_js: iojs \ No newline at end of file diff --git a/docs/index.html b/docs/index.html index 08c0a43..35f4112 100644 --- a/docs/index.html +++ b/docs/index.html @@ -31,16 +31,6 @@ class='block bold'> State - - State - - - _appendMap - @@ -313,62 +303,6 @@ -
-

- State(Driver, driver) -

- -

Parameters

-
    -
  • Driver - : - -

    The driver object for the database in use

    - -
    -
  • -
  • driver - : - - - -
  • -
-
-
-

- _appendMap(conjunction, string, type) -

-

Append a clause to the query map

- -

Parameters

-
-

Returns

- void - - - - -

helpers(o) @@ -429,9 +363,10 @@ function name, eg isNumber

Returns

Array - + : - +

The new array of plucked values

+
@@ -458,15 +393,17 @@ function name, eg isNumber

  • mixed obj : - +

    Object to test

    +
  • Returns

    bool - + : - +

    Is object scalar

    +
    @@ -576,15 +513,17 @@ in the passed array

  • mixed o : - +

    Object to type check

    +
  • Returns

    String - + : - +

    Type of the object

    +
    @@ -611,15 +550,17 @@ in the passed array

  • String str : - +

    The string to modify

    +
  • Returns

    String - + : - +

    The modified string

    +
    @@ -651,22 +592,16 @@ in the passed array

    - constructor(Driver, driver) + constructor(driver)

    Parameters

    @@ -738,15 +673,17 @@ in the passed array

  • Array array : - +

    Set of possible matches

    +
  • Returns

    Array or - + : - +

    Filtered set of possible matches

    +
    @@ -767,9 +704,10 @@ in the passed array

    Returns

    QueryBuilder - + : - +

    The Query Builder object, for chaining

    +
    @@ -842,9 +780,10 @@ in the passed array

    Returns

    String - + : - +

    The compiled sql statement

    +
    @@ -873,9 +812,10 @@ in the passed array

    Returns

    String - + : - +

    The compiled sql statement

    +
    @@ -904,9 +844,10 @@ in the passed array

    Returns

    String - + : - +

    The compiled sql statement

    +
    @@ -935,9 +876,10 @@ in the passed array

    Returns

    String - + : - +

    The compiled sql statement

    +
    @@ -951,15 +893,17 @@ in the passed array

  • String or Array field : - +

    The name of the field to group by

    +
  • Returns

    QueryBuilder - + : - +

    The Query Builder object, for chaining

    +
    @@ -970,9 +914,10 @@ in the passed array

    Returns

    QueryBuilder - + : - +

    The Query Builder object, for chaining

    +
    @@ -983,9 +928,10 @@ in the passed array

    Returns

    QueryBuilder - + : - +

    The Query Builder object, for chaining

    +
    @@ -1007,9 +953,10 @@ If there are no matches, return null

    Returns

    Array or - + : - +

    List of operators

    +
    @@ -1037,9 +984,10 @@ If there are no matches, return null

    Returns

    QueryBuilder - + : - +

    The Query Builder object, for chaining

    +
    @@ -1151,9 +1099,10 @@ If there are no matches, return null

    Returns

    QueryBuilder - + : - +

    The Query Builder object, for chaining

    +
    @@ -1189,9 +1138,10 @@ If there are no matches, return null

    Returns

    QueryBuilder - + : - +

    The Query Builder object, for chaining

    +
    @@ -1219,9 +1169,10 @@ If there are no matches, return null

    Returns

    QueryBuilder - + : - +

    The Query Builder object, for chaining

    +
    @@ -1257,9 +1208,10 @@ If there are no matches, return null

    Returns

    QueryBuilder - + : - +

    The Query Builder object, for chaining

    +
    @@ -1271,9 +1223,10 @@ prefixed with 'OR'

    Returns

    QueryBuilder - + : - +

    The Query Builder object, for chaining

    +
    @@ -1301,9 +1254,10 @@ prefixed with 'OR'

    Returns

    QueryBuilder - + : - +

    The Query Builder object, for chaining

    +
    @@ -1339,9 +1293,10 @@ prefixed with 'OR'

    Returns

    QueryBuilder - + : - +

    The Query Builder object, for chaining

    +
    @@ -1353,9 +1308,10 @@ prefixed with 'OR NOT'

    Returns

    QueryBuilder - + : - +

    The Query Builder object, for chaining

    +
    @@ -1391,9 +1347,10 @@ prefixed with 'OR NOT'

    Returns

    QueryBuilder - + : - +

    The Query Builder object, for chaining

    +
    @@ -1421,9 +1378,10 @@ prefixed with 'OR NOT'

    Returns

    QueryBuilder - + : - +

    The Query Builder object, for chaining

    +
    @@ -1451,9 +1409,10 @@ prefixed with 'OR NOT'

    Returns

    QueryBuilder - + : - +

    The Query Builder object, for chaining

    +
    @@ -1467,15 +1426,17 @@ prefixed with 'OR NOT'

  • String field : - +

    The name of the field

    +
  • Returns

    QueryBuilder - + : - +

    The Query Builder object, for chaining

    +
    @@ -1489,15 +1450,17 @@ prefixed with 'OR NOT'

  • String field : - +

    The name of the field

    +
  • Returns

    QueryBuilder - + : - +

    The Query Builder object, for chaining

    +
    @@ -1525,9 +1488,10 @@ prefixed with 'OR NOT'

    Returns

    QueryBuilder - + : - +

    The Query Builder object, for chaining

    +
    @@ -1556,9 +1520,10 @@ prefixed with 'OR NOT'

    Returns

    QueryBuilder - + : - +

    The Query Builder object, for chaining

    +
    @@ -1572,15 +1537,17 @@ prefixed with 'OR NOT'

  • String sql : - +

    Join sql to parse

    +
  • Returns

    Object - + : - +

    Join condition components

    +
    @@ -1594,13 +1561,15 @@ prefixed with 'OR NOT'

  • Driver driver : - +

    The current db driver

    +
  • State state : - +

    Query Builder state object

    +
  • @@ -1643,9 +1612,10 @@ prefixed with 'OR NOT'

    Returns

    QueryBuilder - + : - +

    The Query Builder object, for chaining

    +
    @@ -1673,9 +1643,10 @@ prefixed with 'OR NOT'

    Returns

    QueryBuilder - + : - +

    The Query Builder object, for chaining

    +
    @@ -1740,9 +1711,10 @@ prefixed with 'OR NOT'

    Returns

    QueryBuilder - + : - +

    The Query Builder object, for chaining

    +
    @@ -1770,9 +1742,10 @@ prefixed with 'OR NOT'

    Returns

    QueryBuilder - + : - +

    The Query Builder object, for chaining

    +
    @@ -1786,15 +1759,17 @@ prefixed with 'OR NOT'

  • String field : - +

    The name so the field that is not to be null

    +
  • Returns

    QueryBuilder - + : - +

    The Query Builder object, for chaining

    +
    @@ -1815,9 +1790,10 @@ prefixed with 'OR NOT'

    Returns

    QueryBuilder - + : - +

    The Query Builder object, for chaining

    +
    @@ -1845,9 +1821,10 @@ prefixed with 'OR NOT'

    Returns

    QueryBuilder - + : - +

    The Query Builder object, for chaining

    +
    diff --git a/gulpfile.js b/gulpfile.js index 4011e1f..9aafd5d 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -1,94 +1,72 @@ -var babel = require('gulp-babel'), - concat = require('gulp-concat'), - documentation = require('gulp-documentation'), +'use strict'; + +const documentation = require('gulp-documentation'), eslint = require('gulp-eslint'), gulp = require('gulp'), - istanbul = require('gulp-babel-istanbul'), - nodeunit_runner = require('gulp-nodeunit-runner'), - sloc = require('gulp-sloc'), - sourcemaps = require('gulp-sourcemaps'); + istanbul = require('gulp-istanbul'), + mocha = require('gulp-mocha'), + pipe = require('gulp-pipe'), + sloc = require('gulp-sloc'); -gulp.task('transpile', function() { - return gulp.src('src/**/*.js') - .pipe(sourcemaps.init()) - .pipe(babel({ - presets: ['es2015'], - plugins: ['transform-es2015-modules-commonjs'] - })) - .pipe(sourcemaps.write('.')) - .pipe(gulp.dest('lib')); +const SRC_FILES = ['lib/**/*.js']; +const TEST_FILES = ['test/**/*_test.js']; + +const ESLINT_SETTINGS = { + "env": { + "node": true, + "es6": true + }, + "rules": { + "arrow-parens": [2, "as-needed"], + "no-console": [1], + "no-constant-condition": [1], + "no-extra-semi": [1], + "no-func-assign": [1], + "no-unexpected-multiline" : [2], + "radix": [2], + "no-with": [2], + "no-eval": [2], + "no-unreachable": [1], + "no-irregular-whitespace": [1], + "no-new-wrappers": [2], + "no-new-func": [2], + "curly" : [2, "multi-line"], + "no-implied-eval": [2], + "no-invalid-this": [2], + "constructor-super": [2], + "no-dupe-args": [2], + "no-dupe-keys": [2], + "no-dupe-class-members": [2], + "no-this-before-super": [2], + "prefer-arrow-callback": [1], + "no-var": [2], + "valid-jsdoc": [1], + "strict": [2, "global"], + "callback-return": [1] + } +}; + +gulp.task('lint', () => { + return pipe(gulp.src(SRC_FILES), [ + eslint(ESLINT_SETTINGS), + eslint.format(), + eslint.failAfterError() + ]); }); -gulp.task('lint', function() { - return gulp.src('src/**/*.js') - .pipe(eslint({ - "env": { - "node": true, - "es6": true - }, - "ecmaFeatures": { - "arrowFunctions": true, - "blockBindings": true, - "classes": true, - "defaultParams": true, - "destructuring": true, - "forOf": true, - "modules": true - }, - "rules": { - "radix": [2], - "no-with": [2], - "no-eval": [2], - "no-unreachable": [2], - "no-irregular-whitespace": [1], - "no-new-wrappers": [2], - "curly" : [2, "multi-line"], - "no-implied-eval": [2], - "no-invalid-this": [2], - "constructor-super": [2], - "no-dupe-class-members": [2], - "no-this-before-super": [2], - "prefer-arrow-callback": [1], - "no-var": [1], - "valid-jsdoc": [1] - } - })) - .pipe(eslint.format()) - .pipe(eslint.failAfterError()); +gulp.task('lint-tests', ['lint'], () => { + return pipe(gulp.src(TEST_FILES), [ + eslint(ESLINT_SETTINGS), + eslint.format(), + eslint.failAfterError() + ]); }); -gulp.task('lint-tests', ['lint'], function() { - return gulp.src('tests/**/*.js') - .pipe(eslint({ - "env": { - "node": true - }, - "rules": { - "radix": [2], - "no-with": [2], - "no-eval": [2], - "no-unreachable": [1], - "no-irregular-whitespace": [1], - "curly" : [2, "multi-line"], - "no-implied-eval": [2], - "no-invalid-this": [2], - "no-dupe-class-members": [2], - "block-scoped-var": [2] - } - })) - .pipe(eslint.format()) - .pipe(eslint.failAfterError()); -}); +gulp.task('sloc', () => gulp.src(SRC_FILES).pipe(sloc())); +gulp.task('test-sloc', () => gulp.src(TEST_FILES).pipe(sloc())); -gulp.task('sloc', ['transpile'], function() { - gulp.src(['src/**/*.js']) - .pipe(sloc()); - gulp.src(['lib/**/*.js']) - .pipe(sloc()); -}) - -gulp.task('docs', ['transpile'], function() { - gulp.src('./src/QueryBuilder.js') +gulp.task('docs', () => { + gulp.src('./lib/QueryBuilder.js') .pipe(documentation({format: 'html'})) .pipe(gulp.dest('docs')); /*gulp.src('./lib/QueryBuilder.js') @@ -96,28 +74,32 @@ gulp.task('docs', ['transpile'], function() { .pipe(gulp.dest('api-docs'));*/ }); -gulp.task('nodeunit', ['transpile', 'lint-tests'], function() { - return gulp.src(['tests/**/*_test.js']) - .pipe(nodeunit_runner()); +gulp.task('mocha', ['lint-tests', 'sloc'], () => { + return gulp.src(TEST_FILES) + .pipe(mocha({ + ui: 'tdd', + bail: true, + //reporter: 'dot', + //reporter: 'landing', + })); }); -gulp.task('fast-test', ['transpile', 'lint-tests'], function() { - return gulp.src(['tests/**/*_test.js']) - .pipe(nodeunit_runner()); -}); - -gulp.task('test', ['transpile', 'lint-tests'], function(cb) { - return gulp.src(['lib/**/*.js']) - .pipe(istanbul()) - .pipe(istanbul.hookRequire()) - .on('finish', function () { - gulp.src(['tests/**/*_test.js']) - .pipe(nodeunit_runner()) - .pipe(istanbul.writeReports({ - dir: './coverage', - reporters: ['lcov', 'lcovonly', 'html', 'text'] - })); - }); +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({ + ui: 'tdd', + bail: true + }), + istanbul.writeReports({ + dir: './coverage', + reporters: ['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 bd29e2a..bb1627d 100755 --- a/lib/Adapter.js +++ b/lib/Adapter.js @@ -1,42 +1,26 @@ -'use strict' +'use strict'; /** @module Adapter */ -; - -var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -module.exports = (function () { +module.exports = class Adapter { /** - * Invoke an adapter - * - * @param {Object} instance - The connection objec - * @return {void} - */ - - function Adapter(instance) { - _classCallCheck(this, Adapter); - + * Invoke an adapter + * + * @param {Object} instance - The connection objec + * @return {void} + */ + constructor(instance) { this.instance = 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 - * @param {Function} callback - Callback to run when a response is recieved - * @return {void} - */ - - _createClass(Adapter, [{ - key: "execute", - value: function execute() /*sql, params, callback*/{ - throw new Error("Correct adapter not defined for query execution"); - } - }]); - - return Adapter; -})(); -//# sourceMappingURL=Adapter.js.map + * 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} + */ + execute(/*sql, params, callback*/) { + throw new Error("Correct adapter not defined for query execution"); + } +} \ No newline at end of file diff --git a/lib/Adapter.js.map b/lib/Adapter.js.map deleted file mode 100644 index 06bc225..0000000 --- a/lib/Adapter.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["Adapter.js"],"names":[],"mappings":"AAAA;;;AAAY,CAAC;;;;;;AAGb,MAAM,CAAC,OAAO;;;;;;;;AAOb,UAPsB,OAAO,CAOjB,QAAQ,EAAE;wBAPA,OAAO;;AAQ5B,MAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;EACzB;;;;;;;;;;AAAA;cATqB,OAAO;;qDAmBM;AAClC,SAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;GACnE;;;QArBqB,OAAO;IAsB7B,CAAA","file":"Adapter.js","sourcesContent":["'use strict';\n\n/** @module Adapter */\nmodule.exports = class Adapter {\n\t/**\n\t * Invoke an adapter\n\t *\n\t * @param {Object} instance - The connection objec\n\t * @return {void}\n\t */\n\tconstructor(instance) {\n\t\tthis.instance = instance;\n\t}\n\n\t/**\n\t * Run the sql query as a prepared statement\n\t *\n\t * @param {String} sql - The sql with placeholders\n\t * @param {Array} params - The values to insert into the query\n\t * @param {Function} callback - Callback to run when a response is recieved\n\t * @return {void}\n\t */\n\texecute(/*sql, params, callback*/) {\n\t\tthrow new Error(\"Correct adapter not defined for query execution\");\n\t}\n}"],"sourceRoot":"/source/"} \ No newline at end of file diff --git a/lib/DriverBase.js b/lib/DriverBase.js index 11ddacf..48884bd 100755 --- a/lib/DriverBase.js +++ b/lib/DriverBase.js @@ -1,78 +1,84 @@ 'use strict'; -var _helpers = require('./helpers'); - -var _helpers2 = _interopRequireDefault(_helpers); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } +let helpers = require('./helpers'); /** * Base Database Driver * * @module driver */ -var d = { +let d = { identifierStartChar: '"', identifierEndChar: '"', tablePrefix: null, hasTruncate: true, /** - * Low level function for naive quoting of strings - * @param {String} str - * @return {String} - * @private - */ - _quote: function _quote(str) { - return _helpers2.default.isString(str) && !(str.startsWith(d.identifierStartChar) || str.endsWith(d.identifierEndChar)) ? d.identifierStartChar + str + d.identifierEndChar : str; + * Low level function for naive quoting of strings + + * @param {String} str - The sql fragment to quote + * @return {String} - The quoted sql fragment + * @private + */ + _quote: function(str) { + return (helpers.isString(str) && ! (str.startsWith(d.identifierStartChar) || str.endsWith(d.identifierEndChar))) + ? `${d.identifierStartChar}${str}${d.identifierEndChar}` + : str; }, /** - * Set the limit clause - * @param {String} sql - * @param {Number} limit - * @param {Number|null} offset - * @return {String} - */ - limit: function limit(sql, _limit, offset) { - sql += " LIMIT " + _limit; + * Set the limit clause - if (_helpers2.default.isNumber(offset)) { - sql += " OFFSET " + offset; + * @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 + */ + limit: function(sql, limit, offset) { + sql += ` LIMIT ${limit}`; + + if (helpers.isNumber(offset)) + { + sql += ` OFFSET ${offset}`; } return sql; }, /** - * Quote database table name, and set prefix - * - * @param {String} table - * @return {String} - */ - quoteTable: function quoteTable(table) { + * Quote database table name, and set prefix + * + * @param {String} table - Table name to quote + * @return {String} - Quoted table name + */ + quoteTable: function(table) { // Quote after prefix return d.quoteIdentifiers(table); }, /** - * Use the driver's escape character to quote identifiers - * - * @param {String|Array} - * @return {String|Array} - */ - quoteIdentifiers: function quoteIdentifiers(str) { - var hiers, raw; - var pattern = new RegExp(d.identifierStartChar + '(' + '([a-zA-Z0-9_]+)' + '(\((.*?)\))' + ')' + d.identifierEndChar, 'ig'); + * Use the driver's escape character to quote identifiers + * + * @param {String|String[]} - String or array of strings to quote identifiers + * @return {String|String[]} - Quoted identifier(s) + */ + quoteIdentifiers: function(str) { + let hiers, raw; + let pattern = new RegExp( + `${d.identifierStartChar}(` + + '([a-zA-Z0-9_]+)' + '(\((.*?)\))' + + `)${d.identifierEndChar}`, 'ig'); // Recurse for arrays of identifiiers - if (Array.isArray(str)) { + if (Array.isArray(str)) + { return str.map(d.quoteIdentifiers); } // Handle commas - if (str.includes(',')) { - var parts = str.split(',').map(_helpers2.default.stringTrim); + if (str.includes(',')) + { + let parts = str.split(',').map(helpers.stringTrim); str = parts.map(d.quoteIdentifiers).join(','); } @@ -81,14 +87,15 @@ var d = { raw = hiers.join('.'); // Fix functions - if (raw.includes('(') && raw.includes(')')) { - var funcs = pattern.exec(raw); + if (raw.includes('(') && raw.includes(')')) + { + let funcs = pattern.exec(raw); // Unquote the function raw = raw.replace(funcs[0], funcs[1]); // Quote the identifiers inside of the parens - var inParens = funcs[3].substring(1, funcs[3].length - 1); + let inParens = funcs[3].substring(1, funcs[3].length -1); raw = raw.replace(inParens, d.quoteIdentifiers(inParens)); } @@ -96,13 +103,15 @@ var d = { }, /** - * SQL to truncate the passed table - * - * @param {String} table - * @return {String} - sql - */ - truncate: function truncate(table) { - var sql = d.hasTruncate ? 'TRUNCATE ' : 'DELETE FROM '; + * SQL to truncate the passed table + * + * @param {String} table - Table to truncate + * @return {String} - Truncation SQL + */ + truncate: function(table) { + let sql = (d.hasTruncate) + ? 'TRUNCATE ' + : 'DELETE FROM '; sql += d.quoteTable(table); @@ -110,24 +119,24 @@ var d = { }, /** - * SQL to insert a group of rows - * - * @param {String} table - The table to insert to - * @param {Array} [data] - The array of object containing data to insert - * @return {String} - */ - insertBatch: function insertBatch(table, data) { - var vals = [], - fields = Object.keys(data[0]), - sql = "", - params = [], - paramString = "", - paramList = []; + * SQL to insert a group of rows + * + * @param {String} table - The table to insert to + * @param {Array} [data] - The array of object containing data to insert + * @return {String} - Query and data to insert + */ + insertBatch: function(table, data) { + let vals = [], + fields = Object.keys(data[0]), + sql = "", + params = [], + paramString = "", + paramList = []; // Get the data values to insert, so they can // be parameterized - data.forEach(function (obj) { - Object.keys(obj).forEach(function (key) { + data.forEach(obj => { + Object.keys(obj).forEach(key => { vals.push(obj[key]); }); }); @@ -136,7 +145,9 @@ var d = { // object inserted table = d.quoteTable(table); - sql += "INSERT INTO " + table + " (" + d.quoteIdentifiers(fields).join(",") + ") VALUES "; + sql += `INSERT INTO ${table} (` + + d.quoteIdentifiers(fields).join(",") + + ") VALUES "; // Create placeholder groups params = Array(fields.length).fill('?'); @@ -153,5 +164,4 @@ var d = { }; -module.exports = d; -//# sourceMappingURL=DriverBase.js.map +module.exports = d; \ No newline at end of file diff --git a/lib/DriverBase.js.map b/lib/DriverBase.js.map deleted file mode 100644 index fdbcf48..0000000 --- a/lib/DriverBase.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["DriverBase.js"],"names":[],"mappings":"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","file":"DriverBase.js","sourcesContent":["'use strict';\n\nimport helpers from './helpers'\n\n/**\n * Base Database Driver\n *\n * @module driver\n */\nvar d = {\n\tidentifierStartChar: '\"',\n\tidentifierEndChar: '\"',\n\ttablePrefix: null,\n\thasTruncate: true,\n\n\t/**\n\t * Low level function for naive quoting of strings\n\n\t * @param {String} str\n\t * @return {String}\n\t * @private\n\t */\n\t_quote: function(str) {\n\t\treturn (helpers.isString(str) && ! (str.startsWith(d.identifierStartChar) || str.endsWith(d.identifierEndChar)))\n\t\t\t? d.identifierStartChar + str + d.identifierEndChar\n\t\t\t: str;\n\t},\n\n\t/**\n\t * Set the limit clause\n\n\t * @param {String} sql\n\t * @param {Number} limit\n\t * @param {Number|null} offset\n\t * @return {String}\n\t */\n\tlimit: function(sql, limit, offset) {\n\t\tsql += \" LIMIT \" + limit;\n\n\t\tif (helpers.isNumber(offset))\n\t\t{\n\t\t\tsql += \" OFFSET \" + offset;\n\t\t}\n\n\t\treturn sql;\n\t},\n\n\t/**\n\t * Quote database table name, and set prefix\n\t *\n\t * @param {String} table\n\t * @return {String}\n\t */\n\tquoteTable: function(table) {\n\t\t// Quote after prefix\n\t\treturn d.quoteIdentifiers(table);\n\t},\n\n\t/**\n\t * Use the driver's escape character to quote identifiers\n\t *\n\t * @param {String|Array}\n\t * @return {String|Array}\n\t */\n\tquoteIdentifiers: function(str) {\n\t\tvar hiers, raw;\n\t\tvar pattern = new RegExp(d.identifierStartChar + '(' + '([a-zA-Z0-9_]+)' + '(\\((.*?)\\))' + ')' + d.identifierEndChar, 'ig');\n\n\t\t// Recurse for arrays of identifiiers\n\t\tif (Array.isArray(str))\n\t\t{\n\t\t\treturn str.map(d.quoteIdentifiers);\n\t\t}\n\n\t\t// Handle commas\n\t\tif (str.includes(','))\n\t\t{\n\t\t\tvar parts = str.split(',').map(helpers.stringTrim);\n\t\t\tstr = parts.map(d.quoteIdentifiers).join(',');\n\t\t}\n\n\t\t// Split identifiers by period\n\t\thiers = str.split('.').map(d._quote);\n\t\traw = hiers.join('.');\n\n\t\t// Fix functions\n\t\tif (raw.includes('(') && raw.includes(')'))\n\t\t{\n\t\t\tvar funcs = pattern.exec(raw);\n\n\t\t\t// Unquote the function\n\t\t\traw = raw.replace(funcs[0], funcs[1]);\n\n\t\t\t// Quote the identifiers inside of the parens\n\t\t\tvar inParens = funcs[3].substring(1, funcs[3].length -1);\n\t\t\traw = raw.replace(inParens, d.quoteIdentifiers(inParens));\n\t\t}\n\n\t\treturn raw;\n\t},\n\n\t/**\n\t * SQL to truncate the passed table\n\t *\n\t * @param {String} table\n\t * @return {String} - sql\n\t */\n\ttruncate: function(table) {\n\t\tvar sql = (d.hasTruncate)\n\t\t\t? 'TRUNCATE '\n\t\t\t: 'DELETE FROM ';\n\n\t\tsql += d.quoteTable(table);\n\n\t\treturn sql;\n\t},\n\n\t/**\n\t * SQL to insert a group of rows\n\t *\n\t * @param {String} table - The table to insert to\n\t * @param {Array} [data] - The array of object containing data to insert\n\t * @return {String}\n\t */\n\tinsertBatch: function(table, data) {\n\t\tvar vals = [],\n\t\t\tfields = Object.keys(data[0]),\n\t\t\tsql = \"\",\n\t\t\tparams = [],\n\t\t\tparamString = \"\",\n\t\t\tparamList = [];\n\n\t\t// Get the data values to insert, so they can\n\t\t// be parameterized\n\t\tdata.forEach(function(obj) {\n\t\t\tObject.keys(obj).forEach(function(key) {\n\t\t\t\tvals.push(obj[key]);\n\t\t\t});\n\t\t});\n\n\t\t// Get the field names from the keys of the first\n\t\t// object inserted\n\t\ttable = d.quoteTable(table);\n\n\t\tsql += \"INSERT INTO \" + table + \" (\"\n\t\t\t+ d.quoteIdentifiers(fields).join(\",\")\n\t\t\t+ \") VALUES \";\n\n\t\t// Create placeholder groups\n\t\tparams = Array(fields.length).fill('?');\n\t\tparamString = \"(\" + params.join(',') + \")\";\n\t\tparamList = Array(data.length).fill(paramString);\n\n\t\tsql += paramList.join(',');\n\n\t\treturn {\n\t\t\tsql: sql,\n\t\t\tvalues: vals\n\t\t};\n\t}\n\n};\n\nmodule.exports = d;"],"sourceRoot":"/source/"} \ No newline at end of file diff --git a/lib/DriverClass.js b/lib/DriverClass.js index 3029d7a..4401867 100644 --- a/lib/DriverClass.js +++ b/lib/DriverClass.js @@ -1,22 +1,18 @@ 'use strict'; -var _DriverBase = require('./DriverBase'); +let driverBase = require('./DriverBase'), + getArgs = require('getargs'); -var _DriverBase2 = _interopRequireDefault(_DriverBase); +module.exports = class DriverClass { + constructor(/* properties:object */) { + let args = getArgs('[properties]:object', arguments); -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + args.properties = args.properties || {}; -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -module.exports = function DriverClass() { - var _this = this; - - var properties = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; - - _classCallCheck(this, DriverClass); - - Object.keys(_DriverBase2.default).forEach(function (key) { - _this[key] = Object.keys(properties).indexOf(key) !== -1 ? properties[key] : _DriverBase2.default[key]; - }); -}; -//# sourceMappingURL=DriverClass.js.map + Object.keys(driverBase).forEach(key => { + this[key] = (Object.keys(args.properties).indexOf(key) !== -1) + ? args.properties[key] + : driverBase[key]; + }); + } +} \ No newline at end of file diff --git a/lib/DriverClass.js.map b/lib/DriverClass.js.map deleted file mode 100644 index 278b095..0000000 --- a/lib/DriverClass.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["DriverClass.js"],"names":[],"mappings":"AAAA,YAAY,CAAC;;;;;;;;;;AAIb,MAAM,CAAC,OAAO,GACb,SADsB,WAAW,GACJ;;;KAAjB,UAAU,yDAAG,EAAE;;uBADL,WAAW;;AAEhC,OAAM,CAAC,IAAI,sBAAY,CAAC,OAAO,CAAC,UAAC,GAAG,EAAK;AACxC,QAAK,GAAG,CAAC,GAAG,AAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,GACrD,UAAU,CAAC,GAAG,CAAC,GACf,qBAAW,GAAG,CAAC,CAAC;EACnB,CAAC,CAAC;CACH,AACD,CAAA","file":"DriverClass.js","sourcesContent":["'use strict';\n\nimport driverBase from './DriverBase';\n\nmodule.exports = class DriverClass {\n\tconstructor(properties = {}) {\n\t\tObject.keys(driverBase).forEach((key) => {\n\t\t\tthis[key] = (Object.keys(properties).indexOf(key) !== -1)\n\t\t\t\t? properties[key]\n\t\t\t\t: driverBase[key];\n\t\t});\n\t}\n}"],"sourceRoot":"/source/"} \ No newline at end of file diff --git a/lib/NodeQuery.js b/lib/NodeQuery.js index 2531bd4..d49c8fc 100755 --- a/lib/NodeQuery.js +++ b/lib/NodeQuery.js @@ -1,103 +1,74 @@ "use strict"; -var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); - -var _fs = require('fs'); - -var _fs2 = _interopRequireDefault(_fs); - -var _helpers = require('./helpers'); - -var _helpers2 = _interopRequireDefault(_helpers); - -var _QueryBuilder = require('./QueryBuilder'); - -var _QueryBuilder2 = _interopRequireDefault(_QueryBuilder); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -var instance = null; +let instance = null; +let fs = require('fs'), + helpers = require('./helpers'), + QueryBuilder = require('./QueryBuilder'); /** * @module NodeQuery */ - -var NodeQuery = (function () { +class NodeQuery { /** - * Constructor - */ - - function NodeQuery() { - _classCallCheck(this, NodeQuery); - + * Constructor + * + * @return {void} + */ + constructor() { this.instance = null; } /** - * Create a query builder object - * - * @memberOf NodeQuery - * @param {String} drivername - The name of the database type, eg. mysql or pg - * @param {Object} connObject - A connection object from the database library you are connecting with - * @param {String} [connLib] - The name of the db connection library you are using, eg. mysql or mysql2. Optional if the same as drivername - * @return {QueryBuilder} - */ + * Create a query builder object + * + * @memberOf NodeQuery + * @param {String} driverType - The name of the database type, eg. mysql or pg + * @param {Object} connObject - A connection object from the database library you are connecting with + * @param {String} [connLib] - The name of the db connection library you are using, eg. mysql or mysql2. Optional if the same as drivername + * @return {QueryBuilder} - The Query Builder object + */ + init(driverType, connObject, connLib) { + connLib = connLib || driverType; - _createClass(NodeQuery, [{ - key: 'init', - value: function init(driverType, connObject, connLib) { - connLib = connLib || driverType; + let paths = { + driver: `${__dirname}/drivers/` + helpers.upperCaseFirst(driverType), + adapter: `${__dirname}/adapters/${connLib}` + }; - var paths = { - driver: __dirname + '/drivers/' + _helpers2.default.upperCaseFirst(driverType), - adapter: __dirname + '/adapters/' + connLib - }; - - /*Object.keys(paths).forEach((type) => { - if ( ! fs.existsSync(paths[type])) - { - console.log(paths[type]); - throw new Error( - `Selected ${type} (` + - helpers.upperCaseFirst(driverType) + - `) does not exist!` - ); - } - });*/ - - var driver = require(paths.driver); - var $adapter = require(paths.adapter); - var adapter = new $adapter(connObject); - - this.instance = new _QueryBuilder2.default(driver, adapter); - - return this.instance; - } - }, { - key: 'getQuery', - - /** - * Return an existing query builder instance - * - * @memberOf NodeQuery - * @return {QueryBuilder} - */ - value: function getQuery() { - if (!this.instance) { - throw new Error("No Query Builder instance to return"); + Object.keys(paths).forEach(type => { + if ( ! fs.existsSync(paths[type])) + { + throw new Error( + `Selected ${type} (` + + helpers.upperCaseFirst(driverType) + + `) does not exist!` + ); } + }); - return this.instance; + let driver = require(paths.driver); + let $adapter = require(paths.adapter); + let adapter = new $adapter(connObject); + + this.instance = new QueryBuilder(driver, adapter); + + return this.instance; + } + + /** + * Return an existing query builder instance + * + * @return {QueryBuilder} - The Query Builder object + */ + getQuery() { + if ( ! this.instance) { + throw new Error("No Query Builder instance to return"); } - }]); - return NodeQuery; -})(); + return this.instance; + } -; +} -module.exports = new NodeQuery(); -//# sourceMappingURL=NodeQuery.js.map +module.exports = new NodeQuery(); \ No newline at end of file diff --git a/lib/NodeQuery.js.map b/lib/NodeQuery.js.map deleted file mode 100644 index 00d5f09..0000000 --- a/lib/NodeQuery.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["NodeQuery.js"],"names":[],"mappings":"AAAA,YAAY,CAAC;;;;;;;;;;;;;;;;;;;;AAEb,IAAI,QAAQ,GAAG,IAAI,CAAC;;;;;;IAQd,SAAS;;;;;;AAKd,UALK,SAAS,GAKA;wBALT,SAAS;;AAMb,MAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;EACrB;;;;;;;;;;;AAAA;cAPI,SAAS;;uBAkBT,UAAU,EAAE,UAAU,EAAE,OAAO,EAAE;AACrC,UAAO,GAAG,OAAO,IAAI,UAAU,CAAC;;AAEhC,OAAI,KAAK,GAAG;AACX,UAAM,EAAE,AAAG,SAAS,iBAAc,kBAAQ,cAAc,CAAC,UAAU,CAAC;AACpE,WAAO,EAAK,SAAS,kBAAa,OAAO,AAAE;IAC3C;;;;;;;;;;;;;;AAAC,AAcF,OAAI,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;AACnC,OAAI,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;AACtC,OAAI,OAAO,GAAG,IAAI,QAAQ,CAAC,UAAU,CAAC,CAAC;;AAEvC,OAAI,CAAC,QAAQ,GAAG,2BAAiB,MAAM,EAAE,OAAO,CAAC,CAAC;;AAElD,UAAO,IAAI,CAAC,QAAQ,CAAC;GACrB;;;;;;;;;;6BAQU;AACV,OAAK,CAAE,IAAI,CAAC,QAAQ,EAAE;AACrB,UAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;IACvD;;AAED,UAAO,IAAI,CAAC,QAAQ,CAAC;GACrB;;;QA3DI,SAAS;;;AA6Dd,CAAC;;AAEF,MAAM,CAAC,OAAO,GAAG,IAAI,SAAS,EAAE,CAAC","file":"NodeQuery.js","sourcesContent":["\"use strict\";\n\nlet instance = null;\nimport fs from 'fs';\nimport helpers from './helpers';\nimport QueryBuilder from './QueryBuilder';\n\n/**\n * @module NodeQuery\n */\nclass NodeQuery {\n\n\t/**\n\t * Constructor\n\t */\n\tconstructor() {\n\t\tthis.instance = null;\n\t}\n\n\t/**\n\t * Create a query builder object\n\t *\n\t * @memberOf NodeQuery\n\t * @param {String} drivername - The name of the database type, eg. mysql or pg\n\t * @param {Object} connObject - A connection object from the database library you are connecting with\n\t * @param {String} [connLib] - The name of the db connection library you are using, eg. mysql or mysql2. Optional if the same as drivername\n\t * @return {QueryBuilder}\n\t */\n\tinit(driverType, connObject, connLib) {\n\t\tconnLib = connLib || driverType;\n\n\t\tlet paths = {\n\t\t\tdriver: `${__dirname}/drivers/` + helpers.upperCaseFirst(driverType),\n\t\t\tadapter: `${__dirname}/adapters/${connLib}`\n\t\t};\n\n\t\t/*Object.keys(paths).forEach((type) => {\n\t\t\tif ( ! fs.existsSync(paths[type]))\n\t\t\t{\n\t\t\t\tconsole.log(paths[type]);\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Selected ${type} (` +\n\t\t\t\t\thelpers.upperCaseFirst(driverType) +\n\t\t\t\t\t`) does not exist!`\n\t\t\t\t);\n\t\t\t}\n\t\t});*/\n\n\t\tlet driver = require(paths.driver);\n\t\tlet $adapter = require(paths.adapter);\n\t\tlet adapter = new $adapter(connObject);\n\n\t\tthis.instance = new QueryBuilder(driver, adapter);\n\n\t\treturn this.instance;\n\t};\n\n\t/**\n\t * Return an existing query builder instance\n\t *\n\t * @memberOf NodeQuery\n\t * @return {QueryBuilder}\n\t */\n\tgetQuery() {\n\t\tif ( ! this.instance) {\n\t\t\tthrow new Error(\"No Query Builder instance to return\");\n\t\t}\n\n\t\treturn this.instance;\n\t};\n\n};\n\nmodule.exports = new NodeQuery();"],"sourceRoot":"/source/"} \ No newline at end of file diff --git a/lib/QueryBuilder.js b/lib/QueryBuilder.js index 3efc825..4c14a25 100755 --- a/lib/QueryBuilder.js +++ b/lib/QueryBuilder.js @@ -1,1063 +1,926 @@ -'use strict' +'use strict'; /** @module QueryBuilder */ -; +let getArgs = require('getargs'), + helpers = require('./helpers'), + State = require('./State'), + QueryParser = require('./QueryParser'); -var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); - -var _getargs = require('getargs'); - -var _getargs2 = _interopRequireDefault(_getargs); - -var _helpers = require('./helpers'); - -var _helpers2 = _interopRequireDefault(_helpers); - -var _State = require('./State'); - -var _State2 = _interopRequireDefault(_State); - -var _QueryParser = require('./QueryParser'); - -var _QueryParser2 = _interopRequireDefault(_QueryParser); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -module.exports = (function () { +module.exports = class QueryBuilder { /* - * SQL generation object - * - * @param {driver} - The syntax driver for the database - * @param {adapter} - The database module adapter for running queries - * @returns {QueryBuilder} - * @constructor - */ - - function QueryBuilder(driver, adapter) { - _classCallCheck(this, QueryBuilder); - + * SQL generation object + * + * @param {driver} - The syntax driver for the database + * @param {adapter} - The database module adapter for running queries + * @return {QueryBuilder} - The Query Builder object, for chaining + * @constructor + */ + constructor(driver, adapter) { this.driver = driver; this.adapter = adapter; - this.parser = new _QueryParser2.default(this.driver); - this.state = new _State2.default(); + this.parser = new QueryParser(this.driver); + this.state = new State(); } /** - * Complete the sql building based on the type provided - * - * @param {String} type - * @param {String} table - * @private - * @return {String} - */ + * 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); - _createClass(QueryBuilder, [{ - key: '_compile', - value: function _compile(type, table) { - var _this = this; + // Set each subClause + ['queryMap', 'groupString', 'orderString', 'havingMap'].forEach(clause => { + let param = this.state[clause]; - // Put together the basic query - var sql = this._compileType(type, table); - - // Set each subClause - ['queryMap', 'groupString', 'orderString', 'havingMap'].forEach(function (clause) { - var param = _this.state[clause]; - - if (!_helpers2.default.isScalar(param)) { - Object.keys(param).forEach(function (part) { - sql += param[part].conjunction + param[part].string; - }); - } else { - sql += param; - } - }); - - // Append the limit, if it exists - if (_helpers2.default.isNumber(this.state.limit)) { - sql = this.driver.limit(sql, this.state.limit, this.state.offset); - } - - return sql; - } - }, { - key: '_compileType', - value: function _compileType(type, table) { - var sql = ''; - - switch (type) { - case "insert": - var params = Array(this.state.setArrayKeys.length).fill('?'); - - sql = 'INSERT INTO ' + table + ' ('; - sql += this.state.setArrayKeys.join(','); - sql += ") VALUES ("; - sql += 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; - } - }, { - key: '_like', - value: function _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 - * - * @param {String} conjunction - * @param {String} string - * @param {String} type - * @return {void} - */ - - }, { - key: '_appendMap', - value: function _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 - */ - - }, { - key: '_mixedSet', - value: function _mixedSet() /* $letName, $valType, $key, [$val] */{ - var _this2 = this; - - var args = (0, _getargs2.default)('$letName:string, $valType:string, $key:object|string|number, [$val]', arguments); - - var obj = {}; - - if (_helpers2.default.isScalar(args.$key) && !_helpers2.default.isUndefined(args.$val)) { - // Convert key/val pair to a simple object - obj[args.$key] = args.$val; - } else if (_helpers2.default.isScalar(args.$key) && _helpers2.default.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(function (k) { - // If a single value for the return - if (['key', 'value'].indexOf(args.$valType) !== -1) { - var pushVal = args.$valType === 'key' ? k : obj[k]; - _this2.state[args.$letName].push(pushVal); - } else { - _this2.state[args.$letName][k] = obj[k]; - } - }); - - return this.state[args.$letName]; - } - }, { - key: '_whereMixedSet', - value: function _whereMixedSet() /*key, val*/{ - var args = (0, _getargs2.default)('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); - } - }, { - key: '_fixConjunction', - value: function _fixConjunction(conj) { - var lastItem = this.state.queryMap[this.state.queryMap.length - 1]; - var conjunctionList = _helpers2.default.arrayPluck(this.state.queryMap, 'conjunction'); - - if (this.state.queryMap.length === 0 || !_helpers2.default.regexInArray(conjunctionList, /^ ?WHERE/i)) { - conj = " WHERE "; - } else if (lastItem.type === 'groupStart') { - conj = ''; - } else { - conj = ' ' + conj + ' '; - } - - return conj; - } - }, { - key: '_where', - value: function _where(key, val, defaultConj) { - var _this3 = this; - - // 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(function (clause) { - var conj = _this3._fixConjunction(defaultConj); - _this3._appendMap(conj, clause, 'where'); - }); - - this.state.whereMap = {}; - } - }, { - key: '_whereNull', - value: function _whereNull(field, stmt, conj) { - field = this.driver.quoteIdentifiers(field); - var item = field + ' ' + stmt; - - this._appendMap(this._fixConjunction(conj), item, 'whereNull'); - } - }, { - key: '_having', - value: function _having() /*key, val, conj*/{ - var _this4 = this; - - var args = (0, _getargs2.default)('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(function (clause) { - // Put in the having map - _this4.state.havingMap.push({ - conjunction: _this4.state.havingMap.length > 0 ? ' ' + args.conj + ' ' : ' HAVING ', - string: clause + if ( ! helpers.isScalar(param)) + { + Object.keys(param).forEach(part => { + sql += param[part].conjunction + param[part].string; }); - }); - - // Clear the where Map - this.state.whereMap = {}; - } - }, { - key: '_whereIn', - value: function _whereIn() /*key, val, inClause, conj*/{ - var _this5 = this; - - var args = (0, _getargs2.default)('key:string, val:array, inClause:string, conj:string', arguments); - - args.key = this.driver.quoteIdentifiers(args.key); - var params = new Array(args.val.length); - params.fill('?'); - - args.val.forEach(function (value) { - _this5.state.whereValues.push(value); - }); - - args.conj = this.state.queryMap.length > 0 ? " " + args.conj + " " : ' WHERE '; - var str = args.key + " " + args.inClause + " (" + params.join(',') + ") "; - - this._appendMap(args.conj, str, 'whereIn'); - } - }, { - key: '_run', - value: function _run(type, table, callback, sql, vals) { - - if (!sql) { - sql = this._compile(type, table); } - - if (!vals) { - vals = this.state.values.concat(this.state.whereValues); + else + { + sql += param; } + }); - //console.log(this.state); - //console.log(sql); - //console.log(vals); - //console.log(callback); - //console.log('------------------------'); - - // Reset the state so another query can be built - this._resetState(); - - // Pass the sql and values to the adapter to run on the database - this.adapter.execute(sql, vals, callback); - } - }, { - key: '_getCompile', - value: function _getCompile(type, table, reset) { - reset = reset || false; - - var sql = this._compile(type, table); - - if (reset) this._resetState(); - - return sql; - } - }, { - key: '_resetState', - value: function _resetState() { - this.state = new _State2.default(); + // Append the limit, if it exists + if (helpers.isNumber(this.state.limit)) + { + sql = this.driver.limit(sql, this.state.limit, this.state.offset); } - // ---------------------------------------------------------------------------- - // ! Miscellaneous Methods - // ---------------------------------------------------------------------------- + return sql; + } - /** - * Reset the object state for a new query - * - * @memberOf QueryBuilder - * @return {void} - */ + _compileType(type, table) { + let sql = ''; - }, { - key: 'resetQuery', - value: function resetQuery() { - this._resetState(); - } + switch(type) { + case "insert": + let params = Array(this.state.setArrayKeys.length).fill('?'); - /** - * Returns the current class state for testing or other purposes - * - * @private - * @return {Object} - */ + sql = `INSERT INTO ${table} (`; + sql += this.state.setArrayKeys.join(','); + sql += ") VALUES ("; + sql += params.join(',') + ')'; + break; - }, { - key: 'getState', - value: function getState() { - return this.state; - } + case "update": + sql = `UPDATE ${table} SET ${this.state.setString}`; + break; - /** - * Closes the database connection for the current adapter - * - * @return {void} - */ + case "delete": + sql = `DELETE FROM ${table}`; + break; - }, { - key: 'end', - value: function end() { - this.adapter.close(); - } + default: + sql = `SELECT * FROM ${this.state.fromString}`; - // ------------------------------------------------------------------------ - // ! Query Builder Methods - // ------------------------------------------------------------------------ - - /** - * Specify rows to select in the query - * - * @memberOf QueryBuilder - * @param {String|Array} fields - The fields to select from the current table - * @return {QueryBuilder} - */ - - }, { - key: 'select', - value: function select(fields) { - - // Split/trim fields by comma - fields = Array.isArray(fields) ? fields : fields.split(",").map(_helpers2.default.stringTrim); - - // Split on 'As' - fields.forEach(function (field, index) { - if (field.match(/as/i)) { - fields[index] = field.split(/ as /i).map(_helpers2.default.stringTrim); + // 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 args = getArgs('$letName:string, $valType:string, $key:object|string|number, [$val]', arguments); + + let obj = {}; + + + if (helpers.isScalar(args.$key) && !helpers.isUndefined(args.$val)) + { + // Convert key/val pair to a simple object + obj[args.$key] = args.$val; + } + else if (helpers.isScalar(args.$key) && helpers.isUndefined(args.$val)) + { + // If just a string for the key, and no value, create a simple object with duplicate key/val + obj[args.$key] = args.$key; + } + else + { + obj = args.$key; + } + + Object.keys(obj).forEach(k => { + // If a single value for the return + if (['key','value'].indexOf(args.$valType) !== -1) + { + let pushVal = (args.$valType === 'key') ? k : obj[k]; + 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 }); + }); - var safeArray = this.driver.quoteIdentifiers(fields); + // Clear the where Map + this.state.whereMap = {}; + } + _whereIn(/*key, val, inClause, conj*/) { + let args = getArgs('key:string, val:array, inClause:string, conj:string', arguments); - // Join the strings back together - safeArray.forEach(function (field, index) { - if (Array.isArray(field)) { - safeArray[index] = safeArray[index].join(' AS '); - } - }); + args.key = this.driver.quoteIdentifiers(args.key); + let params = new Array(args.val.length); + params.fill('?'); - this.state.selectString += safeArray.join(', '); + args.val.forEach(value => { + this.state.whereValues.push(value); + }); - return this; + 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); } - /** - * Specify the database table to select from - * - * @param {String} tableName - The table to use for the current query - * @return {QueryBuilder} - */ - - }, { - key: 'from', - value: function from(tableName) { - // Split identifiers on spaces - var identArray = tableName.trim().split(' ').map(_helpers2.default.stringTrim); - - // Quote/prefix identifiers - identArray[0] = this.driver.quoteTable(identArray[0]); - identArray = this.driver.quoteIdentifiers(identArray); - - // Put it back together - this.state.fromString = identArray.join(' '); - - return this; + if ( ! vals) + { + vals = this.state.values.concat(this.state.whereValues); } - /** - * Add a 'like/ and like' clause to the query - * - * @param {String} field - The name of the field to compare to - * @param {String} val - The value to compare to - * @param {String} [pos=both] - The placement of the wildcard character(s): before, after, or both - * @return {QueryBuilder} - */ +//console.log(this.state); +//console.log(sql); +//console.log(vals); +//console.log(callback); +//console.log('------------------------'); - }, { - key: 'like', - value: function like(field, val, pos) { - this._like(field, val, pos, ' LIKE ', 'AND'); - return this; - } + // Reset the state so another query can be built + this._resetState(); - /** - * Add a 'not like/ and not like' clause to the query - * - * @param {String} field - The name of the field to compare to - * @param {String} val - The value to compare to - * @param {String} [pos=both] - The placement of the wildcard character(s): before, after, or both - * @return {QueryBuilder} - */ + // Pass the sql and values to the adapter to run on the database + this.adapter.execute(sql, vals, callback); - }, { - key: 'notLike', - value: function notLike(field, val, pos) { - this._like(field, val, pos, ' NOT LIKE ', 'AND'); - return this; - } + } - /** - * Add an 'or like' clause to the query - * - * @param {String} field - The name of the field to compare to - * @param {String} val - The value to compare to - * @param {String} [pos=both] - The placement of the wildcard character(s): before, after, or both - * @return {QueryBuilder} - */ + _getCompile(type, table, reset) { + reset = reset || false; - }, { - key: 'orLike', - value: function orLike(field, val, pos) { - this._like(field, val, pos, ' LIKE ', 'OR'); - return this; - } + let sql = this._compile(type, table); - /** - * Add an 'or not like' clause to the query - * - * @param {String} field - The name of the field to compare to - * @param {String} val - The value to compare to - * @param {String} [pos=both] - The placement of the wildcard character(s): before, after, or both - * @return {QueryBuilder} - */ + if (reset) this._resetState(); - }, { - key: 'orNotLike', - value: function orNotLike(field, val, pos) { - this._like(field, val, pos, ' NOT LIKE ', 'OR'); - return this; - } + return sql; + } - /** - * Add a 'having' clause - * - * @param {String|Object} key - The name of the field and the comparision operator, or an object - * @param {String|Number} [val] - The value to compare if the value of key is a string - * @return {QueryBuilder} - */ + _resetState() { + this.state = new State(); + } - }, { - key: 'having', - value: function having() /*key, [val]*/{ - var args = (0, _getargs2.default)('key:string|object, [val]:string|number', arguments); + // ---------------------------------------------------------------------------- + // ! Miscellaneous Methods + // ---------------------------------------------------------------------------- - this._having(args.key, args.val, 'AND'); - return this; - } + /** + * Reset the object state for a new query + * + * @memberOf QueryBuilder + * @return {void} + */ + resetQuery() { + this._resetState(); + } - /** - * Add an 'or having' clause - * - * @param {String|Object} key - The name of the field and the comparision operator, or an object - * @param {String|Number} [val] - The value to compare if the value of key is a string - * @return {QueryBuilder} - */ + /** + * Returns the current class state for testing or other purposes + * + * @private + * @return {Object} - The State object + */ + getState() { + return this.state; + } - }, { - key: 'orHaving', - value: function orHaving() /*key, [val]*/{ - var args = (0, _getargs2.default)('key:string|object, [val]:string|number', arguments); + /** + * Closes the database connection for the current adapter + * + * @return {void} + */ + end() { + this.adapter.close(); + } - this._having(args.key, args.val, 'OR'); - return this; - } + // ------------------------------------------------------------------------ + // ! Query Builder Methods + // ------------------------------------------------------------------------ - /** - * Set a 'where' clause - * - * @param {String|Object} key - The name of the field and the comparision operator, or an object - * @param {String|Number} [val] - The value to compare if the value of key is a string - * @return {QueryBuilder} - */ + /** + * Specify rows to select in the query + * + * @memberOf QueryBuilder + * @param {String|Array} fields - The fields to select from the current table + * @return {QueryBuilder} - The Query Builder object, for chaining + */ + select(fields) { - }, { - key: 'where', - value: function where(key, val) { - this._where(key, val, 'AND'); - return this; - } + // Split/trim fields by comma + fields = (Array.isArray(fields)) + ? fields + : fields.split(",").map(helpers.stringTrim); - /** - * Set a 'or where' clause - * - * @param {String|Object} key - The name of the field and the comparision operator, or an object - * @param {String|Number} [val] - The value to compare if the value of key is a string - * @return {QueryBuilder} - */ - - }, { - key: 'orWhere', - value: function orWhere(key, val) { - this._where(key, val, 'OR'); - return this; - } - - /** - * Select a field that is Null - * - * @param {String} field - The name of the field that has a NULL value - * @return {QueryBuilder} - */ - - }, { - key: 'whereIsNull', - value: function whereIsNull(field) { - this._whereNull(field, 'IS NULL', 'AND'); - return this; - } - - /** - * Specify that a field IS NOT NULL - * - * @param {String} field - * @return {QueryBuilder} - */ - - }, { - key: 'whereIsNotNull', - value: function whereIsNotNull(field) { - this._whereNull(field, 'IS NOT NULL', 'AND'); - return this; - } - - /** - * Field is null prefixed with 'OR' - * - * @param {String} field - * @return {QueryBuilder} - */ - - }, { - key: 'orWhereIsNull', - value: function orWhereIsNull(field) { - this._whereNull(field, 'IS NULL', 'OR'); - return this; - } - - /** - * Field is not null prefixed with 'OR' - * - * @param {String} field - * @return {QueryBuilder} - */ - - }, { - key: 'orWhereIsNotNull', - value: function orWhereIsNotNull(field) { - this._whereNull(field, 'IS NOT NULL', 'OR'); - return this; - } - - /** - * Set a 'where in' clause - * - * @param {String} key - the field to search - * @param {Array} val - the array of items to search in - * @return {QueryBuilder} - */ - - }, { - key: 'whereIn', - value: function whereIn(key, val) { - this._whereIn(key, val, 'IN', 'AND'); - return this; - } - - /** - * Set a 'or where in' clause - * - * @param {String} key - the field to search - * @param {Array} val - the array of items to search in - * @return {QueryBuilder} - */ - - }, { - key: 'orWhereIn', - value: function orWhereIn(key, val) { - this._whereIn(key, val, 'IN', 'OR'); - return this; - } - - /** - * Set a 'where not in' clause - * - * @param {String} key - the field to search - * @param {Array} val - the array of items to search in - * @return {QueryBuilder} - */ - - }, { - key: 'whereNotIn', - value: function whereNotIn(key, val) { - this._whereIn(key, val, 'NOT IN', 'AND'); - return this; - } - - /** - * Set a 'or where not in' clause - * - * @param {String} key - the field to search - * @param {Array} val - the array of items to search in - * @return {QueryBuilder} - */ - - }, { - key: 'orWhereNotIn', - value: function orWhereNotIn(key, val) { - this._whereIn(key, val, 'NOT IN', 'OR'); - return this; - } - - /** - * Set values for insertion or updating - * - * @param {String|Object} key - The key or object to use - * @param {String} [val] - The value if using a scalar key - * @return {QueryBuilder} - */ - - }, { - key: 'set', - value: function set() /* $key, [$val] */{ - var args = (0, _getargs2.default)('$key, [$val]', arguments); - - // Set the appropriate state variables - this._mixedSet('setArrayKeys', 'key', args.$key, args.$val); - this._mixedSet('values', 'value', args.$key, args.$val); - - // Use the keys of the array to make the insert/update string - // and escape the field names - this.state.setArrayKeys = this.state.setArrayKeys.map(this.driver._quote); - - // Generate the "set" string - this.state.setString = this.state.setArrayKeys.join('=?,'); - this.state.setString += '=?'; - - return this; - } - - /** - * Add a join clause to the query - * - * @param {String} table - The table you are joining - * @param {String} cond - The join condition. - * @param {String} [type='inner'] - The type of join, which defaults to inner - * @return {QueryBuilder} - */ - - }, { - key: 'join', - value: function join(table, cond, type) { - type = type || "inner"; - - // Prefix/quote table name - table = table.split(' ').map(_helpers2.default.stringTrim); - table[0] = this.driver.quoteTable(table[0]); - table = table.map(this.driver.quoteIdentifiers); - table = table.join(' '); - - // Parse out the join condition - var parsedCondition = this.parser.compileJoin(cond); - var condition = table + ' ON ' + parsedCondition; - - // Append the join condition to the query map - this._appendMap("\n" + type.toUpperCase() + ' JOIN ', condition, 'join'); - - return this; - } - - /** - * Group the results by the selected field(s) - * - * @param {String|Array} field - * @return {QueryBuilder} - */ - - }, { - key: 'groupBy', - value: function groupBy(field) { - if (!_helpers2.default.isScalar(field)) { - var newGroupArray = field.map(this.driver.quoteIdentifiers); - this.state.groupArray = this.state.groupArray.concat(newGroupArray); - } else { - this.state.groupArray.push(this.driver.quoteIdentifiers(field)); + // Split on 'As' + fields.forEach((field, index) => { + if (field.match(/as/i)) + { + fields[index] = field.split(/ as /i).map(helpers.stringTrim); } + }); - this.state.groupString = ' GROUP BY ' + this.state.groupArray.join(','); + let safeArray = this.driver.quoteIdentifiers(fields); - return this; - } - - /** - * Order the results by the selected field(s) - * - * @param {String} field - The field(s) to order by - * @param {String} [type='ASC'] - The order direction, ASC or DESC - * @return {QueryBuilder} - */ - - }, { - key: 'orderBy', - value: function orderBy(field, type) { - var _this6 = this; - - type = type || 'ASC'; - - // Set the fields for later manipulation - field = this.driver.quoteIdentifiers(field); - - this.state.orderArray[field] = type; - - var orderClauses = []; - - // Flatten key/val pairs into an array of space-separated pairs - Object.keys(this.state.orderArray).forEach(function (key) { - orderClauses.push(key + ' ' + _this6.state.orderArray[key].toUpperCase()); - }); - - // Set the final string - this.state.orderString = ' ORDER BY ' + orderClauses.join(', '); - - return this; - } - - /** - * Put a limit on the query - * - * @param {Number} limit - The maximum number of rows to fetch - * @param {Number} [offset] - The row number to start from - * @return {QueryBuilder} - */ - - }, { - key: 'limit', - value: function limit(_limit, offset) { - this.state.limit = _limit; - this.state.offset = offset || null; - - return this; - } - - /** - * Adds an open paren to the current query for logical grouping - * - * @return {QueryBuilder} - */ - - }, { - key: 'groupStart', - value: function groupStart() { - var conj = this.state.queryMap.length < 1 ? ' WHERE ' : ' AND '; - this._appendMap(conj, '(', 'groupStart'); - - return this; - } - - /** - * Adds an open paren to the current query for logical grouping, - * prefixed with 'OR' - * - * @return {QueryBuilder} - */ - - }, { - key: 'orGroupStart', - value: function orGroupStart() { - this._appendMap('', ' OR (', 'groupStart'); - - return this; - } - - /** - * Adds an open paren to the current query for logical grouping, - * prefixed with 'OR NOT' - * - * @return {QueryBuilder} - */ - - }, { - key: 'orNotGroupStart', - value: function orNotGroupStart() { - this._appendMap('', ' OR NOT (', 'groupStart'); - - return this; - } - - /** - * Ends a logical grouping started with one of the groupStart methods - * - * @return {QueryBuilder} - */ - - }, { - key: 'groupEnd', - value: function groupEnd() { - this._appendMap('', ')', 'groupEnd'); - - return this; - } - - // ------------------------------------------------------------------------ - // ! Result Methods - // ------------------------------------------------------------------------ - - /** - * Get the results of the compiled query - * - * @param {String} [table] - The table to select from - * @param {Number} [limit] - A limit for the query - * @param {Number} [offset] - An offset for the query - * @param {Function} callback - A callback for receiving the result - * @return {void} - */ - - }, { - key: 'get', - value: function get() /* [table], [limit], [offset], callback */{ - var args = (0, _getargs2.default)('[table]:string, [limit]:number, [offset]:number, callback:function', arguments); - - if (args.table) { - this.from(args.table); + // Join the strings back together + safeArray.forEach((field, index) => { + if (Array.isArray(field)) + { + safeArray[index] = safeArray[index].join(' AS '); } + }); - if (args.limit) { - this.limit(args.limit, args.offset); - } + this.state.selectString += safeArray.join(', '); - // Run the query - this._run('get', args.table, args.callback); + return this; + } + + /** + * Specify the database table to select from + * + * @param {String} tableName - The table to use for the current query + * @return {QueryBuilder} - The Query Builder object, for chaining + */ + from(tableName) { + // Split identifiers on spaces + let identArray = tableName.trim().split(' ').map(helpers.stringTrim); + + // Quote/prefix identifiers + identArray[0] = this.driver.quoteTable(identArray[0]); + identArray = this.driver.quoteIdentifiers(identArray); + + // Put it back together + this.state.fromString = identArray.join(' '); + + return this; + } + + /** + * Add a 'like/ and like' clause to the query + * + * @param {String} field - The name of the field to compare to + * @param {String} val - The value to compare to + * @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) { + this._like(field, val, pos, ' LIKE ', 'AND'); + return this; + } + + /** + * Add a 'not like/ and not like' clause to the query + * + * @param {String} field - The name of the field to compare to + * @param {String} val - The value to compare to + * @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) { + this._like(field, val, pos, ' NOT LIKE ', 'AND'); + return this; + } + + /** + * Add an 'or like' clause to the query + * + * @param {String} field - The name of the field to compare to + * @param {String} val - The value to compare to + * @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) { + this._like(field, val, pos, ' LIKE ', 'OR'); + return this; + } + + /** + * Add an 'or not like' clause to the query + * + * @param {String} field - The name of the field to compare to + * @param {String} val - The value to compare to + * @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) { + this._like(field, val, pos, ' NOT LIKE ', 'OR'); + return this; + } + + /** + * Add a 'having' clause + * + * @param {String|Object} key - The name of the field and the comparision operator, or an object + * @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'); + return this; + } + + /** + * Add an 'or having' clause + * + * @param {String|Object} key - The name of the field and the comparision operator, or an object + * @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'); + return this; + } + + /** + * Set a 'where' clause + * + * @param {String|Object} key - The name of the field and the comparision operator, or an object + * @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) { + this._where(key, val, 'AND'); + return this; + } + + /** + * Set a 'or where' clause + * + * @param {String|Object} key - The name of the field and the comparision operator, or an object + * @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) { + this._where(key, val, 'OR'); + return this; + } + + /** + * Select a field that is Null + * + * @param {String} field - The name of the field that has a NULL value + * @return {QueryBuilder} - The Query Builder object, for chaining + */ + whereIsNull(field) { + this._whereNull(field, 'IS NULL', 'AND'); + return this; + } + + /** + * Specify that a field IS NOT NULL + * + * @param {String} field - The name so the field that is not to be null + * @return {QueryBuilder} - The Query Builder object, for chaining + */ + whereIsNotNull(field) { + this._whereNull(field, 'IS NOT NULL', 'AND'); + return this; + } + + /** + * Field is null prefixed with 'OR' + * + * @param {String} field - The name of the field + * @return {QueryBuilder} - The Query Builder object, for chaining + */ + orWhereIsNull(field) { + this._whereNull(field, 'IS NULL', 'OR'); + return this; + } + + /** + * Field is not null prefixed with 'OR' + * + * @param {String} field - The name of the field + * @return {QueryBuilder} - The Query Builder object, for chaining + */ + orWhereIsNotNull(field) { + this._whereNull(field, 'IS NOT NULL', 'OR'); + return this; + } + + /** + * Set a 'where in' clause + * + * @param {String} key - the field to search + * @param {Array} val - the array of items to search in + * @return {QueryBuilder} - The Query Builder object, for chaining + */ + whereIn(key, val) { + this._whereIn(key, val, 'IN', 'AND'); + return this; + } + + /** + * Set a 'or where in' clause + * + * @param {String} key - the field to search + * @param {Array} val - the array of items to search in + * @return {QueryBuilder} - The Query Builder object, for chaining + */ + orWhereIn(key, val) { + this._whereIn(key, val, 'IN', 'OR'); + return this; + } + + /** + * Set a 'where not in' clause + * + * @param {String} key - the field to search + * @param {Array} val - the array of items to search in + * @return {QueryBuilder} - The Query Builder object, for chaining + */ + whereNotIn(key, val) { + this._whereIn(key, val, 'NOT IN', 'AND'); + return this; + } + + /** + * Set a 'or where not in' clause + * + * @param {String} key - the field to search + * @param {Array} val - the array of items to search in + * @return {QueryBuilder} - The Query Builder object, for chaining + */ + orWhereNotIn(key, val) { + this._whereIn(key, val, 'NOT IN', 'OR'); + return this; + } + + /** + * Set values for insertion or updating + * + * @param {String|Object} key - The key or object to use + * @param {String} [val] - The value if using a scalar key + * @return {QueryBuilder} - The Query Builder object, for chaining + */ + set(/* $key, [$val] */) { + let args = getArgs('$key, [$val]', arguments); + + // Set the appropriate state variables + this._mixedSet('setArrayKeys', 'key', args.$key, args.$val); + this._mixedSet('values', 'value', args.$key, args.$val); + + // Use the keys of the array to make the insert/update string + // and escape the field names + this.state.setArrayKeys = this.state.setArrayKeys.map(this.driver._quote); + + // Generate the "set" string + this.state.setString = this.state.setArrayKeys.join('=?,'); + this.state.setString += '=?'; + + return this; + } + + /** + * Add a join clause to the query + * + * @param {String} table - The table you are joining + * @param {String} cond - The join condition. + * @param {String} [type='inner'] - The type of join, which defaults to inner + * @return {QueryBuilder} - The Query Builder object, for chaining + */ + join(table, cond, type) { + type = type || "inner"; + + // Prefix/quote table name + table = table.split(' ').map(helpers.stringTrim); + table[0] = this.driver.quoteTable(table[0]); + table = table.map(this.driver.quoteIdentifiers); + table = table.join(' '); + + // Parse out the join condition + let parsedCondition = this.parser.compileJoin(cond); + let condition = table + ' ON ' + parsedCondition; + + // Append the join condition to the query map + this._appendMap("\n" + type.toUpperCase() + ' JOIN ', condition, 'join'); + + return this; + } + + /** + * Group the results by the selected field(s) + * + * @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)) + { + let newGroupArray = field.map(this.driver.quoteIdentifiers); + this.state.groupArray = this.state.groupArray.concat(newGroupArray); + } + else + { + this.state.groupArray.push(this.driver.quoteIdentifiers(field)); } - /** - * Run the generated insert query - * - * @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} - */ + this.state.groupString = ' GROUP BY ' + this.state.groupArray.join(','); - }, { - key: 'insert', - value: function insert() /* table, data, callback */{ - var args = (0, _getargs2.default)('table:string, [data]:object, callback:function', arguments); + return this; + } - if (args.data) { - this.set(args.data); - } + /** + * Order the results by the selected field(s) + * + * @param {String} field - The field(s) to order by + * @param {String} [type='ASC'] - The order direction, ASC or DESC + * @return {QueryBuilder} - The Query Builder object, for chaining + */ + orderBy(field, type) { + type = type || 'ASC'; - // Run the query - this._run('insert', this.driver.quoteTable(args.table), args.callback); + // Set the fields for later manipulation + field = this.driver.quoteIdentifiers(field); + + this.state.orderArray[field] = type; + + let orderClauses = []; + + // Flatten key/val pairs into an array of space-separated pairs + Object.keys(this.state.orderArray).forEach(key => { + orderClauses.push(key + ' ' + this.state.orderArray[key].toUpperCase()); + }); + + // Set the final string + this.state.orderString = ' ORDER BY ' + orderClauses.join(', '); + + return this; + } + + /** + * Put a limit on the query + * + * @param {Number} limit - The maximum number of rows to fetch + * @param {Number} [offset] - The row number to start from + * @return {QueryBuilder} - The Query Builder object, for chaining + */ + limit(limit, offset) { + this.state.limit = limit; + this.state.offset = offset || null; + + return this; + } + + /** + * Adds an open paren to the current query for logical grouping + * + * @return {QueryBuilder} - The Query Builder object, for chaining + */ + groupStart() { + let conj = (this.state.queryMap.length < 1) ? ' WHERE ' : ' AND '; + this._appendMap(conj, '(', 'groupStart'); + + return this; + } + + /** + * Adds an open paren to the current query for logical grouping, + * prefixed with 'OR' + * + * @return {QueryBuilder} - The Query Builder object, for chaining + */ + orGroupStart() { + this._appendMap('', ' OR (', 'groupStart'); + + return this; + } + + /** + * Adds an open paren to the current query for logical grouping, + * prefixed with 'OR NOT' + * + * @return {QueryBuilder} - The Query Builder object, for chaining + */ + orNotGroupStart() { + this._appendMap('', ' OR NOT (', 'groupStart'); + + return this; + } + + /** + * Ends a logical grouping started with one of the groupStart methods + * + * @return {QueryBuilder} - The Query Builder object, for chaining + */ + groupEnd() { + this._appendMap('', ')', 'groupEnd'); + + return this; + } + + // ------------------------------------------------------------------------ + // ! Result Methods + // ------------------------------------------------------------------------ + + /** + * Get the results of the compiled query + * + * @param {String} [table] - The table to select from + * @param {Number} [limit] - A limit for the query + * @param {Number} [offset] - An offset for the query + * @param {Function} callback - A callback for receiving the result + * @return {void} + */ + get(/* [table], [limit], [offset], callback */) { + let args = getArgs('[table]:string, [limit]:number, [offset]:number, callback:function', arguments); + + if (args.table) { + this.from(args.table); } - /** - * Insert multiple sets of rows at a time - * - * @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); - * @return {void} - */ - - }, { - key: 'insertBatch', - value: function insertBatch() /* table, data, callback */{ - var args = (0, _getargs2.default)('table:string, data:array, callback:function', arguments); - var batch = this.driver.insertBatch(args.table, args.data); - - // Run the query - this._run('', '', args.callback, batch.sql, batch.values); + if (args.limit) { + this.limit(args.limit, args.offset); } - /** - * Run the generated update query - * - * @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} - */ + // Run the query + this._run('get', args.table, args.callback); + } - }, { - key: 'update', - value: function update() /*table, data, callback*/{ - var args = (0, _getargs2.default)('table:string, [data]:object, callback:function', arguments); + /** + * Run the generated insert query + * + * @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} + */ + insert(/* table, data, callback */) { + let args = getArgs('table:string, [data]:object, callback:function', arguments); - if (args.data) { - this.set(args.data); - } - - // Run the query - this._run('update', this.driver.quoteTable(args.table), args.callback); + if (args.data) { + this.set(args.data); } - /** - * Run the generated delete query - * - * @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} - */ + // Run the query + this._run('insert', this.driver.quoteTable(args.table), args.callback); + } - }, { - key: 'delete', - value: function _delete() /*table, [where], callback*/{ - var args = (0, _getargs2.default)('table:string, [where]:object, callback:function', arguments); + /** + * Insert multiple sets of rows at a time + * + * @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); + * @return {void} + */ + insertBatch(/* table, data, callback */) { + let args = getArgs('table:string, data:array, callback:function', arguments); + let batch = this.driver.insertBatch(args.table, args.data); - if (args.where) { - this.where(args.where); - } + // Run the query + this._run('', '', args.callback, batch.sql, batch.values); + } - // Run the query - this._run('delete', this.driver.quoteTable(args.table), args.callback); + /** + * Run the generated update query + * + * @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} + */ + update(/*table, data, callback*/) { + let args = getArgs('table:string, [data]:object, callback:function', arguments); + + if (args.data) { + this.set(args.data); } - // ------------------------------------------------------------------------ - // ! Methods returning SQL - // ------------------------------------------------------------------------ + // Run the query + this._run('update', this.driver.quoteTable(args.table), args.callback); + } - /** - * Return generated select query SQL - * - * @param {String} [table] - the name of the table to retrieve from - * @param {Boolean} [reset=true] - Whether to reset the query builder so another query can be built - * @return {String} - */ + /** + * Run the generated delete query + * + * @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} + */ + delete(/*table, [where], callback*/) { + let args = getArgs('table:string, [where]:object, callback:function', arguments); - }, { - key: 'getCompiledSelect', - value: function getCompiledSelect() /*table, reset*/{ - var args = (0, _getargs2.default)('[table]:string, [reset]:boolean', arguments); - if (args.table) { - this.from(args.table); - } - - return this._getCompile('get', args.table, args.reset); + if (args.where) + { + this.where(args.where); } - /** - * Return generated insert query SQL - * - * @param {String} table - the name of the table to insert into - * @param {Boolean} [reset=true] - Whether to reset the query builder so another query can be built - * @return {String} - */ + // Run the query + this._run('delete', this.driver.quoteTable(args.table), args.callback); + } - }, { - key: 'getCompiledInsert', - value: function getCompiledInsert(table, reset) { - return this._getCompile('insert', this.driver.quoteTable(table), reset); + // ------------------------------------------------------------------------ + // ! Methods returning SQL + // ------------------------------------------------------------------------ + + /** + * Return generated select query SQL + * + * @param {String} [table] - the name of the table to retrieve from + * @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); } - /** - * Return generated update query SQL - * - * @param {String} table - the name of the table to update - * @param {Boolean} [reset=true] - Whether to reset the query builder so another query can be built - * @return {String} - */ + return this._getCompile('get', args.table, args.reset); + } - }, { - key: 'getCompiledUpdate', - value: function getCompiledUpdate(table, reset) { - return this._getCompile('update', this.driver.quoteTable(table), reset); - } + /** + * Return generated insert query SQL + * + * @param {String} table - the name of the table to insert into + * @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) { + return this._getCompile('insert', this.driver.quoteTable(table), reset); + } - /** - * Return generated delete query SQL - * - * @param {String} table - the name of the table to delete from - * @param {Boolean} [reset=true] - Whether to reset the query builder so another query can be built - * @return {String} - */ + /** + * Return generated update query SQL + * + * @param {String} table - the name of the table to update + * @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) { + return this._getCompile('update', this.driver.quoteTable(table), reset); + } - }, { - key: 'getCompiledDelete', - value: function getCompiledDelete(table, reset) { - return this._getCompile('delete', this.driver.quoteTable(table), reset); - } - }]); - - return QueryBuilder; -})(); -//# sourceMappingURL=QueryBuilder.js.map + /** + * Return generated delete query SQL + * + * @param {String} table - the name of the table to delete from + * @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) { + return this._getCompile('delete', this.driver.quoteTable(table), reset); + } +} \ No newline at end of file diff --git a/lib/QueryBuilder.js.map b/lib/QueryBuilder.js.map deleted file mode 100644 index b9903ee..0000000 --- a/lib/QueryBuilder.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["QueryBuilder.js"],"names":[],"mappings":"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","file":"QueryBuilder.js","sourcesContent":["'use strict';\n\n/** @module QueryBuilder */\nimport getArgs from 'getargs';\nimport helpers from './helpers';\nimport State from './State';\nimport QueryParser from './QueryParser';\n\nmodule.exports = class QueryBuilder {\n\t/*\n\t * SQL generation object\n\t *\n\t * @param {driver} - The syntax driver for the database\n\t * @param {adapter} - The database module adapter for running queries\n\t * @returns {QueryBuilder}\n\t * @constructor\n\t */\n\tconstructor(driver, adapter) {\n\t\tthis.driver = driver;\n\t\tthis.adapter = adapter;\n\t\tthis.parser = new QueryParser(this.driver);\n\t\tthis.state = new State();\n\t}\n\n\t/**\n\t * Complete the sql building based on the type provided\n\t *\n\t * @param {String} type\n\t * @param {String} table\n\t * @private\n\t * @return {String}\n\t */\n\t_compile(type, table) {\n\t\t// Put together the basic query\n\t\tlet sql = this._compileType(type, table);\n\n\t\t// Set each subClause\n\t\t['queryMap', 'groupString', 'orderString', 'havingMap'].forEach((clause) => {\n\t\t\tlet param = this.state[clause];\n\n\t\t\tif ( ! helpers.isScalar(param))\n\t\t\t{\n\t\t\t\tObject.keys(param).forEach((part) => {\n\t\t\t\t\tsql += param[part].conjunction + param[part].string;\n\t\t\t\t});\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tsql += param;\n\t\t\t}\n\t\t});\n\n\t\t// Append the limit, if it exists\n\t\tif (helpers.isNumber(this.state.limit))\n\t\t{\n\t\t\tsql = this.driver.limit(sql, this.state.limit, this.state.offset);\n\t\t}\n\n\t\treturn sql;\n\t}\n\n\t_compileType(type, table) {\n\t\tlet sql = '';\n\n\t\tswitch(type) {\n\t\t\tcase \"insert\":\n\t\t\t\tlet params = Array(this.state.setArrayKeys.length).fill('?');\n\n\t\t\t\tsql = `INSERT INTO ${table} (`;\n\t\t\t\tsql += this.state.setArrayKeys.join(',');\n\t\t\t\tsql += \") VALUES (\";\n\t\t\t\tsql += params.join(',') + ')';\n\t\t\tbreak;\n\n\t\t\tcase \"update\":\n\t\t\t\tsql = `UPDATE ${table} SET ${this.state.setString}`;\n\t\t\tbreak;\n\n\t\t\tcase \"delete\":\n\t\t\t\tsql = `DELETE FROM ${table}`;\n\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tsql = `SELECT * FROM ${this.state.fromString}`;\n\n\t\t\t\t// Set the select string\n\t\t\t\tif (this.state.selectString.length > 0)\n\t\t\t\t{\n\t\t\t\t\t// Replace the star with the selected fields\n\t\t\t\t\tsql = sql.replace('*', this.state.selectString);\n\t\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\treturn sql;\n\t}\n\n\t_like(field, val, pos, like, conj) {\n\t\tfield = this.driver.quoteIdentifiers(field);\n\n\t\tlike = `${field} ${like} ?`;\n\n\t\tif (pos == 'before')\n\t\t{\n\t\t\tval = `%${val}`;\n\t\t}\n\t\telse if (pos == 'after')\n\t\t{\n\t\t\tval = `${val}%`;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tval = `%${val}%`;\n\t\t}\n\n\t\tconj = (this.state.queryMap.length < 1) ? ' WHERE ' : ` ${conj} `;\n\t\tthis._appendMap(conj, like, 'like');\n\n\t\tthis.state.whereValues.push(val);\n\t}\n\n\t/**\n\t * Append a clause to the query map\n\t *\n\t * @param {String} conjunction\n\t * @param {String} string\n\t * @param {String} type\n\t * @return {void}\n\t */\n\t_appendMap(conjunction, string, type) {\n\t\tthis.state.queryMap.push({\n\t\t\ttype: type,\n\t\t\tconjunction: conjunction,\n\t\t\tstring: string\n\t\t});\n\t}\n\n\t/**\n\t * Handle key/value pairs in an object the same way as individual arguments,\n\t * when appending to state\n\t *\n\t * @private\n\t */\n\t_mixedSet(/* $letName, $valType, $key, [$val] */) {\n\t\tlet args = getArgs('$letName:string, $valType:string, $key:object|string|number, [$val]', arguments);\n\n\t\tlet obj = {};\n\n\n\t\tif (helpers.isScalar(args.$key) && !helpers.isUndefined(args.$val))\n\t\t{\n\t\t\t// Convert key/val pair to a simple object\n\t\t\tobj[args.$key] = args.$val;\n\t\t}\n\t\telse if (helpers.isScalar(args.$key) && helpers.isUndefined(args.$val))\n\t\t{\n\t\t\t// If just a string for the key, and no value, create a simple object with duplicate key/val\n\t\t\tobj[args.$key] = args.$key;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tobj = args.$key;\n\t\t}\n\n\t\tObject.keys(obj).forEach((k) => {\n\t\t\t// If a single value for the return\n\t\t\tif (['key','value'].indexOf(args.$valType) !== -1)\n\t\t\t{\n\t\t\t\tlet pushVal = (args.$valType === 'key') ? k : obj[k];\n\t\t\t\tthis.state[args.$letName].push(pushVal);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tthis.state[args.$letName][k] = obj[k];\n\t\t\t}\n\t\t});\n\n\n\t\treturn this.state[args.$letName];\n\t}\n\n\t_whereMixedSet(/*key, val*/) {\n\t\tlet args = getArgs('key:string|object, [val]', arguments);\n\n\t\tthis.state.whereMap = [];\n\t\tthis.state.rawWhereValues = [];\n\n\t\tthis._mixedSet('whereMap', 'both', args.key, args.val);\n\t\tthis._mixedSet('rawWhereValues', 'value', args.key, args.val);\n\t}\n\n\t_fixConjunction(conj) {\n\t\tlet lastItem = this.state.queryMap[this.state.queryMap.length - 1];\n\t\tlet conjunctionList = helpers.arrayPluck(this.state.queryMap, 'conjunction');\n\n\t\tif (this.state.queryMap.length === 0 || ( ! helpers.regexInArray(conjunctionList, /^ ?WHERE/i)))\n\t\t{\n\t\t\tconj = \" WHERE \";\n\t\t}\n\t\telse if (lastItem.type === 'groupStart')\n\t\t{\n\t\t\tconj = '';\n\t\t}\n\t\telse\n\t\t{\n\t\t\tconj = ' ' + conj + ' ';\n\t\t}\n\n\t\treturn conj;\n\t}\n\n\t_where(key, val, defaultConj) {\n\t\t// Normalize key and value and insert into this.state.whereMap\n\t\tthis._whereMixedSet(key, val);\n\n\t\t// Parse the where condition to account for operators,\n\t\t// functions, identifiers, and literal values\n\t\tthis.state = this.parser.parseWhere(this.driver, this.state);\n\n\t\tthis.state.whereMap.forEach((clause) => {\n\t\t\tlet conj = this._fixConjunction(defaultConj);\n\t\t\tthis._appendMap(conj, clause, 'where');\n\t\t});\n\n\t\tthis.state.whereMap = {};\n\t}\n\n\t_whereNull(field, stmt, conj) {\n\t\tfield = this.driver.quoteIdentifiers(field);\n\t\tlet item = field + ' ' + stmt;\n\n\t\tthis._appendMap(this._fixConjunction(conj), item, 'whereNull');\n\t}\n\n\t_having(/*key, val, conj*/) {\n\t\tlet args = getArgs('key:string|object, [val]:string|number, [conj]:string', arguments);\n\t\targs.conj = args.conj || 'AND';\n\t\targs.val = args.val || null;\n\n\t\t// Normalize key/val and put in state.whereMap\n\t\tthis._whereMixedSet(args.key, args.val);\n\n\t\t// Parse the having condition to account for operators,\n\t\t// functions, identifiers, and literal values\n\t\tthis.state = this.parser.parseWhere(this.driver, this.state);\n\n\t\tthis.state.whereMap.forEach((clause) => {\n\t\t\t// Put in the having map\n\t\t\tthis.state.havingMap.push({\n\t\t\t\tconjunction: (this.state.havingMap.length > 0) ? ` ${args.conj} ` : ' HAVING ',\n\t\t\t\tstring: clause\n\t\t\t});\n\t\t});\n\n\t\t// Clear the where Map\n\t\tthis.state.whereMap = {};\n\t}\n\t_whereIn(/*key, val, inClause, conj*/) {\n\t\tlet args = getArgs('key:string, val:array, inClause:string, conj:string', arguments);\n\n\t\targs.key = this.driver.quoteIdentifiers(args.key);\n\t\tlet params = new Array(args.val.length);\n\t\tparams.fill('?');\n\n\t\targs.val.forEach((value) => {\n\t\t\tthis.state.whereValues.push(value);\n\t\t});\n\n\t\targs.conj = (this.state.queryMap.length > 0) ? \" \" + args.conj + \" \" : ' WHERE ';\n\t\tlet str = args.key + \" \" + args.inClause + \" (\" + params.join(',') + \") \";\n\n\t\tthis._appendMap(args.conj, str, 'whereIn');\n\t}\n\n\t_run(type, table, callback, sql, vals) {\n\n\t\tif ( ! sql)\n\t\t{\n\t\t\tsql = this._compile(type, table);\n\t\t}\n\n\t\tif ( ! vals)\n\t\t{\n\t\t\tvals = this.state.values.concat(this.state.whereValues);\n\t\t}\n\n//console.log(this.state);\n//console.log(sql);\n//console.log(vals);\n//console.log(callback);\n//console.log('------------------------');\n\n\t\t// Reset the state so another query can be built\n\t\tthis._resetState();\n\n\t\t// Pass the sql and values to the adapter to run on the database\n\t\tthis.adapter.execute(sql, vals, callback);\n\n\t}\n\n\t_getCompile(type, table, reset) {\n\t\treset = reset || false;\n\n\t\tlet sql = this._compile(type, table);\n\n\t\tif (reset) this._resetState();\n\n\t\treturn sql;\n\t}\n\n\t_resetState() {\n\t\tthis.state = new State();\n\t}\n\n\t// ----------------------------------------------------------------------------\n\t// ! Miscellaneous Methods\n\t// ----------------------------------------------------------------------------\n\n\t/**\n\t * Reset the object state for a new query\n\t *\n\t * @memberOf QueryBuilder\n\t * @return {void}\n\t */\n\tresetQuery() {\n\t\tthis._resetState();\n\t}\n\n\t/**\n\t * Returns the current class state for testing or other purposes\n\t *\n\t * @private\n\t * @return {Object}\n\t */\n\tgetState() {\n\t\treturn this.state;\n\t}\n\n\t/**\n\t * Closes the database connection for the current adapter\n\t *\n\t * @return {void}\n\t */\n\tend() {\n\t\tthis.adapter.close();\n\t}\n\n\t// ------------------------------------------------------------------------\n\t// ! Query Builder Methods\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Specify rows to select in the query\n\t *\n\t * @memberOf QueryBuilder\n\t * @param {String|Array} fields - The fields to select from the current table\n\t * @return {QueryBuilder}\n\t */\n\tselect(fields) {\n\n\t\t// Split/trim fields by comma\n\t\tfields = (Array.isArray(fields))\n\t\t\t? fields\n\t\t\t: fields.split(\",\").map(helpers.stringTrim);\n\n\t\t// Split on 'As'\n\t\tfields.forEach((field, index) => {\n\t\t\tif (field.match(/as/i))\n\t\t\t{\n\t\t\t\tfields[index] = field.split(/ as /i).map(helpers.stringTrim);\n\t\t\t}\n\t\t});\n\n\t\tlet safeArray = this.driver.quoteIdentifiers(fields);\n\n\t\t// Join the strings back together\n\t\tsafeArray.forEach((field, index) => {\n\t\t\tif (Array.isArray(field))\n\t\t\t{\n\t\t\t\tsafeArray[index] = safeArray[index].join(' AS ');\n\t\t\t}\n\t\t});\n\n\t\tthis.state.selectString += safeArray.join(', ');\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Specify the database table to select from\n\t *\n\t * @param {String} tableName - The table to use for the current query\n\t * @return {QueryBuilder}\n\t */\n\tfrom(tableName) {\n\t\t// Split identifiers on spaces\n\t\tlet identArray = tableName.trim().split(' ').map(helpers.stringTrim);\n\n\t\t// Quote/prefix identifiers\n\t\tidentArray[0] = this.driver.quoteTable(identArray[0]);\n\t\tidentArray = this.driver.quoteIdentifiers(identArray);\n\n\t\t// Put it back together\n\t\tthis.state.fromString = identArray.join(' ');\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Add a 'like/ and like' clause to the query\n\t *\n\t * @param {String} field - The name of the field to compare to\n\t * @param {String} val - The value to compare to\n\t * @param {String} [pos=both] - The placement of the wildcard character(s): before, after, or both\n\t * @return {QueryBuilder}\n\t */\n\tlike(field, val, pos) {\n\t\tthis._like(field, val, pos, ' LIKE ', 'AND');\n\t\treturn this;\n\t}\n\n\t/**\n\t * Add a 'not like/ and not like' clause to the query\n\t *\n\t * @param {String} field - The name of the field to compare to\n\t * @param {String} val - The value to compare to\n\t * @param {String} [pos=both] - The placement of the wildcard character(s): before, after, or both\n\t * @return {QueryBuilder}\n\t */\n\tnotLike(field, val, pos) {\n\t\tthis._like(field, val, pos, ' NOT LIKE ', 'AND');\n\t\treturn this;\n\t}\n\n\t/**\n\t * Add an 'or like' clause to the query\n\t *\n\t * @param {String} field - The name of the field to compare to\n\t * @param {String} val - The value to compare to\n\t * @param {String} [pos=both] - The placement of the wildcard character(s): before, after, or both\n\t * @return {QueryBuilder}\n\t */\n\torLike(field, val, pos) {\n\t\tthis._like(field, val, pos, ' LIKE ', 'OR');\n\t\treturn this;\n\t}\n\n\t/**\n\t * Add an 'or not like' clause to the query\n\t *\n\t * @param {String} field - The name of the field to compare to\n\t * @param {String} val - The value to compare to\n\t * @param {String} [pos=both] - The placement of the wildcard character(s): before, after, or both\n\t * @return {QueryBuilder}\n\t */\n\torNotLike(field, val, pos) {\n\t\tthis._like(field, val, pos, ' NOT LIKE ', 'OR');\n\t\treturn this;\n\t}\n\n\t/**\n\t * Add a 'having' clause\n\t *\n\t * @param {String|Object} key - The name of the field and the comparision operator, or an object\n\t * @param {String|Number} [val] - The value to compare if the value of key is a string\n\t * @return {QueryBuilder}\n\t */\n\thaving(/*key, [val]*/) {\n\t\tlet args = getArgs('key:string|object, [val]:string|number', arguments);\n\n\t\tthis._having(args.key, args.val, 'AND');\n\t\treturn this;\n\t}\n\n\t/**\n\t * Add an 'or having' clause\n\t *\n\t * @param {String|Object} key - The name of the field and the comparision operator, or an object\n\t * @param {String|Number} [val] - The value to compare if the value of key is a string\n\t * @return {QueryBuilder}\n\t */\n\torHaving(/*key, [val]*/) {\n\t\tlet args = getArgs('key:string|object, [val]:string|number', arguments);\n\n\t\tthis._having(args.key, args.val, 'OR');\n\t\treturn this;\n\t}\n\n\t/**\n\t * Set a 'where' clause\n\t *\n\t * @param {String|Object} key - The name of the field and the comparision operator, or an object\n\t * @param {String|Number} [val] - The value to compare if the value of key is a string\n\t * @return {QueryBuilder}\n\t */\n\twhere(key, val) {\n\t\tthis._where(key, val, 'AND');\n\t\treturn this;\n\t}\n\n\t/**\n\t * Set a 'or where' clause\n\t *\n\t * @param {String|Object} key - The name of the field and the comparision operator, or an object\n\t * @param {String|Number} [val] - The value to compare if the value of key is a string\n\t * @return {QueryBuilder}\n\t */\n\torWhere(key, val) {\n\t\tthis._where(key, val, 'OR');\n\t\treturn this;\n\t}\n\n\t/**\n\t * Select a field that is Null\n\t *\n\t * @param {String} field - The name of the field that has a NULL value\n\t * @return {QueryBuilder}\n\t */\n\twhereIsNull(field) {\n\t\tthis._whereNull(field, 'IS NULL', 'AND');\n\t\treturn this;\n\t}\n\n\t/**\n\t * Specify that a field IS NOT NULL\n\t *\n\t * @param {String} field\n\t * @return {QueryBuilder}\n\t */\n\twhereIsNotNull(field) {\n\t\tthis._whereNull(field, 'IS NOT NULL', 'AND');\n\t\treturn this;\n\t}\n\n\t/**\n\t * Field is null prefixed with 'OR'\n\t *\n\t * @param {String} field\n\t * @return {QueryBuilder}\n\t */\n\torWhereIsNull(field) {\n\t\tthis._whereNull(field, 'IS NULL', 'OR');\n\t\treturn this;\n\t}\n\n\t/**\n\t * Field is not null prefixed with 'OR'\n\t *\n\t * @param {String} field\n\t * @return {QueryBuilder}\n\t */\n\torWhereIsNotNull(field) {\n\t\tthis._whereNull(field, 'IS NOT NULL', 'OR');\n\t\treturn this;\n\t}\n\n\t/**\n\t * Set a 'where in' clause\n\t *\n\t * @param {String} key - the field to search\n\t * @param {Array} val - the array of items to search in\n\t * @return {QueryBuilder}\n\t */\n\twhereIn(key, val) {\n\t\tthis._whereIn(key, val, 'IN', 'AND');\n\t\treturn this;\n\t}\n\n\t/**\n\t * Set a 'or where in' clause\n\t *\n\t * @param {String} key - the field to search\n\t * @param {Array} val - the array of items to search in\n\t * @return {QueryBuilder}\n\t */\n\torWhereIn(key, val) {\n\t\tthis._whereIn(key, val, 'IN', 'OR');\n\t\treturn this;\n\t}\n\n\t/**\n\t * Set a 'where not in' clause\n\t *\n\t * @param {String} key - the field to search\n\t * @param {Array} val - the array of items to search in\n\t * @return {QueryBuilder}\n\t */\n\twhereNotIn(key, val) {\n\t\tthis._whereIn(key, val, 'NOT IN', 'AND');\n\t\treturn this;\n\t}\n\n\t/**\n\t * Set a 'or where not in' clause\n\t *\n\t * @param {String} key - the field to search\n\t * @param {Array} val - the array of items to search in\n\t * @return {QueryBuilder}\n\t */\n\torWhereNotIn(key, val) {\n\t\tthis._whereIn(key, val, 'NOT IN', 'OR');\n\t\treturn this;\n\t}\n\n\t/**\n\t * Set values for insertion or updating\n\t *\n\t * @param {String|Object} key - The key or object to use\n\t * @param {String} [val] - The value if using a scalar key\n\t * @return {QueryBuilder}\n\t */\n\tset(/* $key, [$val] */) {\n\t\tlet args = getArgs('$key, [$val]', arguments);\n\n\t\t// Set the appropriate state variables\n\t\tthis._mixedSet('setArrayKeys', 'key', args.$key, args.$val);\n\t\tthis._mixedSet('values', 'value', args.$key, args.$val);\n\n\t\t// Use the keys of the array to make the insert/update string\n\t\t// and escape the field names\n\t\tthis.state.setArrayKeys = this.state.setArrayKeys.map(this.driver._quote);\n\n\t\t// Generate the \"set\" string\n\t\tthis.state.setString = this.state.setArrayKeys.join('=?,');\n\t\tthis.state.setString += '=?';\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Add a join clause to the query\n\t *\n\t * @param {String} table - The table you are joining\n\t * @param {String} cond - The join condition.\n\t * @param {String} [type='inner'] - The type of join, which defaults to inner\n\t * @return {QueryBuilder}\n\t */\n\tjoin(table, cond, type) {\n\t\ttype = type || \"inner\";\n\n\t\t// Prefix/quote table name\n\t\ttable = table.split(' ').map(helpers.stringTrim);\n\t\ttable[0] = this.driver.quoteTable(table[0]);\n\t\ttable = table.map(this.driver.quoteIdentifiers);\n\t\ttable = table.join(' ');\n\n\t\t// Parse out the join condition\n\t\tlet parsedCondition = this.parser.compileJoin(cond);\n\t\tlet condition = table + ' ON ' + parsedCondition;\n\n\t\t// Append the join condition to the query map\n\t\tthis._appendMap(\"\\n\" + type.toUpperCase() + ' JOIN ', condition, 'join');\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Group the results by the selected field(s)\n\t *\n\t * @param {String|Array} field\n\t * @return {QueryBuilder}\n\t */\n\tgroupBy(field) {\n\t\tif ( ! helpers.isScalar(field))\n\t\t{\n\t\t\tlet newGroupArray = field.map(this.driver.quoteIdentifiers);\n\t\t\tthis.state.groupArray = this.state.groupArray.concat(newGroupArray);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tthis.state.groupArray.push(this.driver.quoteIdentifiers(field));\n\t\t}\n\n\t\tthis.state.groupString = ' GROUP BY ' + this.state.groupArray.join(',');\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Order the results by the selected field(s)\n\t *\n\t * @param {String} field - The field(s) to order by\n\t * @param {String} [type='ASC'] - The order direction, ASC or DESC\n\t * @return {QueryBuilder}\n\t */\n\torderBy(field, type) {\n\t\ttype = type || 'ASC';\n\n\t\t// Set the fields for later manipulation\n\t\tfield = this.driver.quoteIdentifiers(field);\n\n\t\tthis.state.orderArray[field] = type;\n\n\t\tlet orderClauses = [];\n\n\t\t// Flatten key/val pairs into an array of space-separated pairs\n\t\tObject.keys(this.state.orderArray).forEach((key) => {\n\t\t\torderClauses.push(key + ' ' + this.state.orderArray[key].toUpperCase());\n\t\t});\n\n\t\t// Set the final string\n\t\tthis.state.orderString = ' ORDER BY ' + orderClauses.join(', ');\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Put a limit on the query\n\t *\n\t * @param {Number} limit - The maximum number of rows to fetch\n\t * @param {Number} [offset] - The row number to start from\n\t * @return {QueryBuilder}\n\t */\n\tlimit(limit, offset) {\n\t\tthis.state.limit = limit;\n\t\tthis.state.offset = offset || null;\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Adds an open paren to the current query for logical grouping\n\t *\n\t * @return {QueryBuilder}\n\t */\n\tgroupStart() {\n\t\tlet conj = (this.state.queryMap.length < 1) ? ' WHERE ' : ' AND ';\n\t\tthis._appendMap(conj, '(', 'groupStart');\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Adds an open paren to the current query for logical grouping,\n\t * prefixed with 'OR'\n\t *\n\t * @return {QueryBuilder}\n\t */\n\torGroupStart() {\n\t\tthis._appendMap('', ' OR (', 'groupStart');\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Adds an open paren to the current query for logical grouping,\n\t * prefixed with 'OR NOT'\n\t *\n\t * @return {QueryBuilder}\n\t */\n\torNotGroupStart() {\n\t\tthis._appendMap('', ' OR NOT (', 'groupStart');\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Ends a logical grouping started with one of the groupStart methods\n\t *\n\t * @return {QueryBuilder}\n\t */\n\tgroupEnd() {\n\t\tthis._appendMap('', ')', 'groupEnd');\n\n\t\treturn this;\n\t}\n\n\t// ------------------------------------------------------------------------\n\t// ! Result Methods\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Get the results of the compiled query\n\t *\n\t * @param {String} [table] - The table to select from\n\t * @param {Number} [limit] - A limit for the query\n\t * @param {Number} [offset] - An offset for the query\n\t * @param {Function} callback - A callback for receiving the result\n\t * @return {void}\n\t */\n\tget(/* [table], [limit], [offset], callback */) {\n\t\tlet args = getArgs('[table]:string, [limit]:number, [offset]:number, callback:function', arguments);\n\n\t\tif (args.table) {\n\t\t\tthis.from(args.table);\n\t\t}\n\n\t\tif (args.limit) {\n\t\t\tthis.limit(args.limit, args.offset);\n\t\t}\n\n\t\t// Run the query\n\t\tthis._run('get', args.table, args.callback);\n\t}\n\n\t/**\n\t * Run the generated insert query\n\t *\n\t * @param {String} table - The table to insert into\n\t * @param {Object} [data] - Data to insert, if not already added with the 'set' method\n\t * @param {Function} callback - Callback for handling response from the database\n\t * @return {void}\n\t */\n\tinsert(/* table, data, callback */) {\n\t\tlet args = getArgs('table:string, [data]:object, callback:function', arguments);\n\n\t\tif (args.data) {\n\t\t\tthis.set(args.data);\n\t\t}\n\n\t\t// Run the query\n\t\tthis._run('insert', this.driver.quoteTable(args.table), args.callback);\n\t}\n\n\t/**\n\t * Insert multiple sets of rows at a time\n\t *\n\t * @param {String} table - The table to insert into\n\t * @param {Array} data - The array of objects containing data rows to insert\n\t * @param {Function} callback - Callback for handling database response\n\t * @example query.insertBatch('foo',[{id:1,val:'bar'},{id:2,val:'baz'}], callbackFunction);\n\t * @return {void}\n\t */\n\tinsertBatch(/* table, data, callback */) {\n\t\tlet args = getArgs('table:string, data:array, callback:function', arguments);\n\t\tlet batch = this.driver.insertBatch(args.table, args.data);\n\n\t\t// Run the query\n\t\tthis._run('', '', args.callback, batch.sql, batch.values);\n\t}\n\n\t/**\n\t * Run the generated update query\n\t *\n\t * @param {String} table - The table to insert into\n\t * @param {Object} [data] - Data to insert, if not already added with the 'set' method\n\t * @param {Function} callback - Callback for handling response from the database\n\t * @return {void}\n\t */\n\tupdate(/*table, data, callback*/) {\n\t\tlet args = getArgs('table:string, [data]:object, callback:function', arguments);\n\n\t\tif (args.data) {\n\t\t\tthis.set(args.data);\n\t\t}\n\n\t\t// Run the query\n\t\tthis._run('update', this.driver.quoteTable(args.table), args.callback);\n\t}\n\n\t/**\n\t * Run the generated delete query\n\t *\n\t * @param {String} table - The table to insert into\n\t * @param {Object} [where] - Where clause for delete statement\n\t * @param {Function} callback - Callback for handling response from the database\n\t * @return {void}\n\t */\n\tdelete(/*table, [where], callback*/) {\n\t\tlet args = getArgs('table:string, [where]:object, callback:function', arguments);\n\n\t\tif (args.where)\n\t\t{\n\t\t\tthis.where(args.where);\n\t\t}\n\n\t\t// Run the query\n\t\tthis._run('delete', this.driver.quoteTable(args.table), args.callback);\n\t}\n\n\t// ------------------------------------------------------------------------\n\t// ! Methods returning SQL\n\t// ------------------------------------------------------------------------\n\n\t/**\n\t * Return generated select query SQL\n\t *\n\t * @param {String} [table] - the name of the table to retrieve from\n\t * @param {Boolean} [reset=true] - Whether to reset the query builder so another query can be built\n\t * @return {String}\n\t */\n\tgetCompiledSelect(/*table, reset*/) {\n\t\tlet args = getArgs('[table]:string, [reset]:boolean', arguments);\n\t\tif (args.table)\n\t\t{\n\t\t\tthis.from(args.table);\n\t\t}\n\n\t\treturn this._getCompile('get', args.table, args.reset);\n\t}\n\n\t/**\n\t * Return generated insert query SQL\n\t *\n\t * @param {String} table - the name of the table to insert into\n\t * @param {Boolean} [reset=true] - Whether to reset the query builder so another query can be built\n\t * @return {String}\n\t */\n\tgetCompiledInsert(table, reset) {\n\t\treturn this._getCompile('insert', this.driver.quoteTable(table), reset);\n\t}\n\n\t/**\n\t * Return generated update query SQL\n\t *\n\t * @param {String} table - the name of the table to update\n\t * @param {Boolean} [reset=true] - Whether to reset the query builder so another query can be built\n\t * @return {String}\n\t */\n\tgetCompiledUpdate(table, reset) {\n\t\treturn this._getCompile('update', this.driver.quoteTable(table), reset);\n\t}\n\n\t/**\n\t * Return generated delete query SQL\n\t *\n\t * @param {String} table - the name of the table to delete from\n\t * @param {Boolean} [reset=true] - Whether to reset the query builder so another query can be built\n\t * @return {String}\n\t */\n\tgetCompiledDelete(table, reset) {\n\t\treturn this._getCompile('delete', this.driver.quoteTable(table), reset);\n\t}\n}"],"sourceRoot":"/source/"} \ No newline at end of file diff --git a/lib/QueryParser.js b/lib/QueryParser.js index 13bd592..e3beee6 100644 --- a/lib/QueryParser.js +++ b/lib/QueryParser.js @@ -1,35 +1,20 @@ 'use strict'; -var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); - -var _helpers = require('./helpers'); - -var _helpers2 = _interopRequireDefault(_helpers); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } +let helpers = require('./helpers'); // -------------------------------------------------------------------------- -/** - * @constructor - * @param {Driver} - The driver object for the database in use - * @module query-parser - */ -module.exports = (function () { +module.exports = class QueryParser { /** - * @constructor - * @param {Driver} - The driver object for the database in use - * @return {void} - */ - - function QueryParser(driver) { - _classCallCheck(this, QueryParser); - + * @constructor + * + * @param {Driver} driver - The driver object for the database in use + * @return {void} + */ + constructor(driver) { this.driver = driver; - var matchPatterns = { + let matchPatterns = { 'function': /([a-z0-9_]+\((.*)\))/i, operator: /\!=?|\=|\+|&&?|~|\|\|?|\^|\/|<>|>=?|<=?|\-|%|OR|AND|NOT|XOR/ig, literal: /([0-9]+)|'(.*?)'|true|false/ig @@ -37,242 +22,248 @@ module.exports = (function () { // Full pattern for identifiers // Making sure that literals and functions aren't matched - matchPatterns.identifier = new RegExp('(' + '(?!' + matchPatterns['function'].source + '|' + matchPatterns.literal.source + ')' + '([a-z_\-]+[0-9]*\\.?)' + ')+', 'ig'); + matchPatterns.identifier = new RegExp( + '(' + + '(?!' + + matchPatterns['function'].source + '|' + + matchPatterns.literal.source + + ')' + + '([a-z_\-]+[0-9]*\\.?)' + + ')+' + , 'ig'); // Full pattern for determining ordering of the pieces - matchPatterns.joinCombined = new RegExp(matchPatterns['function'].source + "+|" + matchPatterns.literal.source + '+|' + matchPatterns.identifier.source + '|(' + matchPatterns.operator.source + ')+', 'ig'); + matchPatterns.joinCombined = new RegExp( + matchPatterns['function'].source + "+|" + + matchPatterns.literal.source + '+|' + + matchPatterns.identifier.source + + '|(' + matchPatterns.operator.source + ')+' + , 'ig'); this.matchPatterns = matchPatterns; - this.identifierBlacklist = ['true', 'false', 'null']; + this.identifierBlacklist = ['true','false','null']; } /** - * Filter matched patterns - * - * @param {Array} array - * @return {Array|null} - */ + * Filter matched patterns + * + * @param {Array} array - Set of possible matches + * @return {Array|null} - Filtered set of possible matches + */ + filterMatches(array) { + let output = []; - _createClass(QueryParser, [{ - key: 'filterMatches', - value: function filterMatches(array) { - var output = []; + // Return non-array matches + if (helpers.isNull(array)) return null; + if (helpers.isScalar(array) || helpers.isUndefined(array)) return output; - // Return non-array matches - if (_helpers2.default.isNull(array)) return null; - if (_helpers2.default.isScalar(array) || _helpers2.default.isUndefined(array)) return output; + array.forEach(item => { + output.push(item); + }); + return output; + } - array.forEach(function (item) { - output.push(item); - }); - return output; - } + /** + * Check if the string contains an operator, and if so, return the operator(s). + * If there are no matches, return null + * + * @param {String} string - the string to check + * @return {Array|null} - List of operators + */ + hasOperator(string) { + return this.filterMatches(string.match(this.matchPatterns.operator)); + } - /** - * Check if the string contains an operator, and if so, return the operator(s). - * If there are no matches, return null - * - * @param {String} string - the string to check - * @return {Array|null} - */ + /** + * Tokenize the sql into parts for additional processing + * + * @param {String} sql - Join sql to parse + * @return {Object} - Join condition components + */ + parseJoin(sql) { + let matches = {}; + let output = { + functions: [], + identifiers: [], + operators: [], + literals: [] + }; - }, { - key: 'hasOperator', - value: function hasOperator(string) { - return this.filterMatches(string.match(this.matchPatterns.operator)); - } + // Get clause components + matches.functions = sql.match(new RegExp(this.matchPatterns['function'].source, 'ig')); + matches.identifiers = sql.match(this.matchPatterns.identifier); + matches.operators = sql.match(this.matchPatterns.operator); + matches.literals = sql.match(this.matchPatterns.literal); - /** - * Tokenize the sql into parts for additional processing - * - * @param {String} sql - * @return {Object} - */ + // Get everything at once for ordering + matches.combined = sql.match(this.matchPatterns.joinCombined); - }, { - key: 'parseJoin', - value: function parseJoin(sql) { - var _this = this; + // Flatten the matches to increase relevance + Object.keys(matches).forEach(key => { + output[key] = this.filterMatches(matches[key]); + }); - var matches = {}; - var output = { - functions: [], - identifiers: [], - operators: [], - literals: [] - }; + return output; + } - // Get clause components - matches.functions = sql.match(new RegExp(this.matchPatterns['function'].source, 'ig')); - matches.identifiers = sql.match(this.matchPatterns.identifier); - matches.operators = sql.match(this.matchPatterns.operator); - matches.literals = sql.match(this.matchPatterns.literal); + /** + * Return the output of the parsing of the join condition + * + * @param {String} condition - The join condition to evalate + * @return {String} - The parsed/escaped join condition + */ + compileJoin(condition) { + let parts = this.parseJoin(condition); + let count = parts.identifiers.length; + let i; - // Get everything at once for ordering - matches.combined = sql.match(this.matchPatterns.joinCombined); + // Quote the identifiers + parts.combined.forEach((part, i) => { + if (parts.identifiers.indexOf(part) !== -1 && ! helpers.isNumber(part)) + { + parts.combined[i] = this.driver.quoteIdentifiers(part); + } + }); - // Flatten the matches to increase relevance - Object.keys(matches).forEach(function (key) { - output[key] = _this.filterMatches(matches[key]); - }); + return parts.combined.join(' '); + } - return output; - } + /** + * Parse a where clause to separate functions from values + * + * @param {Driver} driver - The current db driver + * @param {State} state - Query Builder state object + * @return {String} - The parsed/escaped where condition + */ + parseWhere(driver, state) { + let whereMap = state.whereMap; + let whereValues = state.rawWhereValues; - /** - * Return the output of the parsing of the join condition - * - * @param {String} condition - The join condition to evalate - * @return {String} - The parsed/escaped join condition - */ + let outputMap = []; + let outputValues = []; - }, { - key: 'compileJoin', - value: function compileJoin(condition) { - var _this2 = this; + Object.keys(whereMap).forEach(key => { + // Combine fields, operators, functions and values into a full clause + // to have a common starting flow + let fullClause = ''; - var parts = this.parseJoin(condition); - var count = parts.identifiers.length; - var i = undefined; + // Add an explicit = sign where one is inferred + if ( ! this.hasOperator(key)) + { + fullClause = key + ' = ' + whereMap[key]; + } + else if (whereMap[key] === key) + { + fullClause = key; + } + else + { + fullClause = key + ' ' + whereMap[key]; + } - // Quote the identifiers - parts.combined.forEach(function (part, i) { - if (parts.identifiers.indexOf(part) !== -1 && !_helpers2.default.isNumber(part)) { - parts.combined[i] = _this2.driver.quoteIdentifiers(part); - } - }); + // Separate the clause into separate pieces + let parts = this.parseJoin(fullClause); - return parts.combined.join(' '); - } + // Filter explicit literals from lists of matches + if (whereValues.indexOf(whereMap[key]) !== -1) + { + let value = whereMap[key]; + let identIndex = (helpers.isArray(parts.identifiers)) ? parts.identifiers.indexOf(value) : -1; + let litIndex = (helpers.isArray(parts.literals)) ? parts.literals.indexOf(value) : -1; + let combIndex = (helpers.isArray(parts.combined)) ? parts.combined.indexOf(value) : -1; + let funcIndex = (helpers.isArray(parts.functions)) ? parts.functions.indexOf(value) : -1; + let inOutputArray = outputValues.indexOf(value) !== -1; - /** - * Parse a where clause to separate functions from values - * - * @param {Driver} driver - * @param {State} state - * @return {String} - The parsed/escaped where condition - */ + // Remove the identifier in question, + // and add to the output values array + if (identIndex !== -1) + { + parts.identifiers.splice(identIndex, 1); - }, { - key: 'parseWhere', - value: function parseWhere(driver, state) { - var _this3 = this; - - var whereMap = state.whereMap; - var whereValues = state.rawWhereValues; - - var outputMap = []; - var outputValues = []; - - Object.keys(whereMap).forEach(function (key) { - // Combine fields, operators, functions and values into a full clause - // to have a common starting flow - var fullClause = ''; - - // Add an explicit = sign where one is inferred - if (!_this3.hasOperator(key)) { - fullClause = key + ' = ' + whereMap[key]; - } else if (whereMap[key] === key) { - fullClause = key; - } else { - fullClause = key + ' ' + whereMap[key]; + if ( ! inOutputArray) + { + outputValues.push(value); + inOutputArray = true; + } } - // Separate the clause into separate pieces - var parts = _this3.parseJoin(fullClause); + // Remove the value from the literals list + // so it is not added twice + if (litIndex !== -1) + { + parts.literals.splice(litIndex, 1); - // Filter explicit literals from lists of matches - if (whereValues.indexOf(whereMap[key]) !== -1) { - var value = whereMap[key]; - var identIndex = _helpers2.default.isArray(parts.identifiers) ? parts.identifiers.indexOf(value) : -1; - var litIndex = _helpers2.default.isArray(parts.literals) ? parts.literals.indexOf(value) : -1; - var combIndex = _helpers2.default.isArray(parts.combined) ? parts.combined.indexOf(value) : -1; - var funcIndex = _helpers2.default.isArray(parts.functions) ? parts.functions.indexOf(value) : -1; - var inOutputArray = outputValues.indexOf(value) !== -1; + if ( ! inOutputArray) + { + outputValues.push(value); + inOutputArray = true; + } + } - // Remove the identifier in question, - // and add to the output values array - if (identIndex !== -1) { - parts.identifiers.splice(identIndex, 1); + // Remove the value from the combined list + // and replace it with a placeholder + if (combIndex !== -1) + { + // Make sure to skip functions when replacing values + if (funcIndex === -1) + { + parts.combined[combIndex] = '?'; - if (!inOutputArray) { + if ( ! inOutputArray) + { outputValues.push(value); inOutputArray = true; } } + } + } - // Remove the value from the literals list - // so it is not added twice - if (litIndex !== -1) { - parts.literals.splice(litIndex, 1); + // Filter false positive identifiers + parts.identifiers = parts.identifiers || []; + parts.identifiers = parts.identifiers.filter(item => { + let isInCombinedMatches = parts.combined.indexOf(item) !== -1; + let isNotInBlackList = this.identifierBlacklist.indexOf(item.toLowerCase()) === -1; - if (!inOutputArray) { - outputValues.push(value); - inOutputArray = true; - } + return isInCombinedMatches && isNotInBlackList; + }, this); + + // Quote identifiers + if (helpers.isArray(parts.identifiers)) + { + parts.identifiers.forEach(ident => { + let index = parts.combined.indexOf(ident); + if (index !== -1) + { + parts.combined[index] = driver.quoteIdentifiers(ident); } + }); + } - // Remove the value from the combined list - // and replace it with a placeholder - if (combIndex !== -1) { - // Make sure to skip functions when replacing values - if (funcIndex === -1) { - parts.combined[combIndex] = '?'; + // Replace each literal with a placeholder in the map + // and add the literal to the values, + // This should only apply to literal values that are not + // explicitly mapped to values, but have to be parsed from + // a where condition, + if (helpers.isArray(parts.literals)) + { + parts.literals.forEach(lit => { + let litIndex = parts.combined.indexOf(lit); - if (!inOutputArray) { - outputValues.push(value); - inOutputArray = true; - } - } + if (litIndex !== -1) + { + parts.combined[litIndex] = (helpers.isArray(parts.operators)) ? '?' : '= ?'; + outputValues.push(lit); } - } + }); + } - // Filter false positive identifiers - parts.identifiers = parts.identifiers || []; - parts.identifiers = parts.identifiers.filter(function (item) { - var isInCombinedMatches = parts.combined.indexOf(item) !== -1; - var isNotInBlackList = _this3.identifierBlacklist.indexOf(item.toLowerCase()) === -1; + outputMap.push(parts.combined.join(' ')); + }); - return isInCombinedMatches && isNotInBlackList; - }, _this3); + state.rawWhereValues = []; + state.whereValues = state.whereValues.concat(outputValues); + state.whereMap = outputMap; - // Quote identifiers - if (_helpers2.default.isArray(parts.identifiers)) { - parts.identifiers.forEach(function (ident) { - var index = parts.combined.indexOf(ident); - if (index !== -1) { - parts.combined[index] = driver.quoteIdentifiers(ident); - } - }); - } - - // Replace each literal with a placeholder in the map - // and add the literal to the values, - // This should only apply to literal values that are not - // explicitly mapped to values, but have to be parsed from - // a where condition, - if (_helpers2.default.isArray(parts.literals)) { - parts.literals.forEach(function (lit) { - var litIndex = parts.combined.indexOf(lit); - - if (litIndex !== -1) { - parts.combined[litIndex] = _helpers2.default.isArray(parts.operators) ? '?' : '= ?'; - outputValues.push(lit); - } - }); - } - - outputMap.push(parts.combined.join(' ')); - }); - - state.rawWhereValues = []; - state.whereValues = state.whereValues.concat(outputValues); - state.whereMap = outputMap; - - return state; - } - }]); - - return QueryParser; -})(); -//# sourceMappingURL=QueryParser.js.map + return state; + } +} \ No newline at end of file diff --git a/lib/QueryParser.js.map b/lib/QueryParser.js.map deleted file mode 100644 index 5a11709..0000000 --- a/lib/QueryParser.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["QueryParser.js"],"names":[],"mappings":"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","file":"QueryParser.js","sourcesContent":["'use strict';\n\nimport helpers from './helpers';\n\n\n// --------------------------------------------------------------------------\n\n/**\n * @constructor\n * @param {Driver} - The driver object for the database in use\n * @module query-parser\n */\nmodule.exports = class QueryParser {\n\t/**\n\t * @constructor\n\t * @param {Driver} - The driver object for the database in use\n\t * @return {void}\n\t */\n\tconstructor(driver) {\n\t\tthis.driver = driver;\n\n\t\tlet matchPatterns = {\n\t\t\t'function': /([a-z0-9_]+\\((.*)\\))/i,\n\t\t\toperator: /\\!=?|\\=|\\+|&&?|~|\\|\\|?|\\^|\\/|<>|>=?|<=?|\\-|%|OR|AND|NOT|XOR/ig,\n\t\t\tliteral: /([0-9]+)|'(.*?)'|true|false/ig\n\t\t};\n\n\t\t// Full pattern for identifiers\n\t\t// Making sure that literals and functions aren't matched\n\t\tmatchPatterns.identifier = new RegExp(\n\t\t\t'('\n\t\t\t\t+ '(?!'\n\t\t\t\t\t+ matchPatterns['function'].source + '|'\n\t\t\t\t\t+ matchPatterns.literal.source\n\t\t\t\t+ ')'\n\t\t\t\t+ '([a-z_\\-]+[0-9]*\\\\.?)'\n\t\t\t+ ')+'\n\t\t, 'ig');\n\n\t\t// Full pattern for determining ordering of the pieces\n\t\tmatchPatterns.joinCombined = new RegExp(\n\t\t\tmatchPatterns['function'].source + \"+|\"\n\t\t\t+ matchPatterns.literal.source + '+|'\n\t\t\t+ matchPatterns.identifier.source\n\t\t\t+ '|(' + matchPatterns.operator.source + ')+'\n\t\t, 'ig');\n\n\t\tthis.matchPatterns = matchPatterns;\n\t\tthis.identifierBlacklist = ['true','false','null'];\n\t}\n\n\t/**\n\t * Filter matched patterns\n\t *\n\t * @param {Array} array\n\t * @return {Array|null}\n\t */\n\tfilterMatches(array) {\n\t\tlet output = [];\n\n\t\t// Return non-array matches\n\t\tif (helpers.isNull(array)) return null;\n\t\tif (helpers.isScalar(array) || helpers.isUndefined(array)) return output;\n\n\t\tarray.forEach((item) => {\n\t\t\toutput.push(item);\n\t\t});\n\t\treturn output;\n\t}\n\n\t/**\n\t * Check if the string contains an operator, and if so, return the operator(s).\n\t * If there are no matches, return null\n\t *\n\t * @param {String} string - the string to check\n\t * @return {Array|null}\n\t */\n\thasOperator(string) {\n\t\treturn this.filterMatches(string.match(this.matchPatterns.operator));\n\t}\n\n\t/**\n\t * Tokenize the sql into parts for additional processing\n\t *\n\t * @param {String} sql\n\t * @return {Object}\n\t */\n\tparseJoin(sql) {\n\t\tlet matches = {};\n\t\tlet output = {\n\t\t\tfunctions: [],\n\t\t\tidentifiers: [],\n\t\t\toperators: [],\n\t\t\tliterals: []\n\t\t};\n\n\t\t// Get clause components\n\t\tmatches.functions = sql.match(new RegExp(this.matchPatterns['function'].source, 'ig'));\n\t\tmatches.identifiers = sql.match(this.matchPatterns.identifier);\n\t\tmatches.operators = sql.match(this.matchPatterns.operator);\n\t\tmatches.literals = sql.match(this.matchPatterns.literal);\n\n\t\t// Get everything at once for ordering\n\t\tmatches.combined = sql.match(this.matchPatterns.joinCombined);\n\n\t\t// Flatten the matches to increase relevance\n\t\tObject.keys(matches).forEach((key) => {\n\t\t\toutput[key] = this.filterMatches(matches[key]);\n\t\t});\n\n\t\treturn output;\n\t}\n\n\t/**\n\t * Return the output of the parsing of the join condition\n\t *\n\t * @param {String} condition - The join condition to evalate\n\t * @return {String} - The parsed/escaped join condition\n\t */\n\tcompileJoin(condition) {\n\t\tlet parts = this.parseJoin(condition);\n\t\tlet count = parts.identifiers.length;\n\t\tlet i;\n\n\t\t// Quote the identifiers\n\t\tparts.combined.forEach((part, i) => {\n\t\t\tif (parts.identifiers.indexOf(part) !== -1 && ! helpers.isNumber(part))\n\t\t\t{\n\t\t\t\tparts.combined[i] = this.driver.quoteIdentifiers(part);\n\t\t\t}\n\t\t});\n\n\t\treturn parts.combined.join(' ');\n\t}\n\n\t/**\n\t * Parse a where clause to separate functions from values\n\t *\n\t * @param {Driver} driver\n\t * @param {State} state\n\t * @return {String} - The parsed/escaped where condition\n\t */\n\tparseWhere(driver, state) {\n\t\tlet whereMap = state.whereMap;\n\t\tlet\twhereValues = state.rawWhereValues;\n\n\t\tlet outputMap = [];\n\t\tlet outputValues = [];\n\n\t\tObject.keys(whereMap).forEach((key) => {\n\t\t\t// Combine fields, operators, functions and values into a full clause\n\t\t\t// to have a common starting flow\n\t\t\tlet fullClause = '';\n\n\t\t\t// Add an explicit = sign where one is inferred\n\t\t\tif ( ! this.hasOperator(key))\n\t\t\t{\n\t\t\t\tfullClause = key + ' = ' + whereMap[key];\n\t\t\t}\n\t\t\telse if (whereMap[key] === key)\n\t\t\t{\n\t\t\t\tfullClause = key;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tfullClause = key + ' ' + whereMap[key];\n\t\t\t}\n\n\t\t\t// Separate the clause into separate pieces\n\t\t\tlet parts = this.parseJoin(fullClause);\n\n\t\t\t// Filter explicit literals from lists of matches\n\t\t\tif (whereValues.indexOf(whereMap[key]) !== -1)\n\t\t\t{\n\t\t\t\tlet value = whereMap[key];\n\t\t\t\tlet identIndex = (helpers.isArray(parts.identifiers)) ? parts.identifiers.indexOf(value) : -1;\n\t\t\t\tlet litIndex = (helpers.isArray(parts.literals)) ? parts.literals.indexOf(value) : -1;\n\t\t\t\tlet combIndex = (helpers.isArray(parts.combined)) ? parts.combined.indexOf(value) : -1;\n\t\t\t\tlet funcIndex = (helpers.isArray(parts.functions)) ? parts.functions.indexOf(value) : -1;\n\t\t\t\tlet inOutputArray = outputValues.indexOf(value) !== -1;\n\n\t\t\t\t// Remove the identifier in question,\n\t\t\t\t// and add to the output values array\n\t\t\t\tif (identIndex !== -1)\n\t\t\t\t{\n\t\t\t\t\tparts.identifiers.splice(identIndex, 1);\n\n\t\t\t\t\tif ( ! inOutputArray)\n\t\t\t\t\t{\n\t\t\t\t\t\toutputValues.push(value);\n\t\t\t\t\t\tinOutputArray = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Remove the value from the literals list\n\t\t\t\t// so it is not added twice\n\t\t\t\tif (litIndex !== -1)\n\t\t\t\t{\n\t\t\t\t\tparts.literals.splice(litIndex, 1);\n\n\t\t\t\t\tif ( ! inOutputArray)\n\t\t\t\t\t{\n\t\t\t\t\t\toutputValues.push(value);\n\t\t\t\t\t\tinOutputArray = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Remove the value from the combined list\n\t\t\t\t// and replace it with a placeholder\n\t\t\t\tif (combIndex !== -1)\n\t\t\t\t{\n\t\t\t\t\t// Make sure to skip functions when replacing values\n\t\t\t\t\tif (funcIndex === -1)\n\t\t\t\t\t{\n\t\t\t\t\t\tparts.combined[combIndex] = '?';\n\n\t\t\t\t\t\tif ( ! inOutputArray)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\toutputValues.push(value);\n\t\t\t\t\t\t\tinOutputArray = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Filter false positive identifiers\n\t\t\tparts.identifiers = parts.identifiers || [];\n\t\t\tparts.identifiers = parts.identifiers.filter((item) => {\n\t\t\t\tlet isInCombinedMatches = parts.combined.indexOf(item) !== -1;\n\t\t\t\tlet isNotInBlackList = this.identifierBlacklist.indexOf(item.toLowerCase()) === -1;\n\n\t\t\t\treturn isInCombinedMatches && isNotInBlackList;\n\t\t\t}, this);\n\n\t\t\t// Quote identifiers\n\t\t\tif (helpers.isArray(parts.identifiers))\n\t\t\t{\n\t\t\t\tparts.identifiers.forEach((ident) => {\n\t\t\t\t\tlet index = parts.combined.indexOf(ident);\n\t\t\t\t\tif (index !== -1)\n\t\t\t\t\t{\n\t\t\t\t\t\tparts.combined[index] = driver.quoteIdentifiers(ident);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Replace each literal with a placeholder in the map\n\t\t\t// and add the literal to the values,\n\t\t\t// This should only apply to literal values that are not\n\t\t\t// explicitly mapped to values, but have to be parsed from\n\t\t\t// a where condition,\n\t\t\tif (helpers.isArray(parts.literals))\n\t\t\t{\n\t\t\t\tparts.literals.forEach((lit) => {\n\t\t\t\t\tlet litIndex = parts.combined.indexOf(lit);\n\n\t\t\t\t\tif (litIndex !== -1)\n\t\t\t\t\t{\n\t\t\t\t\t\tparts.combined[litIndex] = (helpers.isArray(parts.operators)) ? '?' : '= ?';\n\t\t\t\t\t\toutputValues.push(lit);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\n\t\t\toutputMap.push(parts.combined.join(' '));\n\t\t});\n\n\t\tstate.rawWhereValues = [];\n\t\tstate.whereValues = state.whereValues.concat(outputValues);\n\t\tstate.whereMap = outputMap;\n\n\t\treturn state;\n\t}\n}"],"sourceRoot":"/source/"} \ No newline at end of file diff --git a/lib/State.js b/lib/State.js index f290f7d..1e6a889 100644 --- a/lib/State.js +++ b/lib/State.js @@ -1,34 +1,29 @@ -'use strict' +'use strict'; /** @module State */ -; +module.exports = class State { + constructor() { + // Arrays/maps + this.queryMap = []; + this.values = []; + this.whereValues = []; + this.setArrayKeys = []; + this.orderArray = []; + this.groupArray = []; + this.havingMap = []; + this.whereMap = []; + this.rawWhereValues = []; -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + // Partials + this.selectString = ''; + this.fromString = ''; + this.setString = ''; + this.orderString = ''; + this.groupString = ''; -module.exports = function State() { - _classCallCheck(this, State); - - // Arrays/maps - this.queryMap = []; - this.values = []; - this.whereValues = []; - this.setArrayKeys = []; - this.orderArray = []; - this.groupArray = []; - this.havingMap = []; - this.whereMap = []; - this.rawWhereValues = []; - - // Partials - this.selectString = ''; - this.fromString = ''; - this.setString = ''; - this.orderString = ''; - this.groupString = ''; - - // Other various values - this.limit = null; - this.offset = null; -}; -// End of module State -//# sourceMappingURL=State.js.map + // Other various values + this.limit = null; + this.offset = null; + } +} +// End of module State \ No newline at end of file diff --git a/lib/State.js.map b/lib/State.js.map deleted file mode 100644 index 25d0f9b..0000000 --- a/lib/State.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["State.js"],"names":[],"mappings":"AAAA;;;AAAY,CAAC;;;;AAGb,MAAM,CAAC,OAAO,GACb,SADsB,KAAK,GACb;uBADQ,KAAK;;;AAG1B,KAAI,CAAC,QAAQ,GAAG,EAAE,CAAC;AACnB,KAAI,CAAC,MAAM,GAAG,EAAE,CAAC;AACjB,KAAI,CAAC,WAAW,GAAG,EAAE,CAAC;AACtB,KAAI,CAAC,YAAY,GAAG,EAAE,CAAC;AACvB,KAAI,CAAC,UAAU,GAAG,EAAE,CAAC;AACrB,KAAI,CAAC,UAAU,GAAG,EAAE,CAAC;AACrB,KAAI,CAAC,SAAS,GAAG,EAAE,CAAC;AACpB,KAAI,CAAC,QAAQ,GAAG,EAAE,CAAC;AACnB,KAAI,CAAC,cAAc,GAAG,EAAE;;;AAAC,AAGzB,KAAI,CAAC,YAAY,GAAG,EAAE,CAAC;AACvB,KAAI,CAAC,UAAU,GAAG,EAAE,CAAC;AACrB,KAAI,CAAC,SAAS,GAAG,EAAE,CAAC;AACpB,KAAI,CAAC,WAAW,GAAG,EAAE,CAAC;AACtB,KAAI,CAAC,WAAW,GAAG,EAAE;;;AAAC,AAGtB,KAAI,CAAC,KAAK,GAAG,IAAI,CAAC;AAClB,KAAI,CAAC,MAAM,GAAG,IAAI,CAAC;CACnB,AACD;;AAAA","file":"State.js","sourcesContent":["'use strict';\n\n/** @module State */\nmodule.exports = class State {\n\tconstructor() {\n\t\t// Arrays/maps\n\t\tthis.queryMap = [];\n\t\tthis.values = [];\n\t\tthis.whereValues = [];\n\t\tthis.setArrayKeys = [];\n\t\tthis.orderArray = [];\n\t\tthis.groupArray = [];\n\t\tthis.havingMap = [];\n\t\tthis.whereMap = [];\n\t\tthis.rawWhereValues = [];\n\n\t\t// Partials\n\t\tthis.selectString = '';\n\t\tthis.fromString = '';\n\t\tthis.setString = '';\n\t\tthis.orderString = '';\n\t\tthis.groupString = '';\n\n\t\t// Other various values\n\t\tthis.limit = null;\n\t\tthis.offset = null;\n\t}\n}\n// End of module State"],"sourceRoot":"/source/"} \ No newline at end of file diff --git a/lib/adapters/dblite.js b/lib/adapters/dblite.js index 3640241..e649d2d 100644 --- a/lib/adapters/dblite.js +++ b/lib/adapters/dblite.js @@ -1,50 +1,19 @@ 'use strict'; -var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); +let Adapter = require('../Adapter'), + getArgs = require('getargs'); -var _Adapter2 = require('../Adapter'); - -var _Adapter3 = _interopRequireDefault(_Adapter2); - -var _getargs = require('getargs'); - -var _getargs2 = _interopRequireDefault(_getargs); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } - -function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } - -/** @module adapters/dblite */ -module.exports = (function (_Adapter) { - _inherits(dblite, _Adapter); - - function dblite() { - _classCallCheck(this, dblite); - - return _possibleConstructorReturn(this, Object.getPrototypeOf(dblite).apply(this, arguments)); +module.exports = class dblite extends Adapter { + /** + * 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} + */ + execute(/*sql, params, callback*/) { + let args = getArgs('sql:string, [params]:array, callback:function', arguments); + this.instance.query(args.sql, args.params, args.callback); } - - _createClass(dblite, [{ - key: 'execute', - - /** - * 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 - */ - value: function execute() /*sql, params, callback*/{ - var args = (0, _getargs2.default)('sql:string, [params]:array, callback:function', arguments); - this.instance.query(args.sql, args.params, args.callback); - } - }]); - - return dblite; -})(_Adapter3.default); -//# sourceMappingURL=dblite.js.map +} \ No newline at end of file diff --git a/lib/adapters/dblite.js.map b/lib/adapters/dblite.js.map deleted file mode 100644 index 9579bdd..0000000 --- a/lib/adapters/dblite.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["dblite.js"],"names":[],"mappings":"AAAA,YAAY,CAAC;;;;;;;;;;;;;;;;;;;;;AAMb,MAAM,CAAC,OAAO;WAAS,MAAM;;UAAN,MAAM;wBAAN,MAAM;;gEAAN,MAAM;;;cAAN,MAAM;;;;;;;;;;;qDASO;AAClC,OAAI,IAAI,GAAG,uBAAQ,+CAA+C,EAAE,SAAS,CAAC,CAAC;AAC/E,OAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;GAC1D;;;QAZqB,MAAM;qBAa5B,CAAA","file":"adapters/dblite.js","sourcesContent":["'use strict';\n\nimport Adapter from '../Adapter';\nimport getArgs from 'getargs';\n\n/** @module adapters/dblite */\nmodule.exports = class dblite extends Adapter {\n\t/**\n\t * Run the sql query as a prepared statement\n\t *\n\t * @param {String} sql - The sql with placeholders\n\t * @param {Array} params - The values to insert into the query\n\t * @param {Function} callback - Callback to run when a response is recieved\n\t * @return void\n\t */\n\texecute(/*sql, params, callback*/) {\n\t\tlet args = getArgs('sql:string, [params]:array, callback:function', arguments);\n\t\tthis.instance.query(args.sql, args.params, args.callback);\n\t};\n}"],"sourceRoot":"/source/"} \ No newline at end of file diff --git a/lib/adapters/mysql.js b/lib/adapters/mysql.js index 9e934ab..f09da40 100644 --- a/lib/adapters/mysql.js +++ b/lib/adapters/mysql.js @@ -1,45 +1,20 @@ 'use strict'; -var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); +let Adapter = require('../Adapter'), + getArgs = require('getargs'); -var _Adapter2 = require('../Adapter'); - -var _Adapter3 = _interopRequireDefault(_Adapter2); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } - -function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } - -/** @module adapters/mysql */ -module.exports = (function (_Adapter) { - _inherits(mysql, _Adapter); - - function mysql() { - _classCallCheck(this, mysql); - - return _possibleConstructorReturn(this, Object.getPrototypeOf(mysql).apply(this, arguments)); +module.exports = class mysql extends Adapter { + /** + * 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} + */ + execute(sql, params, callback) { + let args = getArgs('sql:string, [params], callback:function', arguments); + return this.instance.query(args.sql, args.params, args.callback); + //this.instance.query.apply(this.instance, Array(args)); } - - _createClass(mysql, [{ - key: 'execute', - - /** - * 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 - */ - value: function execute(sql, params, callback) { - this.instance.query.apply(instance, arguments); - } - }]); - - return mysql; -})(_Adapter3.default); -//# sourceMappingURL=mysql.js.map +} \ No newline at end of file diff --git a/lib/adapters/mysql.js.map b/lib/adapters/mysql.js.map deleted file mode 100644 index 4d83827..0000000 --- a/lib/adapters/mysql.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["mysql.js"],"names":[],"mappings":"AAAA,YAAY,CAAC;;;;;;;;;;;;;;;;;AAKb,MAAM,CAAC,OAAO;WAAS,KAAK;;UAAL,KAAK;wBAAL,KAAK;;gEAAL,KAAK;;;cAAL,KAAK;;;;;;;;;;;0BASnB,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE;AAC9B,OAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;GAC/C;;;QAXqB,KAAK;qBAY3B,CAAA","file":"adapters/mysql.js","sourcesContent":["'use strict';\n\nimport Adapter from '../Adapter';\n\n/** @module adapters/mysql */\nmodule.exports = class mysql extends Adapter {\n\t/**\n\t * Run the sql query as a prepared statement\n\t *\n\t * @param {String} sql - The sql with placeholders\n\t * @param {Array} params - The values to insert into the query\n\t * @param {Function} callback - Callback to run when a response is recieved\n\t * @return void\n\t */\n\texecute(sql, params, callback) {\n\t\tthis.instance.query.apply(instance, arguments);\n\t}\n}"],"sourceRoot":"/source/"} \ No newline at end of file diff --git a/lib/adapters/mysql2.js b/lib/adapters/mysql2.js index ed73744..f559f88 100644 --- a/lib/adapters/mysql2.js +++ b/lib/adapters/mysql2.js @@ -1,45 +1,20 @@ 'use strict'; -var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); +let Adapter = require('../Adapter'), + getArgs = require('getargs'); -var _Adapter2 = require('../Adapter'); - -var _Adapter3 = _interopRequireDefault(_Adapter2); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } - -function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } - -/** @module adapters/mysql2 */ -module.exports = (function (_Adapter) { - _inherits(mysql2, _Adapter); - - function mysql2() { - _classCallCheck(this, mysql2); - - return _possibleConstructorReturn(this, Object.getPrototypeOf(mysql2).apply(this, arguments)); +module.exports = class mysql2 extends Adapter { + /** + * 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} + */ + execute(/*sql, params, callback*/) { + let args = getArgs('sql:string, [params], callback:function', arguments); + return this.instance.execute(args.sql, args.params, args.callback); + //this.instance.execute.apply(this.instance, args); } - - _createClass(mysql2, [{ - key: 'execute', - - /** - * 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 - */ - value: function execute(sql, params, callback) { - this.instance.execute.apply(this.instance, arguments); - } - }]); - - return mysql2; -})(_Adapter3.default); -//# sourceMappingURL=mysql2.js.map +} \ No newline at end of file diff --git a/lib/adapters/mysql2.js.map b/lib/adapters/mysql2.js.map deleted file mode 100644 index 7be47b9..0000000 --- a/lib/adapters/mysql2.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["mysql2.js"],"names":[],"mappings":"AAAA,YAAY,CAAC;;;;;;;;;;;;;;;;;AAKb,MAAM,CAAC,OAAO;WAAS,MAAM;;UAAN,MAAM;wBAAN,MAAM;;gEAAN,MAAM;;;cAAN,MAAM;;;;;;;;;;;0BASpB,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE;AAC9B,OAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;GACtD;;;QAXqB,MAAM;qBAY5B,CAAA","file":"adapters/mysql2.js","sourcesContent":["'use strict';\n\nimport Adapter from '../Adapter';\n\n/** @module adapters/mysql2 */\nmodule.exports = class mysql2 extends Adapter {\n\t/**\n\t * Run the sql query as a prepared statement\n\t *\n\t * @param {String} sql - The sql with placeholders\n\t * @param {Array} params - The values to insert into the query\n\t * @param {Function} callback - Callback to run when a response is recieved\n\t * @return void\n\t */\n\texecute(sql, params, callback) {\n\t\tthis.instance.execute.apply(this.instance, arguments);\n\t};\n}"],"sourceRoot":"/source/"} \ No newline at end of file diff --git a/lib/adapters/node-firebird.js b/lib/adapters/node-firebird.js index a8a2ff5..83ac9e4 100644 --- a/lib/adapters/node-firebird.js +++ b/lib/adapters/node-firebird.js @@ -1,50 +1,19 @@ 'use strict'; -var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); +let Adapter = require('../Adapter'), + getArgs = require('getargs'); -var _Adapter2 = require('../Adapter'); - -var _Adapter3 = _interopRequireDefault(_Adapter2); - -var _getargs = require('getargs'); - -var _getargs2 = _interopRequireDefault(_getargs); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } - -function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } - -/** @module adapters/node-firebird */ -module.exports = (function (_Adapter) { - _inherits(nodefirebird, _Adapter); - - function nodefirebird() { - _classCallCheck(this, nodefirebird); - - return _possibleConstructorReturn(this, Object.getPrototypeOf(nodefirebird).apply(this, arguments)); +module.exports = class nodefirebird extends Adapter { + /** + * 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} + */ + execute(/*sql, params, callback*/) { + let args = getArgs('sql:string, [params], callback:function', arguments); + return this.instance.execute(args.sql, args.params, args.callback); } - - _createClass(nodefirebird, [{ - key: 'execute', - - /** - * 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 - */ - value: function execute() /*sql, params, callback*/{ - var args = (0, _getargs2.default)('sql:string, [params], callback:function', arguments); - this.instance.execute(args.sql, args.params, args.callback); - } - }]); - - return nodefirebird; -})(_Adapter3.default); -//# sourceMappingURL=node-firebird.js.map +} \ No newline at end of file diff --git a/lib/adapters/node-firebird.js.map b/lib/adapters/node-firebird.js.map deleted file mode 100644 index b29163d..0000000 --- a/lib/adapters/node-firebird.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["node-firebird.js"],"names":[],"mappings":"AAAA,YAAY,CAAC;;;;;;;;;;;;;;;;;;;;;AAMb,MAAM,CAAC,OAAO;WAAS,YAAY;;UAAZ,YAAY;wBAAZ,YAAY;;gEAAZ,YAAY;;;cAAZ,YAAY;;;;;;;;;;;qDASC;AAClC,OAAI,IAAI,GAAG,uBAAQ,yCAAyC,EAAE,SAAS,CAAC,CAAC;AACzE,OAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;GAC5D;;;QAZqB,YAAY;qBAalC,CAAA","file":"adapters/node-firebird.js","sourcesContent":["'use strict';\n\nimport Adapter from '../Adapter';\nimport getArgs from 'getargs';\n\n/** @module adapters/node-firebird */\nmodule.exports = class nodefirebird extends Adapter {\n\t/**\n\t * Run the sql query as a prepared statement\n\t *\n\t * @param {String} sql - The sql with placeholders\n\t * @param {Array} params - The values to insert into the query\n\t * @param {Function} callback - Callback to run when a response is recieved\n\t * @return void\n\t */\n\texecute(/*sql, params, callback*/) {\n\t\tlet args = getArgs('sql:string, [params], callback:function', arguments);\n\t\tthis.instance.execute(args.sql, args.params, args.callback);\n\t}\n}"],"sourceRoot":"/source/"} \ No newline at end of file diff --git a/lib/adapters/pg.js b/lib/adapters/pg.js index 05a211e..a51e57d 100644 --- a/lib/adapters/pg.js +++ b/lib/adapters/pg.js @@ -1,58 +1,27 @@ 'use strict'; -var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); +let Adapter = require('../Adapter'), + getArgs = require('getargs'); -var _Adapter2 = require('../Adapter'); +module.exports = class pg extends Adapter { + /** + * 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} + */ + execute(/*sql, params, callback*/) { + let args = getArgs('sql:string, [params]:array, callback:function', arguments); -var _Adapter3 = _interopRequireDefault(_Adapter2); + // Replace question marks with numbered placeholders, because this adapter is different... + let count = 0; + args.sql = args.sql.replace(/\?/g, () => { + count++; + return '$' + count; + }); -var _getargs = require('getargs'); - -var _getargs2 = _interopRequireDefault(_getargs); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } - -function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } - -/** @module adapters/pg */ -module.exports = (function (_Adapter) { - _inherits(pg, _Adapter); - - function pg() { - _classCallCheck(this, pg); - - return _possibleConstructorReturn(this, Object.getPrototypeOf(pg).apply(this, arguments)); + this.instance.query(args.sql, args.params, args.callback); } - - _createClass(pg, [{ - key: 'execute', - - /** - * 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 - */ - value: function execute() /*sql, params, callback*/{ - var args = (0, _getargs2.default)('sql:string, [params]:array, callback:function', arguments); - - // Replace question marks with numbered placeholders, because this adapter is different... - var count = 0; - args.sql = args.sql.replace(/\?/g, function () { - count++; - return '$' + count; - }); - - this.instance.query(args.sql, args.params, args.callback); - } - }]); - - return pg; -})(_Adapter3.default); -//# sourceMappingURL=pg.js.map +} \ No newline at end of file diff --git a/lib/adapters/pg.js.map b/lib/adapters/pg.js.map deleted file mode 100644 index fa33d5f..0000000 --- a/lib/adapters/pg.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["pg.js"],"names":[],"mappings":"AAAA,YAAY,CAAC;;;;;;;;;;;;;;;;;;;;;AAMb,MAAM,CAAC,OAAO;WAAS,EAAE;;UAAF,EAAE;wBAAF,EAAE;;gEAAF,EAAE;;;cAAF,EAAE;;;;;;;;;;;qDASW;AAClC,OAAI,IAAI,GAAG,uBAAQ,+CAA+C,EAAE,SAAS,CAAC;;;AAAC,AAG/E,OAAI,KAAK,GAAG,CAAC,CAAC;AACd,OAAI,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,YAAM;AACxC,SAAK,EAAE,CAAC;AACR,WAAO,GAAG,GAAG,KAAK,CAAC;IACnB,CAAC,CAAC;;AAEH,OAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;GAC1D;;;QApBqB,EAAE;qBAqBxB,CAAA","file":"adapters/pg.js","sourcesContent":["'use strict';\n\nimport Adapter from '../Adapter';\nimport getArgs from 'getargs';\n\n/** @module adapters/pg */\nmodule.exports = class pg extends Adapter {\n\t/**\n\t * Run the sql query as a prepared statement\n\t *\n\t * @param {String} sql - The sql with placeholders\n\t * @param {Array} params - The values to insert into the query\n\t * @param {Function} callback - Callback to run when a response is recieved\n\t * @return void\n\t */\n\texecute(/*sql, params, callback*/) {\n\t\tlet args = getArgs('sql:string, [params]:array, callback:function', arguments);\n\n\t\t// Replace question marks with numbered placeholders, because this adapter is different...\n\t\tlet count = 0;\n\t\targs.sql = args.sql.replace(/\\?/g, () => {\n\t\t\tcount++;\n\t\t\treturn '$' + count;\n\t\t});\n\n\t\tthis.instance.query(args.sql, args.params, args.callback);\n\t}\n}"],"sourceRoot":"/source/"} \ No newline at end of file diff --git a/lib/drivers/Firebird.js b/lib/drivers/Firebird.js index 8b9143f..51ffd67 100644 --- a/lib/drivers/Firebird.js +++ b/lib/drivers/Firebird.js @@ -1,78 +1,44 @@ "use strict"; -var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); +let helpers = require('../helpers'), + Driver = require('../DriverClass'); -var _helpers = require('../helpers'); - -var _helpers2 = _interopRequireDefault(_helpers); - -var _DriverClass = require('../DriverClass'); - -var _DriverClass2 = _interopRequireDefault(_DriverClass); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } - -function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } - -/** - * Driver for Firebird databases - * - * @module drivers/firebird - */ - -var Firebird = (function (_Driver) { - _inherits(Firebird, _Driver); - - function Firebird() { - _classCallCheck(this, Firebird); - - return _possibleConstructorReturn(this, Object.getPrototypeOf(Firebird).call(this, { +class Firebird extends Driver { + constructor() { + super({ hasTruncate: false - })); + }); } /** - * Generate a limit clause for firebird, which uses the syntax closest to the SQL standard - * - * @param {String} sql - * @param {Number} limit - * @param {Number} offset - * @return {String} - */ + * 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 + */ + limit(origSql, limit, offset) { + let sql = `FIRST ${limit}`; - _createClass(Firebird, [{ - key: 'limit', - value: function limit(origSql, _limit, offset) { - var sql = 'FIRST ' + _limit; - - if (_helpers2.default.isNumber(offset)) { - sql += ' SKIP ' + offset; - } - - return origSql.replace(/SELECT/i, "SELECT " + sql); + if (helpers.isNumber(offset)) + { + sql += ` SKIP ${offset}`; } - /** - * SQL to insert a group of rows - * - * @param {String} table - The table to insert to - * @param {Array} [data] - The array of object containing data to insert - * @return {String} - */ + return origSql.replace(/SELECT/i, "SELECT " + sql); + } - }, { - key: 'insertBatch', - value: function insertBatch() { - throw new Error("Not Implemented"); - } - }]); + /** + * SQL to insert a group of rows + * + * @param {String} table - The table to insert to + * @param {Array} [data] - The array of object containing data to insert + * @return {String} - Query and data to insert + */ + insertBatch() { + throw new Error("Not Implemented"); + } +} - return Firebird; -})(_DriverClass2.default); - -module.exports = new Firebird(); -//# sourceMappingURL=Firebird.js.map +module.exports = new Firebird(); \ No newline at end of file diff --git a/lib/drivers/Firebird.js.map b/lib/drivers/Firebird.js.map deleted file mode 100644 index 51e756b..0000000 --- a/lib/drivers/Firebird.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["Firebird.js"],"names":[],"mappings":"AAAA,YAAY,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;IAUP,QAAQ;WAAR,QAAQ;;AACb,UADK,QAAQ,GACC;wBADT,QAAQ;;gEAAR,QAAQ,aAEN;AACL,cAAW,EAAE,KAAK;GAClB;EACD;;;;;;;;;;AAAA;cALI,QAAQ;;wBAeP,OAAO,EAAE,MAAK,EAAE,MAAM,EAAE;AAC7B,OAAI,GAAG,eAAa,MAAK,AAAE,CAAC;;AAE5B,OAAI,kBAAQ,QAAQ,CAAC,MAAM,CAAC,EAC5B;AACC,OAAG,gBAAc,MAAM,AAAE,CAAC;IAC1B;;AAED,UAAO,OAAO,CAAC,OAAO,CAAC,SAAS,EAAE,SAAS,GAAG,GAAG,CAAC,CAAC;GACnD;;;;;;;;;;;;gCASa;AACb,SAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;GACnC;;;QAnCI,QAAQ;;;AAsCd,MAAM,CAAC,OAAO,GAAG,IAAI,QAAQ,EAAE,CAAC","file":"drivers/Firebird.js","sourcesContent":["\"use strict\";\n\nimport helpers from '../helpers';\nimport Driver from '../DriverClass';\n\n/**\n * Driver for Firebird databases\n *\n * @module drivers/firebird\n */\nclass Firebird extends Driver {\n\tconstructor() {\n\t\tsuper({\n\t\t\thasTruncate: false\n\t\t});\n\t}\n\n\t/**\n\t * Generate a limit clause for firebird, which uses the syntax closest to the SQL standard\n\t *\n\t * @param {String} sql\n\t * @param {Number} limit\n\t * @param {Number} offset\n\t * @return {String}\n\t */\n\tlimit(origSql, limit, offset) {\n\t\tlet sql = `FIRST ${limit}`;\n\n\t\tif (helpers.isNumber(offset))\n\t\t{\n\t\t\tsql += ` SKIP ${offset}`;\n\t\t}\n\n\t\treturn origSql.replace(/SELECT/i, \"SELECT \" + sql);\n\t}\n\n\t/**\n\t * SQL to insert a group of rows\n\t *\n\t * @param {String} table - The table to insert to\n\t * @param {Array} [data] - The array of object containing data to insert\n\t * @return {String}\n\t */\n\tinsertBatch() {\n\t\tthrow new Error(\"Not Implemented\");\n\t}\n}\n\nmodule.exports = new Firebird();"],"sourceRoot":"/source/"} \ No newline at end of file diff --git a/lib/drivers/Mysql.js b/lib/drivers/Mysql.js index a52450e..a1f9ea7 100755 --- a/lib/drivers/Mysql.js +++ b/lib/drivers/Mysql.js @@ -1,54 +1,32 @@ "use strict"; -var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); +let helpers = require('../helpers'), + Driver = require('../DriverClass'); -var _helpers = require('../helpers'); - -var _helpers2 = _interopRequireDefault(_helpers); - -var _DriverClass = require('../DriverClass'); - -var _DriverClass2 = _interopRequireDefault(_DriverClass); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } - -function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } - -/** - * Driver for MySQL databases - * - * @module drivers/mysql - */ - -var Mysql = (function (_Driver) { - _inherits(Mysql, _Driver); - - function Mysql() { - _classCallCheck(this, Mysql); - - return _possibleConstructorReturn(this, Object.getPrototypeOf(Mysql).call(this, { +class Mysql extends Driver { + constructor() { + super({ identifierStartChar: '`', identifierEndChar: '`' - })); + }); } - _createClass(Mysql, [{ - key: 'limit', - value: function limit(sql, _limit, offset) { - if (!_helpers2.default.isNumber(offset)) { - return sql += ' LIMIT ' + _limit; - } - - return sql += ' LIMIT ' + offset + ', ' + _limit; + /** + * 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 + */ + limit(sql, limit, offset) { + if ( ! helpers.isNumber(offset)) + { + return sql += ` LIMIT ${limit}`; } - }]); - return Mysql; -})(_DriverClass2.default); + return sql += ` LIMIT ${offset}, ${limit}`; + } +} -module.exports = new Mysql(); -//# sourceMappingURL=Mysql.js.map +module.exports = new Mysql(); \ No newline at end of file diff --git a/lib/drivers/Mysql.js.map b/lib/drivers/Mysql.js.map deleted file mode 100644 index 0db23ec..0000000 --- a/lib/drivers/Mysql.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["Mysql.js"],"names":[],"mappings":"AAAA,YAAY,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;IAUP,KAAK;WAAL,KAAK;;AACV,UADK,KAAK,GACI;wBADT,KAAK;;gEAAL,KAAK,aAEH;AACL,sBAAmB,EAAE,GAAG;AACxB,oBAAiB,EAAE,GAAG;GACtB;EACD;;cANI,KAAK;;wBAQJ,GAAG,EAAE,MAAK,EAAE,MAAM,EAAE;AACzB,OAAK,CAAE,kBAAQ,QAAQ,CAAC,MAAM,CAAC,EAC/B;AACC,WAAO,GAAG,gBAAc,MAAK,AAAE,CAAC;IAChC;;AAED,UAAO,GAAG,gBAAc,MAAM,UAAK,MAAK,AAAE,CAAC;GAC3C;;;QAfI,KAAK;;;AAkBX,MAAM,CAAC,OAAO,GAAG,IAAI,KAAK,EAAE,CAAC","file":"drivers/Mysql.js","sourcesContent":["\"use strict\";\n\nimport helpers from '../helpers';\nimport Driver from '../DriverClass';\n\n/**\n * Driver for MySQL databases\n *\n * @module drivers/mysql\n */\nclass Mysql extends Driver {\n\tconstructor() {\n\t\tsuper({\n\t\t\tidentifierStartChar: '`',\n\t\t\tidentifierEndChar: '`'\n\t\t});\n\t}\n\n\tlimit(sql, limit, offset) {\n\t\tif ( ! helpers.isNumber(offset))\n\t\t{\n\t\t\treturn sql += ` LIMIT ${limit}`;\n\t\t}\n\n\t\treturn sql += ` LIMIT ${offset}, ${limit}`;\n\t}\n}\n\nmodule.exports = new Mysql();"],"sourceRoot":"/source/"} \ No newline at end of file diff --git a/lib/drivers/Pg.js b/lib/drivers/Pg.js index 2de962c..89a6a48 100755 --- a/lib/drivers/Pg.js +++ b/lib/drivers/Pg.js @@ -1,15 +1,10 @@ "use strict"; -var _DriverClass = require("../DriverClass"); - -var _DriverClass2 = _interopRequireDefault(_DriverClass); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } +let Driver = require('../DriverClass'); /** * Driver for PostgreSQL databases * * @module drivers/pg */ -module.exports = new _DriverClass2.default(); -//# sourceMappingURL=Pg.js.map +module.exports = new Driver(); \ No newline at end of file diff --git a/lib/drivers/Pg.js.map b/lib/drivers/Pg.js.map deleted file mode 100644 index 4537eb6..0000000 --- a/lib/drivers/Pg.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["Pg.js"],"names":[],"mappings":"AAAA,YAAY,CAAC;;;;;;;;;;;;;AASb,MAAM,CAAC,OAAO,GAAG,2BAAY,CAAC","file":"drivers/Pg.js","sourcesContent":["\"use strict\";\n\nimport Driver from '../DriverClass';\n\n/**\n * Driver for PostgreSQL databases\n *\n * @module drivers/pg\n */\nmodule.exports = new Driver();"],"sourceRoot":"/source/"} \ No newline at end of file diff --git a/lib/drivers/Sqlite.js b/lib/drivers/Sqlite.js index c748268..30f0d7b 100644 --- a/lib/drivers/Sqlite.js +++ b/lib/drivers/Sqlite.js @@ -1,94 +1,66 @@ "use strict"; -var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); +let helpers = require('../helpers'), + Driver = require('../DriverClass'); -var _helpers = require('../helpers'); - -var _helpers2 = _interopRequireDefault(_helpers); - -var _DriverClass = require('../DriverClass'); - -var _DriverClass2 = _interopRequireDefault(_DriverClass); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } - -function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } - -/** - * Driver for Sqlite databases - * - * @module drivers/sqlite - */ - -var Sqlite = (function (_Driver) { - _inherits(Sqlite, _Driver); - - function Sqlite() { - _classCallCheck(this, Sqlite); - - var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(Sqlite).call(this)); - - _this.hasTruncate = false; - return _this; +class Sqlite extends Driver { + constructor() { + super({ + hasTruncate: false + }); } - _createClass(Sqlite, [{ - key: 'insertBatch', - value: function insertBatch(table, data) { - var _this2 = this; + /** + * SQL to insert a group of rows + * + * @param {String} table - The table to insert to + * @param {Array} [data] - The array of object containing data to insert + * @return {String} - Query and data to insert + */ + insertBatch(table, data) { + // Get the data values to insert, so they can + // be parameterized + let sql = "", + vals = [], + cols = [], + fields = [], + first = data.shift(), + params = [], + paramString = "", + paramList = []; - // Get the data values to insert, so they can - // be parameterized - var sql = "", - vals = [], - cols = [], - fields = [], - first = data.shift(), - params = [], - paramString = "", - paramList = []; - data.forEach(function (obj) { - var row = []; - Object.keys(obj).forEach(function (key) { - row.push(obj[key]); - }); - vals.push(row); + data.forEach(obj => { + let row = []; + Object.keys(obj).forEach(key => { + row.push(obj[key]); }); + vals.push(row); + }); - sql += "INSERT INTO " + this.quoteTable(table) + "\n"; + sql += "INSERT INTO " + this.quoteTable(table) + "\n"; - // Get the field names from the keys of the first - // object to be inserted - fields = Object.keys(first); - Object.keys(first).forEach(function (key) { - cols.push("'" + _this2._quote(first[key]) + "' AS " + _this2.quoteIdentifiers(key)); + // Get the field names from the keys of the first + // object to be inserted + fields = Object.keys(first); + Object.keys(first).forEach(key => { + cols.push("'" + this._quote(first[key]) + "' AS " + this.quoteIdentifiers(key)); + }); + + sql += "SELECT " + cols.join(', ') + "\n"; + + vals.forEach(row_values => { + let quoted = row_values.map(value => { + return String(value).replace("'", "'\'"); }); + sql += "UNION ALL SELECT '" + quoted.join("', '") + "'\n"; + }); - sql += "SELECT " + cols.join(', ') + "\n"; + return { + sql: sql, + values: null + }; + } +} - vals.forEach(function (row_values) { - var quoted = row_values.map(function (value) { - return String(value).replace("'", "'\'"); - }); - sql += "UNION ALL SELECT '" + quoted.join("', '") + "'\n"; - }); - - return { - sql: sql, - values: null - }; - } - }]); - - return Sqlite; -})(_DriverClass2.default); - -; - -module.exports = new Sqlite(); -//# sourceMappingURL=Sqlite.js.map +module.exports = new Sqlite(); \ No newline at end of file diff --git a/lib/drivers/Sqlite.js.map b/lib/drivers/Sqlite.js.map deleted file mode 100644 index 3306c4d..0000000 --- a/lib/drivers/Sqlite.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["Sqlite.js"],"names":[],"mappings":"AAAA,YAAY,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;IAUP,MAAM;WAAN,MAAM;;AACX,UADK,MAAM,GACG;wBADT,MAAM;;qEAAN,MAAM;;AAGV,QAAK,WAAW,GAAG,KAAK,CAAC;;EACzB;;cAJI,MAAM;;8BAMC,KAAK,EAAE,IAAI,EAAE;;;;;AAIxB,OAAI,GAAG,GAAG,EAAE;OACX,IAAI,GAAG,EAAE;OACT,IAAI,GAAG,EAAE;OACT,MAAM,GAAG,EAAE;OACX,KAAK,GAAG,IAAI,CAAC,KAAK,EAAE;OACpB,MAAM,GAAG,EAAE;OACX,WAAW,GAAG,EAAE;OAChB,SAAS,GAAG,EAAE,CAAC;;AAGhB,OAAI,CAAC,OAAO,CAAC,UAAC,GAAG,EAAK;AACrB,QAAI,GAAG,GAAG,EAAE,CAAC;AACb,UAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,UAAC,GAAG,EAAK;AACjC,QAAG,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;KACnB,CAAC,CAAC;AACH,QAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACf,CAAC,CAAC;;AAEH,MAAG,IAAI,cAAc,GAAG,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,GAAG,IAAI;;;;AAAC,AAItD,SAAM,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAC5B,SAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,UAAC,GAAG,EAAK;AACnC,QAAI,CAAC,IAAI,CAAC,GAAG,GAAG,OAAK,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,OAAO,GAAG,OAAK,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC;IAChF,CAAC,CAAC;;AAEH,MAAG,IAAI,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;;AAE1C,OAAI,CAAC,OAAO,CAAC,UAAC,UAAU,EAAK;AAC5B,QAAI,MAAM,GAAG,UAAU,CAAC,GAAG,CAAC,UAAC,KAAK,EAAK;AACtC,YAAO,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;KACzC,CAAC,CAAC;AACH,OAAG,IAAI,oBAAoB,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,KAAK,CAAC;IAC1D,CAAC,CAAC;;AAEH,UAAO;AACN,OAAG,EAAE,GAAG;AACR,UAAM,EAAE,IAAI;IACZ,CAAC;GACF;;;QAlDI,MAAM;;;AAmDX,CAAC;;AAEF,MAAM,CAAC,OAAO,GAAG,IAAI,MAAM,EAAE,CAAC","file":"drivers/Sqlite.js","sourcesContent":["\"use strict\";\n\nimport helpers from '../helpers';\nimport Driver from '../DriverClass';\n\n/**\n * Driver for Sqlite databases\n *\n * @module drivers/sqlite\n */\nclass Sqlite extends Driver {\n\tconstructor() {\n\t\tsuper();\n\t\tthis.hasTruncate = false;\n\t}\n\n\tinsertBatch(table, data) {\n\n\t\t// Get the data values to insert, so they can\n\t\t// be parameterized\n\t\tlet sql = \"\",\n\t\t\tvals = [],\n\t\t\tcols = [],\n\t\t\tfields = [],\n\t\t\tfirst = data.shift(),\n\t\t\tparams = [],\n\t\t\tparamString = \"\",\n\t\t\tparamList = [];\n\n\n\t\tdata.forEach((obj) => {\n\t\t\tlet row = [];\n\t\t\tObject.keys(obj).forEach((key) => {\n\t\t\t\trow.push(obj[key]);\n\t\t\t});\n\t\t\tvals.push(row);\n\t\t});\n\n\t\tsql += \"INSERT INTO \" + this.quoteTable(table) + \"\\n\";\n\n\t\t// Get the field names from the keys of the first\n\t\t// object to be inserted\n\t\tfields = Object.keys(first);\n\t\tObject.keys(first).forEach((key) => {\n\t\t\tcols.push(\"'\" + this._quote(first[key]) + \"' AS \" + this.quoteIdentifiers(key));\n\t\t});\n\n\t\tsql += \"SELECT \" + cols.join(', ') + \"\\n\";\n\n\t\tvals.forEach((row_values) => {\n\t\t\tlet quoted = row_values.map((value) => {\n\t\t\t\treturn String(value).replace(\"'\", \"'\\'\");\n\t\t\t});\n\t\t\tsql += \"UNION ALL SELECT '\" + quoted.join(\"', '\") + \"'\\n\";\n\t\t});\n\n\t\treturn {\n\t\t\tsql: sql,\n\t\t\tvalues: null\n\t\t};\n\t}\n};\n\nmodule.exports = new Sqlite();"],"sourceRoot":"/source/"} \ No newline at end of file diff --git a/lib/helpers.js b/lib/helpers.js index d5aefd3..259814b 100755 --- a/lib/helpers.js +++ b/lib/helpers.js @@ -1,66 +1,62 @@ -"use strict" +"use strict"; -//require('es6-shim'); - -; -var helpers = { +let helpers = { /** - * Wrap String.prototype.trim in a way that is easily mappable - * - * @param {String} str - The string to trim - * @return {String} - The trimmed string - */ - stringTrim: function stringTrim(str) { - return str.trim(); + * Wrap String.prototype.trim in a way that is easily mappable + * + * @param {String} str - The string to trim + * @return {String} - The trimmed string + */ + stringTrim: str => str.trim(), + /** + * Get the type of the variable passed + * + * @see https://techblog.badoo.com/blog/2013/11/01/type-checking-in-javascript/ + * @see http://toddmotto.com/understanding-javascript-types-and-reliable-type-checking/ + * @param {mixed} o - Object to type check + * @return {String} - Type of the object + */ + type: o => { + let type = Object.prototype.toString.call(o).slice(8, -1).toLowerCase(); + + // handle NaN and Infinity + if (type === 'number') { + if (isNaN(o)) { + return 'nan'; + } + if (!isFinite(o)) { + return 'infinity'; + } + } + + return type; }, /** - * Get the type of the variable passed - * - * @see https://techblog.badoo.com/blog/2013/11/01/type-checking-in-javascript/ - * @see http://toddmotto.com/understanding-javascript-types-and-reliable-type-checking/ - * @param {mixed} o - * @return {String} - */ - type: function type(o) { - var type = Object.prototype.toString.call(o).slice(8, -1).toLowerCase(); - - // handle NaN and Infinity - if (type === 'number') { - if (isNaN(o)) { - return 'nan'; - } - if (!isFinite(o)) { - return 'infinity'; - } - } - - return type; - }, - /** - * Determine whether an object is scalar - * - * @param {mixed} obj - * @return {bool} - */ - isScalar: function isScalar(obj) { - var scalar = ['string', 'number', 'boolean']; + * Determine whether an object is scalar + * + * @param {mixed} obj - Object to test + * @return {bool} - Is object scalar + */ + isScalar: obj => { + let scalar = ['string', 'number', 'boolean']; return scalar.indexOf(helpers.type(obj)) !== -1; }, /** - * Get a list of values with a common key from an array of objects - * - * @param {Array} arr - The array of objects to search - * @param {String} key - The key of the object to get - * @return {Array} - */ - arrayPluck: function arrayPluck(arr, key) { - var output = []; + * Get a list of values with a common key from an array of objects + * + * @param {Array} arr - The array of objects to search + * @param {String} key - The key of the object to get + * @return {Array} - The new array of plucked values + */ + arrayPluck: (arr, key) => { + let output = []; // Empty case if (arr.length === 0) return output; - arr.forEach(function (obj) { - if (!helpers.isUndefined(obj[key])) { + arr.forEach(obj => { + if ( ! helpers.isUndefined(obj[key])) + { output.push(obj[key]); } }); @@ -68,22 +64,22 @@ var helpers = { return output; }, /** - * Determine if a value matching the passed regular expression is - * in the passed array - * - * @param {Array} arr - The array to search - * @param {RegExp} pattern - The pattern to match - * @return {Boolean} - If an array item matches the pattern - */ - regexInArray: function regexInArray(arr, pattern) { + * Determine if a value matching the passed regular expression is + * in the passed array + * + * @param {Array} arr - The array to search + * @param {RegExp} pattern - The pattern to match + * @return {Boolean} - If an array item matches the pattern + */ + regexInArray: (arr, pattern) => { // Empty case(s) - if (!helpers.isArray(arr)) return false; + if ( ! helpers.isArray(arr)) return false; if (arr.length === 0) return false; - var i = undefined, - l = arr.length; + let i, l = arr.length; - for (i = 0; i < l; i++) { + for(i=0; i< l; i++) + { // Short circuit if any items match if (pattern.test(arr[i])) return true; } @@ -91,39 +87,39 @@ var helpers = { return false; }, /** - * Make the first letter of the string uppercase - * - * @param {String} str - * @return {String} - */ - upperCaseFirst: function upperCaseFirst(str) { + * Make the first letter of the string uppercase + * + * @param {String} str - The string to modify + * @return {String} - The modified string + */ + upperCaseFirst: str => { str += ''; - var first = str.charAt(0).toUpperCase(); + let first = str.charAt(0).toUpperCase(); return first + str.substr(1); } }; // Define an 'is' method for each type -var types = ['Null', 'Undefined', 'Object', 'Array', 'String', 'Number', 'Boolean', 'Function', 'RegExp', 'NaN', 'Infinite']; -types.forEach(function (t) { +let types = ['Null','Undefined','Object','Array','String','Number','Boolean','Function','RegExp','NaN','Infinite']; +types.forEach(t => { /** - * Determine whether a variable is of the type specified in the - * function name, eg isNumber - * - * Types available are Null, Undefined, Object, Array, String, Number, Boolean, Function, RegExp, NaN and Infinite - * - * @name is[type] - * @param {mixed} o - * @return {Boolean} - */ - helpers['is' + t] = function (o) { - if (t.toLowerCase() === 'infinite') { - t = 'infinity'; - } + * Determine whether a variable is of the type specified in the + * function name, eg isNumber + * + * Types available are Null, Undefined, Object, Array, String, Number, Boolean, Function, RegExp, NaN and Infinite + * + * @name is[type] + * @param {mixed} o + * @return {Boolean} + */ + 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; -//# sourceMappingURL=helpers.js.map +module.exports = helpers; \ No newline at end of file diff --git a/lib/helpers.js.map b/lib/helpers.js.map deleted file mode 100644 index 567c6aa..0000000 --- a/lib/helpers.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["helpers.js"],"names":[],"mappings":"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","file":"helpers.js","sourcesContent":["\"use strict\";\n\n//require('es6-shim');\n\nlet helpers = {\n\t/**\n\t * Wrap String.prototype.trim in a way that is easily mappable\n\t *\n\t * @param {String} str - The string to trim\n\t * @return {String} - The trimmed string\n\t */\n\tstringTrim: (str) => str.trim(),\n\t/**\n\t * Get the type of the variable passed\n\t *\n\t * @see https://techblog.badoo.com/blog/2013/11/01/type-checking-in-javascript/\n\t * @see http://toddmotto.com/understanding-javascript-types-and-reliable-type-checking/\n\t * @param {mixed} o\n\t * @return {String}\n\t */\n\ttype: (o) => {\n\t let type = Object.prototype.toString.call(o).slice(8, -1).toLowerCase();\n\n\t // handle NaN and Infinity\n\t if (type === 'number') {\n\t if (isNaN(o)) {\n\t return 'nan';\n\t }\n\t if (!isFinite(o)) {\n\t return 'infinity';\n\t }\n\t }\n\n\t return type;\n\t},\n\t/**\n\t * Determine whether an object is scalar\n\t *\n\t * @param {mixed} obj\n\t * @return {bool}\n\t */\n\tisScalar: (obj) => {\n\t\tlet scalar = ['string', 'number', 'boolean'];\n\t\treturn scalar.indexOf(helpers.type(obj)) !== -1;\n\t},\n\t/**\n\t * Get a list of values with a common key from an array of objects\n\t *\n\t * @param {Array} arr - The array of objects to search\n\t * @param {String} key - The key of the object to get\n\t * @return {Array}\n\t */\n\tarrayPluck: (arr, key) => {\n\t\tlet output = [];\n\n\t\t// Empty case\n\t\tif (arr.length === 0) return output;\n\n\t\tarr.forEach((obj) => {\n\t\t\tif ( ! helpers.isUndefined(obj[key]))\n\t\t\t{\n\t\t\t\toutput.push(obj[key]);\n\t\t\t}\n\t\t});\n\n\t\treturn output;\n\t},\n\t/**\n\t * Determine if a value matching the passed regular expression is\n\t * in the passed array\n\t *\n\t * @param {Array} arr - The array to search\n\t * @param {RegExp} pattern - The pattern to match\n\t * @return {Boolean} - If an array item matches the pattern\n\t */\n\tregexInArray: (arr, pattern) => {\n\t\t// Empty case(s)\n\t\tif ( ! helpers.isArray(arr)) return false;\n\t\tif (arr.length === 0) return false;\n\n\t\tlet i, l = arr.length;\n\n\t\tfor(i=0; i< l; i++)\n\t\t{\n\t\t\t// Short circuit if any items match\n\t\t\tif (pattern.test(arr[i])) return true;\n\t\t}\n\n\t\treturn false;\n\t},\n\t/**\n\t * Make the first letter of the string uppercase\n\t *\n\t * @param {String} str\n\t * @return {String}\n\t */\n\tupperCaseFirst: (str) => {\n\t\tstr += '';\n\t\tlet first = str.charAt(0).toUpperCase();\n\t\treturn first + str.substr(1);\n\t}\n};\n\n// Define an 'is' method for each type\nlet types = ['Null','Undefined','Object','Array','String','Number','Boolean','Function','RegExp','NaN','Infinite'];\ntypes.forEach((t) => {\n\t/**\n\t * Determine whether a variable is of the type specified in the\n\t * function name, eg isNumber\n\t *\n\t * Types available are Null, Undefined, Object, Array, String, Number, Boolean, Function, RegExp, NaN and Infinite\n\t *\n\t * @name is[type]\n\t * @param {mixed} o\n\t * @return {Boolean}\n\t */\n helpers['is' + t] = function (o) {\n\t if (t.toLowerCase() === 'infinite')\n\t {\n\t\t t = 'infinity';\n\t }\n\n return helpers.type(o) === t.toLowerCase();\n };\n});\n\nmodule.exports = helpers;"],"sourceRoot":"/source/"} \ No newline at end of file diff --git a/package.json b/package.json index 715dfda..d78025c 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "A query builder for node based on the one in CodeIgniter", "author": "Timothy J Warren ", "engines": { - "node": ">=0.10.0" + "node": ">=4.0.0" }, "contributors": [ { @@ -38,10 +38,7 @@ "mysql2": "^0.15.8", "node-firebird": "^0.7.0", "pg": "^4.4.3", - "es6-shim": "", - "babel": "", - "babel-preset-es2015": "", - "babel-plugin-transform-es2015-modules-commonjs": "" + "require-reload": "*" }, "optionalDependencies": { "dblite": "*", @@ -49,22 +46,21 @@ "pg": "*" }, "devDependencies": { + "chai": "", "documentation": "", - "nodeunit": "", - "gulp": "", - "gulp-babel": "", - "gulp-babel-istanbul": "", - "gulp-sourcemaps": "", - "gulp-concat": "", - "gulp-documentation": "", - "gulp-nodeunit-runner": "", - "gulp-sloc": "", - "gulp-eslint": "", "eslint": "", - "istanbul": "" + "gulp": "", + "gulp-documentation": "^2.1.0", + "gulp-eslint": "", + "gulp-istanbul": "^0.10.3", + "gulp-mocha": "^2.2.0", + "gulp-pipe": "^1.0.4", + "gulp-sloc": "", + "istanbul": "", + "mocha": "" }, "license": "MIT", "scripts": { - "test": "gulp nodeunit" + "test": "gulp test" } } diff --git a/sonar-project.properties b/sonar-project.properties index 6c7cb44..b7c3b7c 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -1,5 +1,5 @@ sonar.projectKey=node-query sonar.projectName=NodeJS Query Builder -sonar.projectVersion=2.2.1 +sonar.projectVersion=3.0.0 sonar.sources=lib sonar.javascript.lcov.reportPath=coverage/lcov.info \ No newline at end of file diff --git a/src/Adapter.js b/src/Adapter.js deleted file mode 100755 index bb1627d..0000000 --- a/src/Adapter.js +++ /dev/null @@ -1,26 +0,0 @@ -'use strict'; - -/** @module Adapter */ -module.exports = class Adapter { - /** - * Invoke an adapter - * - * @param {Object} instance - The connection objec - * @return {void} - */ - constructor(instance) { - this.instance = 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 - * @param {Function} callback - Callback to run when a response is recieved - * @return {void} - */ - execute(/*sql, params, callback*/) { - throw new Error("Correct adapter not defined for query execution"); - } -} \ No newline at end of file diff --git a/src/DriverBase.js b/src/DriverBase.js deleted file mode 100755 index c4a257e..0000000 --- a/src/DriverBase.js +++ /dev/null @@ -1,164 +0,0 @@ -'use strict'; - -import helpers from './helpers' - -/** - * Base Database Driver - * - * @module driver - */ -var d = { - identifierStartChar: '"', - identifierEndChar: '"', - tablePrefix: null, - hasTruncate: true, - - /** - * Low level function for naive quoting of strings - - * @param {String} str - * @return {String} - * @private - */ - _quote: function(str) { - return (helpers.isString(str) && ! (str.startsWith(d.identifierStartChar) || str.endsWith(d.identifierEndChar))) - ? d.identifierStartChar + str + d.identifierEndChar - : str; - }, - - /** - * Set the limit clause - - * @param {String} sql - * @param {Number} limit - * @param {Number|null} offset - * @return {String} - */ - limit: function(sql, limit, offset) { - sql += " LIMIT " + limit; - - if (helpers.isNumber(offset)) - { - sql += " OFFSET " + offset; - } - - return sql; - }, - - /** - * Quote database table name, and set prefix - * - * @param {String} table - * @return {String} - */ - quoteTable: function(table) { - // Quote after prefix - return d.quoteIdentifiers(table); - }, - - /** - * Use the driver's escape character to quote identifiers - * - * @param {String|Array} - * @return {String|Array} - */ - quoteIdentifiers: function(str) { - var hiers, raw; - var pattern = new RegExp(d.identifierStartChar + '(' + '([a-zA-Z0-9_]+)' + '(\((.*?)\))' + ')' + d.identifierEndChar, 'ig'); - - // Recurse for arrays of identifiiers - if (Array.isArray(str)) - { - return str.map(d.quoteIdentifiers); - } - - // Handle commas - if (str.includes(',')) - { - var parts = str.split(',').map(helpers.stringTrim); - str = parts.map(d.quoteIdentifiers).join(','); - } - - // Split identifiers by period - hiers = str.split('.').map(d._quote); - raw = hiers.join('.'); - - // Fix functions - if (raw.includes('(') && raw.includes(')')) - { - var funcs = pattern.exec(raw); - - // Unquote the function - raw = raw.replace(funcs[0], funcs[1]); - - // Quote the identifiers inside of the parens - var inParens = funcs[3].substring(1, funcs[3].length -1); - raw = raw.replace(inParens, d.quoteIdentifiers(inParens)); - } - - return raw; - }, - - /** - * SQL to truncate the passed table - * - * @param {String} table - * @return {String} - sql - */ - truncate: function(table) { - var sql = (d.hasTruncate) - ? 'TRUNCATE ' - : 'DELETE FROM '; - - sql += d.quoteTable(table); - - return sql; - }, - - /** - * SQL to insert a group of rows - * - * @param {String} table - The table to insert to - * @param {Array} [data] - The array of object containing data to insert - * @return {String} - */ - insertBatch: function(table, data) { - var vals = [], - fields = Object.keys(data[0]), - sql = "", - params = [], - paramString = "", - paramList = []; - - // Get the data values to insert, so they can - // be parameterized - data.forEach(function(obj) { - Object.keys(obj).forEach(function(key) { - vals.push(obj[key]); - }); - }); - - // Get the field names from the keys of the first - // object inserted - table = d.quoteTable(table); - - sql += "INSERT INTO " + table + " (" - + d.quoteIdentifiers(fields).join(",") - + ") VALUES "; - - // Create placeholder groups - params = Array(fields.length).fill('?'); - paramString = "(" + params.join(',') + ")"; - paramList = Array(data.length).fill(paramString); - - sql += paramList.join(','); - - return { - sql: sql, - values: vals - }; - } - -}; - -module.exports = d; \ No newline at end of file diff --git a/src/DriverClass.js b/src/DriverClass.js deleted file mode 100644 index bc80b23..0000000 --- a/src/DriverClass.js +++ /dev/null @@ -1,13 +0,0 @@ -'use strict'; - -import driverBase from './DriverBase'; - -module.exports = class DriverClass { - constructor(properties = {}) { - Object.keys(driverBase).forEach((key) => { - this[key] = (Object.keys(properties).indexOf(key) !== -1) - ? properties[key] - : driverBase[key]; - }); - } -} \ No newline at end of file diff --git a/src/NodeQuery.js b/src/NodeQuery.js deleted file mode 100755 index fc8abe3..0000000 --- a/src/NodeQuery.js +++ /dev/null @@ -1,74 +0,0 @@ -"use strict"; - -let instance = null; -import fs from 'fs'; -import helpers from './helpers'; -import QueryBuilder from './QueryBuilder'; - -/** - * @module NodeQuery - */ -class NodeQuery { - - /** - * Constructor - */ - constructor() { - this.instance = null; - } - - /** - * Create a query builder object - * - * @memberOf NodeQuery - * @param {String} drivername - The name of the database type, eg. mysql or pg - * @param {Object} connObject - A connection object from the database library you are connecting with - * @param {String} [connLib] - The name of the db connection library you are using, eg. mysql or mysql2. Optional if the same as drivername - * @return {QueryBuilder} - */ - init(driverType, connObject, connLib) { - connLib = connLib || driverType; - - let paths = { - driver: `${__dirname}/drivers/` + helpers.upperCaseFirst(driverType), - adapter: `${__dirname}/adapters/${connLib}` - }; - - /*Object.keys(paths).forEach((type) => { - if ( ! fs.existsSync(paths[type])) - { - console.log(paths[type]); - throw new Error( - `Selected ${type} (` + - helpers.upperCaseFirst(driverType) + - `) does not exist!` - ); - } - });*/ - - let driver = require(paths.driver); - let $adapter = require(paths.adapter); - let adapter = new $adapter(connObject); - - this.instance = new QueryBuilder(driver, adapter); - - return this.instance; - }; - - /** - * Return an existing query builder instance - * - * @memberOf NodeQuery - * @return {QueryBuilder} - */ - getQuery() { - if ( ! this.instance) { - throw new Error("No Query Builder instance to return"); - } - - return this.instance; - }; - -}; - -module.exports = new NodeQuery(); \ No newline at end of file diff --git a/src/QueryBuilder.js b/src/QueryBuilder.js deleted file mode 100755 index 7d4d120..0000000 --- a/src/QueryBuilder.js +++ /dev/null @@ -1,924 +0,0 @@ -'use strict'; - -/** @module QueryBuilder */ -import getArgs from 'getargs'; -import helpers from './helpers'; -import State from './State'; -import QueryParser from './QueryParser'; - -module.exports = class QueryBuilder { - /* - * SQL generation object - * - * @param {driver} - The syntax driver for the database - * @param {adapter} - The database module adapter for running queries - * @returns {QueryBuilder} - * @constructor - */ - 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 - * - * @param {String} type - * @param {String} table - * @private - * @return {String} - */ - _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 ("; - sql += 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 - * - * @param {String} conjunction - * @param {String} string - * @param {String} type - * @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 - */ - _mixedSet(/* $letName, $valType, $key, [$val] */) { - let args = getArgs('$letName:string, $valType:string, $key:object|string|number, [$val]', arguments); - - let obj = {}; - - - if (helpers.isScalar(args.$key) && !helpers.isUndefined(args.$val)) - { - // Convert key/val pair to a simple object - obj[args.$key] = args.$val; - } - else if (helpers.isScalar(args.$key) && helpers.isUndefined(args.$val)) - { - // If just a string for the key, and no value, create a simple object with duplicate key/val - obj[args.$key] = args.$key; - } - else - { - obj = args.$key; - } - - Object.keys(obj).forEach((k) => { - // If a single value for the return - if (['key','value'].indexOf(args.$valType) !== -1) - { - let pushVal = (args.$valType === 'key') ? k : obj[k]; - 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 = new 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); - } - -//console.log(this.state); -//console.log(sql); -//console.log(vals); -//console.log(callback); -//console.log('------------------------'); - - // Reset the state so another query can be built - this._resetState(); - - // Pass the sql and values to the adapter to run on the database - this.adapter.execute(sql, vals, callback); - - } - - _getCompile(type, table, reset) { - reset = reset || false; - - let sql = this._compile(type, table); - - if (reset) this._resetState(); - - return sql; - } - - _resetState() { - this.state = new State(); - } - - // ---------------------------------------------------------------------------- - // ! Miscellaneous Methods - // ---------------------------------------------------------------------------- - - /** - * Reset the object state for a new query - * - * @memberOf QueryBuilder - * @return {void} - */ - resetQuery() { - this._resetState(); - } - - /** - * Returns the current class state for testing or other purposes - * - * @private - * @return {Object} - */ - getState() { - return this.state; - } - - /** - * Closes the database connection for the current adapter - * - * @return {void} - */ - end() { - this.adapter.close(); - } - - // ------------------------------------------------------------------------ - // ! Query Builder Methods - // ------------------------------------------------------------------------ - - /** - * Specify rows to select in the query - * - * @memberOf QueryBuilder - * @param {String|Array} fields - The fields to select from the current table - * @return {QueryBuilder} - */ - select(fields) { - - // Split/trim fields by comma - fields = (Array.isArray(fields)) - ? fields - : fields.split(",").map(helpers.stringTrim); - - // Split on 'As' - fields.forEach((field, index) => { - if (field.match(/as/i)) - { - fields[index] = field.split(/ as /i).map(helpers.stringTrim); - } - }); - - let safeArray = this.driver.quoteIdentifiers(fields); - - // Join the strings back together - safeArray.forEach((field, index) => { - if (Array.isArray(field)) - { - safeArray[index] = safeArray[index].join(' AS '); - } - }); - - this.state.selectString += safeArray.join(', '); - - return this; - } - - /** - * Specify the database table to select from - * - * @param {String} tableName - The table to use for the current query - * @return {QueryBuilder} - */ - from(tableName) { - // Split identifiers on spaces - let identArray = tableName.trim().split(' ').map(helpers.stringTrim); - - // Quote/prefix identifiers - identArray[0] = this.driver.quoteTable(identArray[0]); - identArray = this.driver.quoteIdentifiers(identArray); - - // Put it back together - this.state.fromString = identArray.join(' '); - - return this; - } - - /** - * Add a 'like/ and like' clause to the query - * - * @param {String} field - The name of the field to compare to - * @param {String} val - The value to compare to - * @param {String} [pos=both] - The placement of the wildcard character(s): before, after, or both - * @return {QueryBuilder} - */ - like(field, val, pos) { - this._like(field, val, pos, ' LIKE ', 'AND'); - return this; - } - - /** - * Add a 'not like/ and not like' clause to the query - * - * @param {String} field - The name of the field to compare to - * @param {String} val - The value to compare to - * @param {String} [pos=both] - The placement of the wildcard character(s): before, after, or both - * @return {QueryBuilder} - */ - notLike(field, val, pos) { - this._like(field, val, pos, ' NOT LIKE ', 'AND'); - return this; - } - - /** - * Add an 'or like' clause to the query - * - * @param {String} field - The name of the field to compare to - * @param {String} val - The value to compare to - * @param {String} [pos=both] - The placement of the wildcard character(s): before, after, or both - * @return {QueryBuilder} - */ - orLike(field, val, pos) { - this._like(field, val, pos, ' LIKE ', 'OR'); - return this; - } - - /** - * Add an 'or not like' clause to the query - * - * @param {String} field - The name of the field to compare to - * @param {String} val - The value to compare to - * @param {String} [pos=both] - The placement of the wildcard character(s): before, after, or both - * @return {QueryBuilder} - */ - orNotLike(field, val, pos) { - this._like(field, val, pos, ' NOT LIKE ', 'OR'); - return this; - } - - /** - * Add a 'having' clause - * - * @param {String|Object} key - The name of the field and the comparision operator, or an object - * @param {String|Number} [val] - The value to compare if the value of key is a string - * @return {QueryBuilder} - */ - having(/*key, [val]*/) { - let args = getArgs('key:string|object, [val]:string|number', arguments); - - this._having(args.key, args.val, 'AND'); - return this; - } - - /** - * Add an 'or having' clause - * - * @param {String|Object} key - The name of the field and the comparision operator, or an object - * @param {String|Number} [val] - The value to compare if the value of key is a string - * @return {QueryBuilder} - */ - orHaving(/*key, [val]*/) { - let args = getArgs('key:string|object, [val]:string|number', arguments); - - this._having(args.key, args.val, 'OR'); - return this; - } - - /** - * Set a 'where' clause - * - * @param {String|Object} key - The name of the field and the comparision operator, or an object - * @param {String|Number} [val] - The value to compare if the value of key is a string - * @return {QueryBuilder} - */ - where(key, val) { - this._where(key, val, 'AND'); - return this; - } - - /** - * Set a 'or where' clause - * - * @param {String|Object} key - The name of the field and the comparision operator, or an object - * @param {String|Number} [val] - The value to compare if the value of key is a string - * @return {QueryBuilder} - */ - orWhere(key, val) { - this._where(key, val, 'OR'); - return this; - } - - /** - * Select a field that is Null - * - * @param {String} field - The name of the field that has a NULL value - * @return {QueryBuilder} - */ - whereIsNull(field) { - this._whereNull(field, 'IS NULL', 'AND'); - return this; - } - - /** - * Specify that a field IS NOT NULL - * - * @param {String} field - * @return {QueryBuilder} - */ - whereIsNotNull(field) { - this._whereNull(field, 'IS NOT NULL', 'AND'); - return this; - } - - /** - * Field is null prefixed with 'OR' - * - * @param {String} field - * @return {QueryBuilder} - */ - orWhereIsNull(field) { - this._whereNull(field, 'IS NULL', 'OR'); - return this; - } - - /** - * Field is not null prefixed with 'OR' - * - * @param {String} field - * @return {QueryBuilder} - */ - orWhereIsNotNull(field) { - this._whereNull(field, 'IS NOT NULL', 'OR'); - return this; - } - - /** - * Set a 'where in' clause - * - * @param {String} key - the field to search - * @param {Array} val - the array of items to search in - * @return {QueryBuilder} - */ - whereIn(key, val) { - this._whereIn(key, val, 'IN', 'AND'); - return this; - } - - /** - * Set a 'or where in' clause - * - * @param {String} key - the field to search - * @param {Array} val - the array of items to search in - * @return {QueryBuilder} - */ - orWhereIn(key, val) { - this._whereIn(key, val, 'IN', 'OR'); - return this; - } - - /** - * Set a 'where not in' clause - * - * @param {String} key - the field to search - * @param {Array} val - the array of items to search in - * @return {QueryBuilder} - */ - whereNotIn(key, val) { - this._whereIn(key, val, 'NOT IN', 'AND'); - return this; - } - - /** - * Set a 'or where not in' clause - * - * @param {String} key - the field to search - * @param {Array} val - the array of items to search in - * @return {QueryBuilder} - */ - orWhereNotIn(key, val) { - this._whereIn(key, val, 'NOT IN', 'OR'); - return this; - } - - /** - * Set values for insertion or updating - * - * @param {String|Object} key - The key or object to use - * @param {String} [val] - The value if using a scalar key - * @return {QueryBuilder} - */ - set(/* $key, [$val] */) { - let args = getArgs('$key, [$val]', arguments); - - // Set the appropriate state variables - this._mixedSet('setArrayKeys', 'key', args.$key, args.$val); - this._mixedSet('values', 'value', args.$key, args.$val); - - // Use the keys of the array to make the insert/update string - // and escape the field names - this.state.setArrayKeys = this.state.setArrayKeys.map(this.driver._quote); - - // Generate the "set" string - this.state.setString = this.state.setArrayKeys.join('=?,'); - this.state.setString += '=?'; - - return this; - } - - /** - * Add a join clause to the query - * - * @param {String} table - The table you are joining - * @param {String} cond - The join condition. - * @param {String} [type='inner'] - The type of join, which defaults to inner - * @return {QueryBuilder} - */ - join(table, cond, type) { - type = type || "inner"; - - // Prefix/quote table name - table = table.split(' ').map(helpers.stringTrim); - table[0] = this.driver.quoteTable(table[0]); - table = table.map(this.driver.quoteIdentifiers); - table = table.join(' '); - - // Parse out the join condition - let parsedCondition = this.parser.compileJoin(cond); - let condition = table + ' ON ' + parsedCondition; - - // Append the join condition to the query map - this._appendMap("\n" + type.toUpperCase() + ' JOIN ', condition, 'join'); - - return this; - } - - /** - * Group the results by the selected field(s) - * - * @param {String|Array} field - * @return {QueryBuilder} - */ - groupBy(field) { - if ( ! helpers.isScalar(field)) - { - let newGroupArray = field.map(this.driver.quoteIdentifiers); - this.state.groupArray = this.state.groupArray.concat(newGroupArray); - } - else - { - this.state.groupArray.push(this.driver.quoteIdentifiers(field)); - } - - this.state.groupString = ' GROUP BY ' + this.state.groupArray.join(','); - - return this; - } - - /** - * Order the results by the selected field(s) - * - * @param {String} field - The field(s) to order by - * @param {String} [type='ASC'] - The order direction, ASC or DESC - * @return {QueryBuilder} - */ - orderBy(field, type) { - type = type || 'ASC'; - - // Set the fields for later manipulation - field = this.driver.quoteIdentifiers(field); - - this.state.orderArray[field] = type; - - let orderClauses = []; - - // Flatten key/val pairs into an array of space-separated pairs - Object.keys(this.state.orderArray).forEach((key) => { - orderClauses.push(key + ' ' + this.state.orderArray[key].toUpperCase()); - }); - - // Set the final string - this.state.orderString = ' ORDER BY ' + orderClauses.join(', '); - - return this; - } - - /** - * Put a limit on the query - * - * @param {Number} limit - The maximum number of rows to fetch - * @param {Number} [offset] - The row number to start from - * @return {QueryBuilder} - */ - limit(limit, offset) { - this.state.limit = limit; - this.state.offset = offset || null; - - return this; - } - - /** - * Adds an open paren to the current query for logical grouping - * - * @return {QueryBuilder} - */ - groupStart() { - let conj = (this.state.queryMap.length < 1) ? ' WHERE ' : ' AND '; - this._appendMap(conj, '(', 'groupStart'); - - return this; - } - - /** - * Adds an open paren to the current query for logical grouping, - * prefixed with 'OR' - * - * @return {QueryBuilder} - */ - orGroupStart() { - this._appendMap('', ' OR (', 'groupStart'); - - return this; - } - - /** - * Adds an open paren to the current query for logical grouping, - * prefixed with 'OR NOT' - * - * @return {QueryBuilder} - */ - orNotGroupStart() { - this._appendMap('', ' OR NOT (', 'groupStart'); - - return this; - } - - /** - * Ends a logical grouping started with one of the groupStart methods - * - * @return {QueryBuilder} - */ - groupEnd() { - this._appendMap('', ')', 'groupEnd'); - - return this; - } - - // ------------------------------------------------------------------------ - // ! Result Methods - // ------------------------------------------------------------------------ - - /** - * Get the results of the compiled query - * - * @param {String} [table] - The table to select from - * @param {Number} [limit] - A limit for the query - * @param {Number} [offset] - An offset for the query - * @param {Function} callback - A callback for receiving the result - * @return {void} - */ - get(/* [table], [limit], [offset], callback */) { - let args = getArgs('[table]:string, [limit]:number, [offset]:number, callback:function', arguments); - - if (args.table) { - this.from(args.table); - } - - if (args.limit) { - this.limit(args.limit, args.offset); - } - - // Run the query - this._run('get', args.table, args.callback); - } - - /** - * Run the generated insert query - * - * @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} - */ - insert(/* table, data, callback */) { - let args = getArgs('table:string, [data]:object, callback:function', arguments); - - if (args.data) { - this.set(args.data); - } - - // Run the query - this._run('insert', this.driver.quoteTable(args.table), args.callback); - } - - /** - * Insert multiple sets of rows at a time - * - * @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); - * @return {void} - */ - insertBatch(/* table, data, callback */) { - let args = getArgs('table:string, data:array, callback:function', arguments); - let batch = this.driver.insertBatch(args.table, args.data); - - // Run the query - this._run('', '', args.callback, batch.sql, batch.values); - } - - /** - * Run the generated update query - * - * @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} - */ - update(/*table, data, callback*/) { - let args = getArgs('table:string, [data]:object, callback:function', arguments); - - if (args.data) { - this.set(args.data); - } - - // Run the query - this._run('update', this.driver.quoteTable(args.table), args.callback); - } - - /** - * Run the generated delete query - * - * @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} - */ - delete(/*table, [where], callback*/) { - let args = getArgs('table:string, [where]:object, callback:function', arguments); - - if (args.where) - { - this.where(args.where); - } - - // Run the query - this._run('delete', this.driver.quoteTable(args.table), args.callback); - } - - // ------------------------------------------------------------------------ - // ! Methods returning SQL - // ------------------------------------------------------------------------ - - /** - * Return generated select query SQL - * - * @param {String} [table] - the name of the table to retrieve from - * @param {Boolean} [reset=true] - Whether to reset the query builder so another query can be built - * @return {String} - */ - getCompiledSelect(/*table, reset*/) { - let args = getArgs('[table]:string, [reset]:boolean', arguments); - if (args.table) - { - this.from(args.table); - } - - return this._getCompile('get', args.table, args.reset); - } - - /** - * Return generated insert query SQL - * - * @param {String} table - the name of the table to insert into - * @param {Boolean} [reset=true] - Whether to reset the query builder so another query can be built - * @return {String} - */ - getCompiledInsert(table, reset) { - return this._getCompile('insert', this.driver.quoteTable(table), reset); - } - - /** - * Return generated update query SQL - * - * @param {String} table - the name of the table to update - * @param {Boolean} [reset=true] - Whether to reset the query builder so another query can be built - * @return {String} - */ - getCompiledUpdate(table, reset) { - return this._getCompile('update', this.driver.quoteTable(table), reset); - } - - /** - * Return generated delete query SQL - * - * @param {String} table - the name of the table to delete from - * @param {Boolean} [reset=true] - Whether to reset the query builder so another query can be built - * @return {String} - */ - getCompiledDelete(table, reset) { - return this._getCompile('delete', this.driver.quoteTable(table), reset); - } -} \ No newline at end of file diff --git a/src/QueryParser.js b/src/QueryParser.js deleted file mode 100644 index 822a3b6..0000000 --- a/src/QueryParser.js +++ /dev/null @@ -1,274 +0,0 @@ -'use strict'; - -import helpers from './helpers'; - - -// -------------------------------------------------------------------------- - -/** - * @constructor - * @param {Driver} - The driver object for the database in use - * @module query-parser - */ -module.exports = class QueryParser { - /** - * @constructor - * @param {Driver} - The driver object for the database in use - * @return {void} - */ - constructor(driver) { - this.driver = driver; - - let matchPatterns = { - 'function': /([a-z0-9_]+\((.*)\))/i, - operator: /\!=?|\=|\+|&&?|~|\|\|?|\^|\/|<>|>=?|<=?|\-|%|OR|AND|NOT|XOR/ig, - literal: /([0-9]+)|'(.*?)'|true|false/ig - }; - - // Full pattern for identifiers - // Making sure that literals and functions aren't matched - matchPatterns.identifier = new RegExp( - '(' - + '(?!' - + matchPatterns['function'].source + '|' - + matchPatterns.literal.source - + ')' - + '([a-z_\-]+[0-9]*\\.?)' - + ')+' - , 'ig'); - - // Full pattern for determining ordering of the pieces - matchPatterns.joinCombined = new RegExp( - matchPatterns['function'].source + "+|" - + matchPatterns.literal.source + '+|' - + matchPatterns.identifier.source - + '|(' + matchPatterns.operator.source + ')+' - , 'ig'); - - this.matchPatterns = matchPatterns; - this.identifierBlacklist = ['true','false','null']; - } - - /** - * Filter matched patterns - * - * @param {Array} array - * @return {Array|null} - */ - filterMatches(array) { - let output = []; - - // Return non-array matches - if (helpers.isNull(array)) return null; - if (helpers.isScalar(array) || helpers.isUndefined(array)) return output; - - array.forEach((item) => { - output.push(item); - }); - return output; - } - - /** - * Check if the string contains an operator, and if so, return the operator(s). - * If there are no matches, return null - * - * @param {String} string - the string to check - * @return {Array|null} - */ - hasOperator(string) { - return this.filterMatches(string.match(this.matchPatterns.operator)); - } - - /** - * Tokenize the sql into parts for additional processing - * - * @param {String} sql - * @return {Object} - */ - parseJoin(sql) { - let matches = {}; - let output = { - functions: [], - identifiers: [], - operators: [], - literals: [] - }; - - // Get clause components - matches.functions = sql.match(new RegExp(this.matchPatterns['function'].source, 'ig')); - matches.identifiers = sql.match(this.matchPatterns.identifier); - matches.operators = sql.match(this.matchPatterns.operator); - matches.literals = sql.match(this.matchPatterns.literal); - - // Get everything at once for ordering - matches.combined = sql.match(this.matchPatterns.joinCombined); - - // Flatten the matches to increase relevance - Object.keys(matches).forEach((key) => { - output[key] = this.filterMatches(matches[key]); - }); - - return output; - } - - /** - * Return the output of the parsing of the join condition - * - * @param {String} condition - The join condition to evalate - * @return {String} - The parsed/escaped join condition - */ - compileJoin(condition) { - let parts = this.parseJoin(condition); - let count = parts.identifiers.length; - let i; - - // Quote the identifiers - parts.combined.forEach((part, i) => { - if (parts.identifiers.indexOf(part) !== -1 && ! helpers.isNumber(part)) - { - parts.combined[i] = this.driver.quoteIdentifiers(part); - } - }); - - return parts.combined.join(' '); - } - - /** - * Parse a where clause to separate functions from values - * - * @param {Driver} driver - * @param {State} state - * @return {String} - The parsed/escaped where condition - */ - parseWhere(driver, state) { - let whereMap = state.whereMap; - let whereValues = state.rawWhereValues; - - let outputMap = []; - let outputValues = []; - - Object.keys(whereMap).forEach((key) => { - // Combine fields, operators, functions and values into a full clause - // to have a common starting flow - let fullClause = ''; - - // Add an explicit = sign where one is inferred - if ( ! this.hasOperator(key)) - { - fullClause = key + ' = ' + whereMap[key]; - } - else if (whereMap[key] === key) - { - fullClause = key; - } - else - { - fullClause = key + ' ' + whereMap[key]; - } - - // Separate the clause into separate pieces - let parts = this.parseJoin(fullClause); - - // Filter explicit literals from lists of matches - if (whereValues.indexOf(whereMap[key]) !== -1) - { - let value = whereMap[key]; - let identIndex = (helpers.isArray(parts.identifiers)) ? parts.identifiers.indexOf(value) : -1; - let litIndex = (helpers.isArray(parts.literals)) ? parts.literals.indexOf(value) : -1; - let combIndex = (helpers.isArray(parts.combined)) ? parts.combined.indexOf(value) : -1; - let funcIndex = (helpers.isArray(parts.functions)) ? parts.functions.indexOf(value) : -1; - let inOutputArray = outputValues.indexOf(value) !== -1; - - // Remove the identifier in question, - // and add to the output values array - if (identIndex !== -1) - { - parts.identifiers.splice(identIndex, 1); - - if ( ! inOutputArray) - { - outputValues.push(value); - inOutputArray = true; - } - } - - // Remove the value from the literals list - // so it is not added twice - if (litIndex !== -1) - { - parts.literals.splice(litIndex, 1); - - if ( ! inOutputArray) - { - outputValues.push(value); - inOutputArray = true; - } - } - - // Remove the value from the combined list - // and replace it with a placeholder - if (combIndex !== -1) - { - // Make sure to skip functions when replacing values - if (funcIndex === -1) - { - parts.combined[combIndex] = '?'; - - if ( ! inOutputArray) - { - outputValues.push(value); - inOutputArray = true; - } - } - } - } - - // Filter false positive identifiers - parts.identifiers = parts.identifiers || []; - parts.identifiers = parts.identifiers.filter((item) => { - let isInCombinedMatches = parts.combined.indexOf(item) !== -1; - let isNotInBlackList = this.identifierBlacklist.indexOf(item.toLowerCase()) === -1; - - return isInCombinedMatches && isNotInBlackList; - }, this); - - // Quote identifiers - if (helpers.isArray(parts.identifiers)) - { - parts.identifiers.forEach((ident) => { - let index = parts.combined.indexOf(ident); - if (index !== -1) - { - parts.combined[index] = driver.quoteIdentifiers(ident); - } - }); - } - - // Replace each literal with a placeholder in the map - // and add the literal to the values, - // This should only apply to literal values that are not - // explicitly mapped to values, but have to be parsed from - // a where condition, - if (helpers.isArray(parts.literals)) - { - parts.literals.forEach((lit) => { - let litIndex = parts.combined.indexOf(lit); - - if (litIndex !== -1) - { - parts.combined[litIndex] = (helpers.isArray(parts.operators)) ? '?' : '= ?'; - outputValues.push(lit); - } - }); - } - - outputMap.push(parts.combined.join(' ')); - }); - - state.rawWhereValues = []; - state.whereValues = state.whereValues.concat(outputValues); - state.whereMap = outputMap; - - return state; - } -} \ No newline at end of file diff --git a/src/State.js b/src/State.js deleted file mode 100644 index 1e6a889..0000000 --- a/src/State.js +++ /dev/null @@ -1,29 +0,0 @@ -'use strict'; - -/** @module State */ -module.exports = class State { - constructor() { - // Arrays/maps - this.queryMap = []; - this.values = []; - this.whereValues = []; - this.setArrayKeys = []; - this.orderArray = []; - this.groupArray = []; - this.havingMap = []; - this.whereMap = []; - this.rawWhereValues = []; - - // Partials - this.selectString = ''; - this.fromString = ''; - this.setString = ''; - this.orderString = ''; - this.groupString = ''; - - // Other various values - this.limit = null; - this.offset = null; - } -} -// End of module State \ No newline at end of file diff --git a/src/adapters/dblite.js b/src/adapters/dblite.js deleted file mode 100644 index 337f9fd..0000000 --- a/src/adapters/dblite.js +++ /dev/null @@ -1,20 +0,0 @@ -'use strict'; - -import Adapter from '../Adapter'; -import getArgs from 'getargs'; - -/** @module adapters/dblite */ -module.exports = class dblite extends Adapter { - /** - * 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 - */ - execute(/*sql, params, callback*/) { - let args = getArgs('sql:string, [params]:array, callback:function', arguments); - this.instance.query(args.sql, args.params, args.callback); - }; -} \ No newline at end of file diff --git a/src/adapters/mysql.js b/src/adapters/mysql.js deleted file mode 100644 index 2a07ccb..0000000 --- a/src/adapters/mysql.js +++ /dev/null @@ -1,18 +0,0 @@ -'use strict'; - -import Adapter from '../Adapter'; - -/** @module adapters/mysql */ -module.exports = class mysql extends Adapter { - /** - * 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 - */ - execute(sql, params, callback) { - this.instance.query.apply(instance, arguments); - } -} \ No newline at end of file diff --git a/src/adapters/mysql2.js b/src/adapters/mysql2.js deleted file mode 100644 index 85daf2c..0000000 --- a/src/adapters/mysql2.js +++ /dev/null @@ -1,18 +0,0 @@ -'use strict'; - -import Adapter from '../Adapter'; - -/** @module adapters/mysql2 */ -module.exports = class mysql2 extends Adapter { - /** - * 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 - */ - execute(sql, params, callback) { - this.instance.execute.apply(this.instance, arguments); - }; -} \ No newline at end of file diff --git a/src/adapters/node-firebird.js b/src/adapters/node-firebird.js deleted file mode 100644 index d60fae4..0000000 --- a/src/adapters/node-firebird.js +++ /dev/null @@ -1,20 +0,0 @@ -'use strict'; - -import Adapter from '../Adapter'; -import getArgs from 'getargs'; - -/** @module adapters/node-firebird */ -module.exports = class nodefirebird extends Adapter { - /** - * 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 - */ - execute(/*sql, params, callback*/) { - let args = getArgs('sql:string, [params], callback:function', arguments); - this.instance.execute(args.sql, args.params, args.callback); - } -} \ No newline at end of file diff --git a/src/adapters/pg.js b/src/adapters/pg.js deleted file mode 100644 index 9ca4bb0..0000000 --- a/src/adapters/pg.js +++ /dev/null @@ -1,28 +0,0 @@ -'use strict'; - -import Adapter from '../Adapter'; -import getArgs from 'getargs'; - -/** @module adapters/pg */ -module.exports = class pg extends Adapter { - /** - * 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 - */ - execute(/*sql, params, callback*/) { - let args = getArgs('sql:string, [params]:array, callback:function', arguments); - - // Replace question marks with numbered placeholders, because this adapter is different... - let count = 0; - args.sql = args.sql.replace(/\?/g, () => { - count++; - return '$' + count; - }); - - this.instance.query(args.sql, args.params, args.callback); - } -} \ No newline at end of file diff --git a/src/drivers/Firebird.js b/src/drivers/Firebird.js deleted file mode 100644 index 92f7bc4..0000000 --- a/src/drivers/Firebird.js +++ /dev/null @@ -1,49 +0,0 @@ -"use strict"; - -import helpers from '../helpers'; -import Driver from '../DriverClass'; - -/** - * Driver for Firebird databases - * - * @module drivers/firebird - */ -class Firebird extends Driver { - constructor() { - super({ - hasTruncate: false - }); - } - - /** - * Generate a limit clause for firebird, which uses the syntax closest to the SQL standard - * - * @param {String} sql - * @param {Number} limit - * @param {Number} offset - * @return {String} - */ - 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 - * - * @param {String} table - The table to insert to - * @param {Array} [data] - The array of object containing data to insert - * @return {String} - */ - insertBatch() { - throw new Error("Not Implemented"); - } -} - -module.exports = new Firebird(); \ No newline at end of file diff --git a/src/drivers/Mysql.js b/src/drivers/Mysql.js deleted file mode 100755 index 1146231..0000000 --- a/src/drivers/Mysql.js +++ /dev/null @@ -1,29 +0,0 @@ -"use strict"; - -import helpers from '../helpers'; -import Driver from '../DriverClass'; - -/** - * Driver for MySQL databases - * - * @module drivers/mysql - */ -class Mysql extends Driver { - constructor() { - super({ - identifierStartChar: '`', - identifierEndChar: '`' - }); - } - - limit(sql, limit, offset) { - if ( ! helpers.isNumber(offset)) - { - return sql += ` LIMIT ${limit}`; - } - - return sql += ` LIMIT ${offset}, ${limit}`; - } -} - -module.exports = new Mysql(); \ No newline at end of file diff --git a/src/drivers/Pg.js b/src/drivers/Pg.js deleted file mode 100755 index 43e2fd7..0000000 --- a/src/drivers/Pg.js +++ /dev/null @@ -1,10 +0,0 @@ -"use strict"; - -import Driver from '../DriverClass'; - -/** - * Driver for PostgreSQL databases - * - * @module drivers/pg - */ -module.exports = new Driver(); \ No newline at end of file diff --git a/src/drivers/Sqlite.js b/src/drivers/Sqlite.js deleted file mode 100644 index 0dd7872..0000000 --- a/src/drivers/Sqlite.js +++ /dev/null @@ -1,64 +0,0 @@ -"use strict"; - -import helpers from '../helpers'; -import Driver from '../DriverClass'; - -/** - * Driver for Sqlite databases - * - * @module drivers/sqlite - */ -class Sqlite extends Driver { - constructor() { - super(); - this.hasTruncate = false; - } - - insertBatch(table, data) { - - // Get the data values to insert, so they can - // be parameterized - let sql = "", - vals = [], - cols = [], - fields = [], - first = data.shift(), - params = [], - paramString = "", - paramList = []; - - - data.forEach((obj) => { - let row = []; - Object.keys(obj).forEach((key) => { - row.push(obj[key]); - }); - vals.push(row); - }); - - sql += "INSERT INTO " + this.quoteTable(table) + "\n"; - - // Get the field names from the keys of the first - // object to be inserted - fields = Object.keys(first); - Object.keys(first).forEach((key) => { - cols.push("'" + this._quote(first[key]) + "' AS " + this.quoteIdentifiers(key)); - }); - - sql += "SELECT " + cols.join(', ') + "\n"; - - vals.forEach((row_values) => { - let quoted = row_values.map((value) => { - return String(value).replace("'", "'\'"); - }); - sql += "UNION ALL SELECT '" + quoted.join("', '") + "'\n"; - }); - - return { - sql: sql, - values: null - }; - } -}; - -module.exports = new Sqlite(); \ No newline at end of file diff --git a/src/helpers.js b/src/helpers.js deleted file mode 100755 index 1458e0d..0000000 --- a/src/helpers.js +++ /dev/null @@ -1,127 +0,0 @@ -"use strict"; - -//require('es6-shim'); - -let helpers = { - /** - * Wrap String.prototype.trim in a way that is easily mappable - * - * @param {String} str - The string to trim - * @return {String} - The trimmed string - */ - stringTrim: (str) => str.trim(), - /** - * Get the type of the variable passed - * - * @see https://techblog.badoo.com/blog/2013/11/01/type-checking-in-javascript/ - * @see http://toddmotto.com/understanding-javascript-types-and-reliable-type-checking/ - * @param {mixed} o - * @return {String} - */ - type: (o) => { - let type = Object.prototype.toString.call(o).slice(8, -1).toLowerCase(); - - // handle NaN and Infinity - if (type === 'number') { - if (isNaN(o)) { - return 'nan'; - } - if (!isFinite(o)) { - return 'infinity'; - } - } - - return type; - }, - /** - * Determine whether an object is scalar - * - * @param {mixed} obj - * @return {bool} - */ - isScalar: (obj) => { - let scalar = ['string', 'number', 'boolean']; - return scalar.indexOf(helpers.type(obj)) !== -1; - }, - /** - * Get a list of values with a common key from an array of objects - * - * @param {Array} arr - The array of objects to search - * @param {String} key - The key of the object to get - * @return {Array} - */ - arrayPluck: (arr, key) => { - let output = []; - - // Empty case - if (arr.length === 0) return output; - - arr.forEach((obj) => { - if ( ! helpers.isUndefined(obj[key])) - { - output.push(obj[key]); - } - }); - - return output; - }, - /** - * Determine if a value matching the passed regular expression is - * in the passed array - * - * @param {Array} arr - The array to search - * @param {RegExp} pattern - The pattern to match - * @return {Boolean} - If an array item matches the pattern - */ - regexInArray: (arr, pattern) => { - // Empty case(s) - if ( ! helpers.isArray(arr)) return false; - if (arr.length === 0) return false; - - let i, l = arr.length; - - for(i=0; i< l; i++) - { - // Short circuit if any items match - if (pattern.test(arr[i])) return true; - } - - return false; - }, - /** - * Make the first letter of the string uppercase - * - * @param {String} str - * @return {String} - */ - upperCaseFirst: (str) => { - str += ''; - let first = str.charAt(0).toUpperCase(); - return first + str.substr(1); - } -}; - -// Define an 'is' method for each type -let types = ['Null','Undefined','Object','Array','String','Number','Boolean','Function','RegExp','NaN','Infinite']; -types.forEach((t) => { - /** - * Determine whether a variable is of the type specified in the - * function name, eg isNumber - * - * Types available are Null, Undefined, Object, Array, String, Number, Boolean, Function, RegExp, NaN and Infinite - * - * @name is[type] - * @param {mixed} o - * @return {Boolean} - */ - helpers['is' + t] = function (o) { - if (t.toLowerCase() === 'infinite') - { - t = 'infinity'; - } - - return helpers.type(o) === t.toLowerCase(); - }; -}); - -module.exports = helpers; \ No newline at end of file diff --git a/tests/FB_TEST_DB.FDB b/test/FB_TEST_DB.FDB similarity index 99% rename from tests/FB_TEST_DB.FDB rename to test/FB_TEST_DB.FDB index bcf765a..b682cad 100755 Binary files a/tests/FB_TEST_DB.FDB and b/test/FB_TEST_DB.FDB differ diff --git a/test/base_test.js b/test/base_test.js new file mode 100644 index 0000000..f0c773d --- /dev/null +++ b/test/base_test.js @@ -0,0 +1,39 @@ +'use strict'; + +let assert = require('chai').assert; +let nodeQuery = require('../lib/NodeQuery'); + +suite('Base tests', () => { + test('Sanity check', () => { + let modules = { + helpers: require('../lib/helpers'), + driver: require('../lib/DriverBase'), + qb: require('../lib/QueryBuilder'), + 'node-query': require('../lib/NodeQuery'), + 'state': require('../lib/State'), + 'drivers/pg': require('../lib/drivers/Pg'), + 'drivers/mysql': require('../lib/drivers/Mysql'), + 'drivers/sqlite': require('../lib/drivers/Sqlite'), + 'adapters/mysql': require('../lib/adapters/mysql'), + 'adapters/mysql2': require('../lib/adapters/mysql2'), + 'adapters/pg': require('../lib/adapters/pg'), + 'adapters/dblite': require('../lib/adapters/dblite') + }; + + Object.keys(modules).forEach(mod => { + assert.ok(modules[mod], mod + " module is sane"); + }); + }); + + test('NodeQuery.getQuery with no instance', () => { + assert.throws(() => { + nodeQuery.getQuery(); + }, Error, "No Query Builder instance to return"); + }); + + test('Invalid driver type', () => { + assert.throws(() => { + nodeQuery.init('foo', {}, 'bar'); + }, Error, "Selected driver (Foo) does not exist!"); + }); +}); \ No newline at end of file diff --git a/tests/config-travis.json b/test/config-travis.json similarity index 100% rename from tests/config-travis.json rename to test/config-travis.json diff --git a/tests/config.json b/test/config.json similarity index 100% rename from tests/config.json rename to test/config.json diff --git a/test/helpers_test.js b/test/helpers_test.js new file mode 100644 index 0000000..616ae38 --- /dev/null +++ b/test/helpers_test.js @@ -0,0 +1,136 @@ +'use strict'; + +let chai = require('chai'), + assert = chai.assert, + expect = chai.expect, + should = chai.should(); + +let helpers = require('../lib/helpers'); + +suite('Helper Module Tests', () => { + suite('Type-checking methods', () => { + suite('Object wrappers are listed as their native type', () => { + test("Boolean Wrapper returns 'boolean' not 'object'", () => { + let item = Boolean(true); + expect(helpers.type(item)).to.deep.equal('boolean'); + }); + test("Number Wrapper returns 'number' not 'object", () => { + let item = Number(4867); + expect(helpers.type(item)).to.deep.equal('number'); + }); + test("String Wrapper returns 'string' not 'object'", () => { + let item = String("Foo"); + expect(helpers.type(item)).to.deep.equal('string'); + }); + }); + suite('is..Method methods exist', () => { + let types = ['Null','Undefined','Object','Array','String','Number','Boolean','Function','RegExp','NaN','Infinite']; + + types.forEach(type => { + test(`is${type} method exists`, () => { + assert.ok(helpers[`is${type}`]); + }); + }); + }); + suite('isScalar', () => { + let trueCases = { + 'Strings are scalar': 'foo', + 'Booleans are scalar': true, + 'Numbers are scalar': 545 + }; + Object.keys(trueCases).forEach(desc => { + test(desc, () => { + expect(helpers.isScalar(trueCases[desc])).to.be.true; + }); + }); + + let falseCases = { + 'Arrays are not scalar': [], + 'Objects are not scalar': [], + }; + Object.keys(falseCases).forEach(desc => { + test(desc, () => { + expect(helpers.isScalar(falseCases[desc])).to.be.false; + }); + }); + }); + suite('isInfinity', () => { + test('The type of 1/0 is infinity', () => { + expect(helpers.type(1/0)).to.equal('infinity'); + }); + test('isInfinity is the same as isInfinite', () => { + expect(helpers.isInfinite(1/0)).to.be.true; + }); + }); + suite('isNaN', () => { + test('The type of 0 / 0 is NaN', () => { + expect(helpers.type(0 / 0)).to.equal('nan'); + }); + test('isNaN method agrees with type', () => { + expect(helpers.isNaN(0 / 0)).to.be.true; + }); + }); + }); + suite('Other helper methods', () => { + suite('stringTrim', () => { + test('stringTrim method works as expected', () => { + let orig = [' x y ', 'z ', ' q']; + let ret = ['x y', 'z', 'q']; + + expect(orig.map(helpers.stringTrim)).to.be.deep.equal(ret); + }); + }); + suite('arrayPluck', () => { + let orig = [{ + foo: 1 + },{ + foo: 2, + bar: 10 + },{ + foo: 3, + bar: 15 + }]; + + test('Finding members in all objects', () => { + expect(helpers.arrayPluck(orig, 'foo')).to.be.deep.equal([1,2,3]); + }); + test('Some members are missing in some objects', () => { + expect(helpers.arrayPluck(orig, 'bar')).to.be.deep.equal([10, 15]); + }); + test('Empty case', () => { + expect(helpers.arrayPluck([], 'apple')).to.be.deep.equal([]); + }); + }); + suite('regexInArray', () => { + let orig = ['apple', ' string ', 6, 4, 7]; + + let cases = [{ + 'Dollar sign is not in any of the array items': /\$/, + 'None of the numbers in the array match /5/': /5/ + },{ + "' string ' matches /^ ?string/": /^ ?string/, + "'apple' matches /APPLE/i": /APPLE/i + }]; + + [0, 1].forEach(i => { + let boolCase = cases[i]; + Object.keys(boolCase).forEach(desc => { + test(desc, () => { + if (i) { + expect(helpers.regexInArray(orig, boolCase[desc])).to.be.true; + } else { + expect(helpers.regexInArray(orig, boolCase[desc])).to.be.false; + } + }); + }); + }); + + test('First argument is not an array', () => { + expect(helpers.regexInArray(5, /5/)).to.be.false; + }); + test('Array is empty', () => { + expect(helpers.regexInArray([], /.*/)).to.be.false; + }); + }); + }); +}); diff --git a/test/query-parser_test.js b/test/query-parser_test.js new file mode 100644 index 0000000..d136f6c --- /dev/null +++ b/test/query-parser_test.js @@ -0,0 +1,178 @@ +"use strict"; + +let expect = require('chai').expect; + +// Use the base driver as a mock for testing +let getArgs = require('getargs'); +let helpers = require('../lib/helpers'); +let driver = require('../lib/DriverBase'); + +let p = require('../lib/QueryParser'); +let parser = new p(driver); + +let State = require('../lib/State'); + +// Simulate query builder state +let state = new State(); + +let mixedSet = function mixedSet(/* $letName, $valType, $key, [$val] */) { + let args = getArgs('$letName:string, $valType:string, $key:object|string|number, [$val]', arguments); + + let obj = {}; + + if (helpers.isScalar(args.$key) && !helpers.isUndefined(args.$val)) + { + // Convert key/val pair to a simple object + obj[args.$key] = args.$val; + } + else if (helpers.isScalar(args.$key) && helpers.isUndefined(args.$val)) + { + // If just a string for the key, and no value, create a simple object with duplicate key/val + obj[args.$key] = args.$key; + } + else + { + obj = args.$key; + } + + Object.keys(obj).forEach(k => { + // If a single value for the return + if (['key','value'].indexOf(args.$valType) !== -1) + { + let pushVal = (args.$valType === 'key') ? k : obj[k]; + state[args.$letName].push(pushVal); + } + else + { + state[args.$letName][k] = obj[k]; + } + }); + + + return state[args.$letName]; +} + +let whereMock = function() { + let args = getArgs('key:string|object, [val]', arguments); + + state.whereMap = []; + state.whereValues = []; + + mixedSet('rawWhereValues', 'value', args.key, args.val); + mixedSet('whereMap', 'both', args.key, args.val); +} + +// ----------------------------------------------------------------------------- +// ! Start Tests +// ----------------------------------------------------------------------------- + +suite('Query Parser Tests', () => { + suite('Has operator tests', () => { + test('Has operator', () => { + let matches = parser.hasOperator('foo <> 2'); + expect(matches).to.be.deep.equal(['<>']); + }); + test('Has no operator', () => { + let matches = parser.hasOperator('foo'); + expect(matches).to.be.null; + }) + }); + suite('Where parser tests', () => { + setup(() => { + state = new State(); + }); + test('Has function full string', () => { + whereMock('time < SUM(FOO(BAR()))'); + parser.parseWhere(driver, state); + expect(state.whereMap) + .to.be.deep.equal(['"time" < SUM(FOO(BAR()))']); + }); + test('Has function key/val', () => { + whereMock('time <', 'SUM(FOO(BAR()))'); + parser.parseWhere(driver, state); + expect(state.whereMap) + .to.be.deep.equal(['"time" < SUM(FOO(BAR()))']); + }); + test('Has function key/val object', () => { + whereMock({ + 'time <': "SUM(FOO(BAR('x')))" + }); + parser.parseWhere(driver, state); + expect(state.whereMap) + .to.be.deep.equal(['"time" < SUM(FOO(BAR(\'x\')))']); + }); + test('Has literal value', () => { + whereMock({ + 'foo': 3 + }); + parser.parseWhere(driver, state); + expect(state.whereMap) + .to.be.deep.equal(['"foo" = ?']); + expect(state.whereValues) + .to.be.deep.equal(['3']); + }); + test('Has multiple literal values', () => { + whereMock({ + foo: 3, + bar: 5 + }); + parser.parseWhere(driver, state); + expect(state.whereMap) + .to.be.deep.equal(['"foo" = ?', '"bar" = ?']); + expect(state.whereValues) + .to.be.deep.equal(['3','5']); + }); + }); + suite('Parse join tests', () => { + let data = [{ + desc: 'Simple equals condition', + join: 'table1.field1=table2.field2', + expected: ['table1.field1','=','table2.field2'] + },{ + desc: 'Db.table.field condition', + join: 'db1.table1.field1!=db2.table2.field2', + expected: ['db1.table1.field1','!=', 'db2.table2.field2'] + },{ + desc: 'Underscore in identifier', + join: 'table_1.field1 = tab_le2.field_2', + expected: ['table_1.field1', '=', 'tab_le2.field_2'] + },{ + desc: 'Function in condition', + join: 'table1.field1 > SUM(3+6)', + expected: ['table1.field1', '>', 'SUM(3+6)'] + }]; + + data.forEach(datum => { + test(datum.desc, () => { + let matches = parser.parseJoin(datum.join); + expect(matches.combined).to.be.deep.equal(datum.expected); + }) + }); + }); + suite('Compile join tests', () => { + let data = [{ + desc: 'Simple equals condition', + clause: 'table1.field1=table2.field2', + expected: '"table1"."field1" = "table2"."field2"' + },{ + desc: 'Db.table.field condition', + clause: 'db1.table1.field1!=db2.table2.field2', + expected: '"db1"."table1"."field1" != "db2"."table2"."field2"' + },{ + desc: 'Underscore in identifier', + clause: 'table_1.field1 = tab_le2.field_2', + expected: '"table_1"."field1" = "tab_le2"."field_2"' + },{ + desc: 'Function in condition', + clause: 'table1.field1 > SUM(3+6)', + expected: '"table1"."field1" > SUM(3+6)' + }]; + + data.forEach(datum => { + test(datum.desc, () => { + let join = parser.compileJoin(datum.clause); + expect(join).to.be.deep.equal(datum.expected); + }); + }); + }); +}); \ No newline at end of file diff --git a/tests/sql/mysql.sql b/test/sql/mysql.sql similarity index 100% rename from tests/sql/mysql.sql rename to test/sql/mysql.sql diff --git a/tests/sql/pgsql.sql b/test/sql/pgsql.sql similarity index 100% rename from tests/sql/pgsql.sql rename to test/sql/pgsql.sql diff --git a/tests/sql/sqlite.sql b/test/sql/sqlite.sql similarity index 100% rename from tests/sql/sqlite.sql rename to test/sql/sqlite.sql diff --git a/tests/adapters/dblite_test.js b/tests/adapters/dblite_test.js deleted file mode 100644 index 6af9260..0000000 --- a/tests/adapters/dblite_test.js +++ /dev/null @@ -1,91 +0,0 @@ -'use strict'; - -// Load the test base -delete require.cache[require.resolve('../query-builder-base')]; -var testBase = require('../query-builder-base'); -var tests = testBase.tests; - -// Load the test config file -var adapterName = 'dblite'; -var sqlite = null; -var connection = null; - -// Set up the connection -try { - sqlite = require(adapterName).withSQLite('3.7.11'); - connection = sqlite(':memory:'); -} catch (e) { - // Export an empty testsuite if module not loaded - console.log(e); - console.log("Database adapter dblite not found"); - //return {}; -} - -if (connection) -{ - // Set up the query builder object - var nodeQuery = require('../../lib/NodeQuery'); - var qb = nodeQuery.init('sqlite', connection, adapterName); - - // Set up the sqlite database - var sql = 'CREATE TABLE IF NOT EXISTS "create_test" ("id" INTEGER PRIMARY KEY, "key" TEXT, "val" TEXT);' + - 'CREATE TABLE IF NOT EXISTS "create_join" ("id" INTEGER PRIMARY KEY, "key" TEXT, "val" TEXT);'; - connection.query(sql); - - // Set up the test base - testBase._setUp(qb, function(test, err, rows) { - if (err != null) { - test.done(); - throw new Error(err); - } - - // Insert/Update/Delete queries return undefined - if (rows === undefined) { - rows = {}; - } - - test.ok(rows, 'dblite: Invalid result for generated query'); - test.done(); - }); - - tests['nodeQuery.getQuery = nodeQuery.init'] = function(test) { - test.expect(1); - test.deepEqual(qb, nodeQuery.getQuery(), "getQuery returns same object"); - test.done(); - }; - - tests['Select tests']['Select with function and argument in WHERE clause'] = function(test) { - test.expect(1); - qb.select('id') - .from('create_test') - .where('id', 'ABS(-88)') - .get(function(err, rows) { - if (err != null) { - test.done(); - throw new Error(err); - } - - test.ok(rows, 'dblite: Valid result for generated query'); - test.done(); - }); - }; - - tests["dblite adapter with query builder"] = function(test) { - test.expect(1); - test.ok(testBase.qb); - - // Close the db connection - connection.close(); - test.done(); - }; - - // Export the final test object - module.exports = tests; -} -else -{ - module.exports = {}; -} - - - diff --git a/tests/adapters/mysql2_test.js b/tests/adapters/mysql2_test.js deleted file mode 100644 index 324071a..0000000 --- a/tests/adapters/mysql2_test.js +++ /dev/null @@ -1,51 +0,0 @@ -'use strict'; - -var configFile = (process.env.CI) ? '../config-travis.json' : '../config.json'; - -// Load a fresh version of the test base -delete require.cache[require.resolve('../query-builder-base')]; -var testBase = require('../query-builder-base'); -var tests = testBase.tests; - -// Load the test config file -var adapterName = 'mysql2'; -var config = require(configFile)[adapterName]; - -// Set up the connection -var mysql2 = require(adapterName); -var connection = mysql2.createConnection(config.conn); - -// Set up the query builder object -var nodeQuery = require('../../lib/NodeQuery'); -var qb = nodeQuery.init('mysql', connection, adapterName); - - - -tests['nodeQuery.getQuery = nodeQuery.init'] = function(test) { - test.expect(1); - test.deepEqual(qb, nodeQuery.getQuery(), "getQuery returns same object"); - test.done(); -}; - -// Export the final test object -tests["mysql2 adapter with query builder"] = function(test) { - test.expect(1); - test.ok(testBase.qb); - - // Close the db connection - connection.end(); - test.done(); -}; - -// Set up the test base -testBase._setUp(qb, function(test, err, rows) { - if (err != null) { - test.done(); - throw new Error(err); - } - - test.ok(rows, 'mysql2: Invalid result for generated query'); - test.done(); -}); - -module.exports = tests; \ No newline at end of file diff --git a/tests/adapters/mysql_test.js b/tests/adapters/mysql_test.js deleted file mode 100644 index e46e02e..0000000 --- a/tests/adapters/mysql_test.js +++ /dev/null @@ -1,51 +0,0 @@ -'use strict'; - -var configFile = (process.env.CI) ? '../config-travis.json' : '../config.json'; - -// Load the test base -delete require.cache[require.resolve('../query-builder-base')]; -var testBase = require('../query-builder-base'); -var tests = testBase.tests; - -// Load the test config file -var adapterName = 'mysql'; -var config = require(configFile)[adapterName]; - -// Set up the connection -var mysql = require(adapterName); -var connection = mysql.createConnection(config.conn); - -// Set up the query builder object -var nodeQuery = require('../../lib/NodeQuery'); -var qb = nodeQuery.init('mysql', connection); - -// Set up the test base -testBase._setUp(qb, function(test, err, rows) { - if (err != null) { - test.done(); - throw new Error(err); - } - - test.ok(rows, 'mysql: Valid result for generated query'); - test.done(); -}); - -tests['nodeQuery.getQuery = nodeQuery.init'] = function(test) { - test.expect(1); - test.deepEqual(qb, nodeQuery.getQuery(), "getQuery returns same object"); - test.done(); -}; - -tests["mysql adapter with query builder"] = function(test) { - test.expect(1); - test.ok(testBase.qb); - - // Close the db connection - qb = null; - connection.destroy(); - - test.done(); -}; - -// Export the final test object -module.exports = tests; diff --git a/tests/adapters/node-firebird_test.js b/tests/adapters/node-firebird_test.js deleted file mode 100644 index d8774c7..0000000 --- a/tests/adapters/node-firebird_test.js +++ /dev/null @@ -1,96 +0,0 @@ -'use strict'; - -// Load the test base -var testBase = require('../query-builder-base'); - -// Load the test config file -var adapterName = 'node-firebird'; -var config = require('../config.json')[adapterName]; -config.conn.database = __dirname + config.conn.database; -var nodeQuery = require('../../lib/NodeQuery'); - -// Skip on TravisCi -if (process.env.CI || process.env.JENKINS_HOME) -{ - module.exports = {}; - return; -} - -// Set up the connection -try { - var Firebird = require(adapterName); - var conn = null; - var qb = null; - - // Setup testbase from the inside out - // Because the connection is async, utilize - // the setUp function from nodeunit to get the connection - testBase.tests.setUp = function(cb) { - if ( ! conn) - { - // Connect to the database - Firebird.attach(config.conn, function(err, db) { - if (err) { - console.error(err); - } - conn = db; - - // Set up the query builder object - qb = nodeQuery.init('firebird', db, adapterName); - - testBase._setUp(qb, function(test, err, result) { - if (err) { - test.done(); - throw new Error(err); - } - - result = result || []; - - test.ok(result, 'firebird: Valid result for generated query'); - test.done(); - }); - - cb(); - }); - } - else - { - cb(); - } - }; - - //delete testBase.tests['DB update tests']; - testBase.tests['DB update tests']['Test Insert Batch'] = function(test) { - test.expect(1); - - test.throws(function() { - qb.insertBatch({}, (function() {})); - }, Error, "Insert Batch not implemented for firebird"); - - test.done(); - }; - - testBase.tests['nodeQuery.getQuery = nodeQuery.init'] = function(test) { - test.expect(1); - test.deepEqual(qb, nodeQuery.getQuery(), "getQuery returns same object"); - test.done(); - }; - - testBase.tests["firebird adapter with query builder"] = function(test) { - test.expect(1); - test.ok(testBase.qb); - - // Disconnect from the db - conn.detach(); - - test.done(); - }; - - module.exports = testBase.tests; - -} catch (e) { - // Export an empty testBase.testsuite if module not loaded - console.log(e); - console.log("Database adapter firebird not found"); - module.exports = {}; -} \ No newline at end of file diff --git a/tests/adapters/pg_test.js b/tests/adapters/pg_test.js deleted file mode 100644 index e787420..0000000 --- a/tests/adapters/pg_test.js +++ /dev/null @@ -1,54 +0,0 @@ -'use strict'; - -var configFile = (process.env.CI) ? '../config-travis.json' : '../config.json'; - -// Load the test base -delete require.cache[require.resolve('../query-builder-base')]; -var testBase = require('../query-builder-base'); -var tests = testBase.tests; - -// Load the test config file -var adapterName = 'pg'; -var config = require(configFile)[adapterName]; - -// Set up the connection -var pg = require(adapterName); -var connection = new pg.Client(config.conn); -connection.connect(function(err) { - if (err) { - throw new Error(err); - } -}); - -// Set up the query builder object -var nodeQuery = require('../../lib/NodeQuery'); -var qb = nodeQuery.init('pg', connection); - - -// Set up the test base -testBase._setUp(qb, function(test, err, result) { - if (err) { - console.error('SQL syntax error', err); - } - - test.ok(result, 'pg: Valid result for generated query'); - test.done(); -}); - -tests['nodeQuery.getQuery = nodeQuery.init'] = function(test) { - test.expect(1); - test.deepEqual(qb, nodeQuery.getQuery(), "getQuery returns same object"); - test.done(); -}; - -tests["pg adapter with query builder"] = function(test) { - test.expect(1); - test.ok(testBase.qb); - - // Close the db connection - connection.end(); - test.done(); -}; - - -module.exports = tests; \ No newline at end of file diff --git a/tests/base_test.js b/tests/base_test.js deleted file mode 100755 index 8406519..0000000 --- a/tests/base_test.js +++ /dev/null @@ -1,42 +0,0 @@ -"use strict"; - -var modules = { - helpers: require('../lib/helpers'), - driver: require('../lib/DriverBase'), - qb: require('../lib/QueryBuilder'), - 'node-query': require('../lib/NodeQuery'), - 'state': require('../lib/State'), - 'drivers/pg': require('../lib/drivers/Pg'), - 'drivers/mysql': require('../lib/drivers/Mysql'), - 'drivers/sqlite': require('../lib/drivers/Sqlite'), - 'adapters/mysql': require('../lib/adapters/mysql'), - 'adapters/mysql2': require('../lib/adapters/mysql2'), - 'adapters/pg': require('../lib/adapters/pg'), - 'adapters/dblite': require('../lib/adapters/dblite') -}; - -module.exports = { - 'Sanity check': function (test) { - test.expect(modules.length); - - Object.keys(modules).forEach(function(mod) { - test.ok(modules[mod], mod + " module is sane"); - }); - - test.done(); - }, - 'NodeQuery.getQuery with no instance': function(test) { - test.expect(1); - test.throws(function() { - nodeQuery.getQuery(); - }, Error, "No query builder instance if none created"); - test.done(); - }, - 'Invalid driver type': function(test) { - test.expect(1); - test.throws(function() { - nodeQuery.init('foo', {}, 'bar'); - }, Error, "Bad driver throws exception"); - test.done(); - } -}; \ No newline at end of file diff --git a/tests/helpers_test.js b/tests/helpers_test.js deleted file mode 100644 index f96be15..0000000 --- a/tests/helpers_test.js +++ /dev/null @@ -1,87 +0,0 @@ -'use strict'; - -var helpers = require('../lib/helpers'); - -var helperTests = { - 'Type checking method tests' : { - "Object wrappers are listed as their native type": function(test) { - test.deepEqual('boolean', helpers.type(new Boolean(true)), "Boolean Wrapper returns 'boolean' not 'object'"); - test.deepEqual('number', helpers.type(new Number(4867)), "Number Wrapper returns 'number' not 'object"); - test.deepEqual('string', helpers.type(new String("Foo")), "String Wrapper returns 'string' not 'object'"); - test.done(); - } - }, - 'is..Method tests exist' : function(test) { - test.expect(11); - - var types = ['Null','Undefined','Object','Array','String','Number','Boolean','Function','RegExp','NaN','Infinite']; - - types.forEach(function(type) { - test.ok(helpers['is' + type], 'is' + type + ' method exists'); - }); - - test.done(); - }, - 'isNaN': function(test) { - test.expect(2); - test.equal(helpers.type(0 / 0), 'nan'); - test.deepEqual(helpers.isNaN(0 / 0), true); - - test.done(); - }, - 'isInfinity': function(test) { - test.expect(2); - - test.equal(helpers.type(1/0), 'infinity'); - test.deepEqual(helpers.isInfinite(1/0), true); - test.done(); - }, - 'stringTrim': function(test) { - test.expect(1); - - var orig = [' x y ', 'z ', ' q']; - var ret = ['x y', 'z', 'q']; - - test.deepEqual(ret, orig.map(helpers.stringTrim)); - - test.done(); - }, - 'arrayPluck': function(test) { - test.expect(3); - - var orig = [{ - foo: 1 - },{ - foo: 2, - bar: 10 - },{ - foo: 3, - bar: 15 - }]; - - test.deepEqual([1,2,3], helpers.arrayPluck(orig, 'foo'), 'Finding members in all objects'); - test.deepEqual([10,15], helpers.arrayPluck(orig, 'bar'), 'Some members are missing in some objects'); - - // Empty case - test.deepEqual([], helpers.arrayPluck([], 'apple')); - - test.done(); - }, - 'regexInArray': function(test) { - var orig = ['apple', ' string ', 6, 4, 7]; - - test.expect(6); - - test.equal(false, helpers.regexInArray(orig, /\$/), 'Dollar sign is not in any of the array items'); - test.equal(true, helpers.regexInArray(orig, /^ ?string/), "' string ' matches /^ ?string/"); - test.equal(true, helpers.regexInArray(orig, /APPLE/i), "'apple' matches /APPLE/i"); - test.equal(false, helpers.regexInArray(orig, /5/), 'None of the numbers in the array match /5/'); - test.equal(false, helpers.regexInArray(5, /5/), 'First argument is not an array'); - test.equal(false, helpers.regexInArray([], /.*/), 'Array is empty'); - - test.done(); - } -}; - - -module.exports = helperTests; \ No newline at end of file diff --git a/tests/query-builder-base.js b/tests/query-builder-base.js deleted file mode 100644 index 8349236..0000000 --- a/tests/query-builder-base.js +++ /dev/null @@ -1,526 +0,0 @@ -'use strict'; - -var helpers = require('../lib/helpers'); -var State = require('../lib/State'); - -module.exports = (function QueryBuilderTestBase() { - - // That 'new' keyword is annoying - if ( ! (this instanceof QueryBuilderTestBase)) return new QueryBuilderTestBase(); - - var base = {}; - - /** - * Inject the appropriate driver and adapter for the test suite - * - * @param {Object} qb - The adapter-specific query builder object - * @param {Function} callback - The test callback - * @return void - */ - this._setUp = function(qb, callback) { - base.qb = qb; - base.testCallback = callback; - - this.qb = base.qb; - }; - - /** - * Generic query builder tests - */ - this.tests = { - // ! Get tests - 'Get tests' : { - 'Get with function': function(test) { - test.expect(1); - base.qb.select('id, COUNT(id) as count') - .from('create_test') - .groupBy('id') - .get(base.testCallback.bind(this, test)); - }, - 'Basic select all get': function(test) { - test.expect(1); - base.qb.get('create_test', base.testCallback.bind(this, test)); - }, - 'Basic select all with from': function(test) { - test.expect(1); - base.qb.from('create_test') - .get(base.testCallback.bind(this, test)); - }, - 'Get with limit': function(test) { - test.expect(1); - base.qb.get('create_test', 2, base.testCallback.bind(this, test)); - }, - 'Get with limit and offset': function(test) { - test.expect(1); - base.qb.get('create_test', 2, 1, base.testCallback.bind(this, test)); - }, - 'Test get with having': function(test) { - test.expect(1); - base.qb.select('id') - .from('create_test') - .groupBy('id') - .having({'id >':1}) - .having('id !=', 3) - .having('id', 900) - .get(base.testCallback.bind(this, test)); - }, - "Test get with 'orHaving'": function(test) { - test.expect(1); - base.qb.select('id') - .from('create_test') - .groupBy('id') - .having({'id >':1}) - .orHaving('id !=', 3) - .get(base.testCallback.bind(this, test)); - } - }, - // ! Select tests - 'Select tests' : { - 'Select where get': function(test) { - test.expect(1); - base.qb.select(['id', 'key as k', 'val']) - .where('id >', 1) - .where('id <', 900) - .get('create_test', 2, 1, base.testCallback.bind(this, test)); - }, - 'Select where get 2': function(test) { - test.expect(1); - base.qb.select('id, key as k, val') - .where('id !=', 1) - .get('create_test', 2, 1, base.testCallback.bind(this, test)); - }, - 'Multi Order By': function(test) { - test.expect(1); - base.qb.from('create_test') - .orderBy('id, key') - .get(base.testCallback.bind(this, test)); - }, - 'Select get': function(test) { - test.expect(1); - base.qb.select('id, key as k, val') - .get('create_test', 2, 1, base.testCallback.bind(this, test)); - }, - 'Select from get': function(test) { - test.expect(1); - base.qb.select('id, key as k, val') - .from('create_test ct') - .where('id >', 1) - .get(base.testCallback.bind(this, test)); - }, - 'Select from limit get': function(test) { - test.expect(1); - base.qb.select('id, key as k, val') - .from('create_test ct') - .where('id >', 1) - .limit(3) - .get(base.testCallback.bind(this, test)); - }, - 'Select where IS NOT NULL': function(test) { - test.expect(1); - base.qb.select('id', 'key as k', 'val') - .from('create_test ct') - .whereIsNotNull('id') - .get(base.testCallback.bind(this, test)); - }, - 'Select where IS NULL': function(test) { - test.expect(1); - base.qb.select('id', 'key as k', 'val') - .from('create_test ct') - .whereIsNull('id') - .get(base.testCallback.bind(this, test)); - }, - 'Select where OR IS NOT NULL': function(test) { - test.expect(1); - base.qb.select('id', 'key as k', 'val') - .from('create_test ct') - .whereIsNull('id') - .orWhereIsNotNull('id') - .get(base.testCallback.bind(this, test)); - }, - 'Select where OR IS NULL': function(test) { - test.expect(1); - base.qb.select('id', 'key as k', 'val') - .from('create_test ct') - .where('id', 3) - .orWhereIsNull('id') - .get(base.testCallback.bind(this, test)); - }, - 'Select with string where value': function(test) { - test.expect(1); - base.qb.select('id','key as k', 'val') - .from('create_test ct') - .where('id > 3') - .get(base.testCallback.bind(this, test)); - }, - 'Select with function and argument in WHERE clause': function(test) { - test.expect(1); - base.qb.select('id') - .from('create_test') - .where('id', 'CEILING(SQRT(88))') - .get(base.testCallback.bind(this, test)); - } - }, - // ! Grouping tests - 'Grouping tests' : { - 'Using grouping method': function(test) { - test.expect(1); - base.qb.select('id, key as k, val') - .from('create_test') - .groupStart() - .where('id >', 1) - .where('id <', 900) - .groupEnd() - .limit(2, 1) - .get(base.testCallback.bind(this, test)); - }, - 'Using where first grouping': function(test) { - test.expect(1); - base.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(base.testCallback.bind(this, test)); - }, - 'Using or grouping method': function(test) { - test.expect(1); - base.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(base.testCallback.bind(this, test)); - }, - 'Using or not grouping method': function(test) { - test.expect(1); - base.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(base.testCallback.bind(this, test)); - } - }, - // ! Where in tests - 'Where in tests' : { - 'Where in': function(test) { - test.expect(1); - base.qb.from('create_test') - .whereIn('id', [0, 6, 56, 563, 341]) - .get(base.testCallback.bind(this, test)); - }, - 'Or Where in': function(test) { - test.expect(1); - base.qb.from('create_test') - .where('key', 'false') - .orWhereIn('id', [0, 6, 56, 563, 341]) - .get(base.testCallback.bind(this, test)); - }, - 'Where Not in': function(test) { - test.expect(1); - base.qb.from('create_test') - .where('key', 'false') - .whereNotIn('id', [0, 6, 56, 563, 341]) - .get(base.testCallback.bind(this, test)); - }, - 'Or Where Not in': function(test) { - test.expect(1); - base.qb.from('create_test') - .where('key', 'false') - .orWhereNotIn('id', [0, 6, 56, 563, 341]) - .get(base.testCallback.bind(this, test)); - } - }, - // ! Query modifier tests - 'Query modifier tests': { - 'Order By': function(test) { - test.expect(1); - base.qb.select('id, key as k, val') - .from('create_test') - .where('id >', 0) - .where('id <', 9000) - .orderBy('id', 'DESC') - .orderBy('k', "ASC") - .limit(5, 2) - .get(base.testCallback.bind(this, test)); - }, - 'Group by': function(test) { - test.expect(1); - base.qb.select('id, key as k, val') - .from('create_test') - .where('id >', 0) - .where('id <', 9000) - .groupBy('k') - .groupBy(['id', 'val']) - .orderBy('id', 'DESC') - .orderBy('k', "ASC") - .limit(5, 2) - .get(base.testCallback.bind(this, test)); - }, - 'Or Where': function(test) { - test.expect(1); - base.qb.select('id, key as k, val') - .from('create_test') - .where(' id ', 1) - .orWhere('key >', 0) - .limit(2, 1) - .get(base.testCallback.bind(this, test)); - }, - 'Like' : function(test) { - test.expect(1); - base.qb.from('create_test') - .like('key', 'og') - .get(base.testCallback.bind(this, test)); - }, - 'Or Like': function(test) { - test.expect(1); - base.qb.from('create_test') - .like('key', 'og') - .orLike('key', 'val') - .get(base.testCallback.bind(this, test)); - }, - 'Not Like': function(test) { - test.expect(1); - base.qb.from('create_test') - .like('key', 'og', 'before') - .notLike('key', 'val') - .get(base.testCallback.bind(this, test)); - }, - 'Or Not Like': function(test) { - test.expect(1); - base.qb.from('create_test') - .like('key', 'og', 'before') - .orNotLike('key', 'val') - .get(base.testCallback.bind(this, test)); - }, - 'Like Before': function(test) { - test.expect(1); - base.qb.from('create_test') - .like('key', 'og', 'before') - .get(base.testCallback.bind(this, test)); - }, - 'Like After': function(test) { - test.expect(1); - base.qb.from('create_test') - .like('key', 'og', 'after') - .get(base.testCallback.bind(this, test)); - }, - 'Basic Join': function(test) { - test.expect(1); - base.qb.from('create_test ct') - .join('create_join cj', 'cj.id=ct.id') - .get(base.testCallback.bind(this, test)); - }, - 'Left Join': function(test) { - test.expect(1); - base.qb.from('create_test ct') - .join('create_join cj', 'cj.id=ct.id', 'left') - .get(base.testCallback.bind(this, test)); - }, - 'InnerJoin': function(test) { - test.expect(1); - base.qb.from('create_test ct') - .join('create_join cj', 'cj.id=ct.id', 'inner') - .get(base.testCallback.bind(this, test)); - }, - 'Join with multiple where values': function(test) { - test.expect(1); - base.qb.from('create_test ct') - .join('create_join cj', 'cj.id=ct.id', 'inner') - .where({ - 'ct.id <': 3, - 'ct.key': 'foo' - }) - .get(base.testCallback.bind(this, test)); - } - }, - // ! DB Update test - 'DB update tests' : { - setUp: function(callback) { - var sql = base.qb.driver.truncate('create_test'); - base.qb.adapter.execute(sql, function(err, result) { - callback(); - }); - }, - tearDown: function(callback) { - callback(); - }, - 'Test Insert': function(test) { - test.expect(1); - base.qb.set('id', 98) - .set('key', "84") - .set('val', new Buffer("120")) - .insert('create_test', base.testCallback.bind(this, test)); - }, - 'Test Insert Object': function(test) { - test.expect(1); - base.qb.insert('create_test', { - id: 587, - key: 1, - val: new Buffer('2') - }, base.testCallback.bind(this, test)); - }, - 'Test Insert Batch': function(test) { - test.expect(1); - var 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') - }]; - - base.qb.insertBatch('create_test', data, base.testCallback.bind(this, test)); - }, - 'Test Update': function(test) { - test.expect(1); - base.qb.where('id', 7) - .update('create_test', { - id: 7, - key: 'gogle', - val: new Buffer('non-word') - }, base.testCallback.bind(this, test)); - }, - 'Test set Array Update': function(test) { - test.expect(1); - var object = { - id: 22, - key: 'gogle', - val: new Buffer('non-word') - }; - - base.qb.set(object) - .where('id', 22) - .update('create_test', base.testCallback.bind(this, test)); - }, - 'Test where set update': function(test) { - test.expect(1); - base.qb.where('id', 36) - .set('id', 36) - .set('key', 'gogle') - .set('val', new Buffer('non-word')) - .update('create_test', base.testCallback.bind(this, test)); - }, - 'Test delete': function(test) { - test.expect(1); - base.qb.delete('create_test', {id: 5}, base.testCallback.bind(this, test)); - }, - 'delete with where': function(test) { - test.expect(1); - base.qb.where('id', 5) - .delete('create_test', base.testCallback.bind(this, test)); - }, - 'Delete multiple where values': function(test) { - test.expect(1); - base.qb.delete('create_test', { - id: 5, - key: 'gogle' - }, base.testCallback.bind(this, test)); - } - }, - // ! Get compiled tests - 'Get compiled tests' : { - 'select': function(test) { - test.expect(1); - var string = base.qb.select('id') - .from('create_test') - .getCompiledSelect(true); - - test.equal(true, helpers.isString(string)); - - test.done(); - }, - 'select from': function(test) { - test.expect(1); - var string = base.qb.select('id') - .getCompiledSelect('create_test', true); - - test.equal(true, helpers.isString(string)); - - test.done(); - }, - 'insert': function(test) { - test.expect(1); - - var string = base.qb.set('id', 3) - .getCompiledInsert('create_test'); - - test.equal(true, helpers.isString(string)); - - test.done(); - }, - 'update': function(test) { - test.expect(1); - - var string = base.qb.set('id', 3) - .where('id', 5) - .getCompiledUpdate('create_test'); - - test.equal(true, helpers.isString(string)); - - test.done(); - }, - 'delete': function(test) { - test.expect(1); - - var string = base.qb.where('id', 5) - .getCompiledDelete('create_test'); - - test.equal(true, helpers.isString(string)); - - test.done(); - } - }, - // ! Misc tests - 'Misc tests' : { - 'Get State': function(test) { - test.expect(1); - - base.qb.select('foo') - .from('bar') - .where('baz', 'foobar'); - - var state = new State(); - - test.notDeepEqual(JSON.stringify(state), JSON.stringify(base.qb.getState())); - test.done(); - }, - 'Reset State': function(test) { - test.expect(1); - - base.qb.select('foo') - .from('bar') - .where('baz', 'foobar'); - - base.qb.resetQuery(); - - var state = new State(); - - test.deepEqual(state, base.qb.getState()); - - test.done(); - } - } - }; - - return this; -}()); \ No newline at end of file diff --git a/tests/query-parser_test.js b/tests/query-parser_test.js deleted file mode 100644 index a7fd249..0000000 --- a/tests/query-parser_test.js +++ /dev/null @@ -1,178 +0,0 @@ -'use strict'; - -// Use the base driver as a mock for testing -var getArgs = require('getargs'); -var helpers = require('../lib/helpers'); -var driver = require('../lib/DriverBase'); - -var p = require('../lib/QueryParser'); -var parser = new p(driver); - -var State = require('../lib/State'); - -// Simulate query builder state -var state = new State(); - -var mixedSet = function(/* $varName, $valType, $key, [$val] */) { - var args = getArgs('$varName:string, $valType:string, $key:object|string|number, [$val]', arguments); - - var 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(function(k) { - // If a single value for the return - if (['key','value'].indexOf(args.$valType) !== -1) - { - var pushVal = (args.$valType === 'key') ? k : obj[k]; - state[args.$varName].push(pushVal); - } - else - { - state[args.$varName][k] = obj[k]; - } - }); - - - return state[args.$varName]; -} - -var whereMock = function() { - var args = getArgs('key:string|object, [val]', arguments); - - state.whereMap = []; - state.whereValues = []; - - mixedSet('rawWhereValues', 'value', args.key, args.val); - mixedSet('whereMap', 'both', args.key, args.val); -} - -// ----------------------------------------------------------------------------- -// ! Start Tests -// ----------------------------------------------------------------------------- - -var tests = { - 'Has operator tests': { - 'Has operator': function(test) { - var matches = parser.hasOperator('foo <> 2'); - test.deepEqual(['<>'], matches); - test.done(); - }, - 'Has no operator': function(test) { - var matches = parser.hasOperator('foo'); - test.equal(null, matches); - test.done(); - } - }, - 'Where parser tests': { - 'Has function full string': function(test) { - test.expect(1); - whereMock('time < SUM(FOO(BAR()))'); - var result = parser.parseWhere(driver, state); - test.deepEqual(['"time" < SUM(FOO(BAR()))'], state.whereMap); - - test.done(); - }, - 'Has function key/val': function(test) { - test.expect(1); - var map = whereMock('time <', 'SUM(FOO(BAR()))'); - state = parser.parseWhere(driver, state); - test.deepEqual(['"time" < SUM(FOO(BAR()))'], state.whereMap); - - test.done(); - }, - 'Has function key/val object': function(test) { - test.expect(1); - var map = whereMock({ - 'time <': "SUM(FOO(BAR('x')))" - }); - state = parser.parseWhere(driver, state); - test.deepEqual(['"time" < SUM(FOO(BAR(\'x\')))'], state.whereMap); - - test.done(); - }, - 'Has literal value': function(test) { - test.expect(2); - var map = whereMock({ - 'foo': 3 - }); - state = parser.parseWhere(driver, state); - test.deepEqual(['"foo" = ?'], state.whereMap); - test.deepEqual(['3'], state.whereValues); - - test.done(); - }, - 'Has multiple literal values': function(test) { - test.expect(2); - var map = whereMock({ - foo: 3, - bar: 5 - }); - state = parser.parseWhere(driver, state); - test.deepEqual(['"foo" = ?', '"bar" = ?'], state.whereMap); - test.deepEqual(['3','5'], state.whereValues); - - test.done(); - } - }, - 'Parse join tests' : { - 'Simple equals condition': function(test) { - var matches = parser.parseJoin('table1.field1=table2.field2'); - test.deepEqual(['table1.field1','=','table2.field2'], matches.combined); - test.done(); - }, - 'Db.table.field condition': function(test) { - var matches = parser.parseJoin('db1.table1.field1!=db2.table2.field2'); - test.deepEqual(['db1.table1.field1','!=', 'db2.table2.field2'], matches.combined); - test.done(); - }, - 'Underscore in identifier': function(test) { - var matches = parser.parseJoin('table_1.field1 = tab_le2.field_2'); - test.deepEqual(['table_1.field1', '=', 'tab_le2.field_2'], matches.combined); - test.done(); - }, - 'Function in condition': function(test) { - var matches = parser.parseJoin('table1.field1 > SUM(3+6)'); - test.deepEqual(['table1.field1', '>', 'SUM(3+6)'], matches.combined); - test.done(); - } - }, - 'Compile join tests': { - 'Simple equals condition': function(test) { - var join = parser.compileJoin('table1.field1=table2.field2'); - test.deepEqual('"table1"."field1" = "table2"."field2"', join); - test.done(); - }, - 'Db.table.field condition': function(test) { - var join = parser.compileJoin('db1.table1.field1!=db2.table2.field2'); - test.deepEqual('"db1"."table1"."field1" != "db2"."table2"."field2"', join); - test.done(); - }, - 'Underscore in identifier': function(test) { - var join = parser.compileJoin('table_1.field1 = tab_le2.field_2'); - test.deepEqual('"table_1"."field1" = "tab_le2"."field_2"', join); - test.done(); - }, - 'Function in condition': function(test) { - var join = parser.compileJoin('table1.field1 > SUM(3+6)'); - test.deepEqual('"table1"."field1" > SUM(3+6)', join); - test.done(); - } - } -}; - -module.exports = tests; \ No newline at end of file