diff --git a/docs/index.html b/docs/index.html index ed5f819..08c0a43 100644 --- a/docs/index.html +++ b/docs/index.html @@ -22,14 +22,9 @@ type='text' />
- NodeQuery - - - QueryParser + QueryBuilder - h + State - - .arrayPluck - - - .isScalar - - - .regexInArray - - - .stringTrim - - - .type - - h + _appendMap + + + helpers .arrayPluck .isScalar .regexInArray .stringTrim .type + + .upperCaseFirst + compileJoin + + constructor + @@ -111,6 +96,11 @@ class='block bold'> end + + filterMatches + @@ -121,11 +111,6 @@ class='block bold'> get - - getArgs - @@ -146,11 +131,6 @@ class='block bold'> getCompiledUpdate - - getQuery - @@ -176,16 +156,6 @@ class='block bold'> having - - helpers - - - init - @@ -332,14 +302,20 @@
-

- NodeQuery +

+ QueryBuilder

-

- QueryParser(Driver, driver) +

+ State +

+ +
+
+

+ State(Driver, driver)

Parameters

@@ -360,14 +336,42 @@
-

- State +

+ _appendMap(conjunction, string, type)

- +

Append a clause to the query map

+ +

Parameters

+
+

Returns

+ void + + + +
-

- h(o) +

+ helpers(o)

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

@@ -389,8 +393,8 @@ function name, eg isNumber

Static members

-
- +
+ .arrayPluck(arr, key) @@ -401,7 +405,7 @@ function name, eg isNumber

-

+

arrayPluck(arr, key)

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

@@ -432,8 +436,8 @@ function name, eg isNumber

-
- +
+ .isScalar(obj) @@ -444,7 +448,7 @@ function name, eg isNumber

-

+

isScalar(obj)

Determine whether an object is scalar

@@ -467,8 +471,8 @@ function name, eg isNumber

-
- +
+ .regexInArray(arr, pattern) @@ -480,7 +484,7 @@ in the passed array

-

+

regexInArray(arr, pattern)

Determine if a value matching the passed regular expression is @@ -513,8 +517,8 @@ in the passed array

-
- +
+ .stringTrim(str) @@ -525,7 +529,7 @@ in the passed array

-

+

stringTrim(str)

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

@@ -550,8 +554,8 @@ in the passed array

-
- +
+ .type(o) @@ -562,7 +566,7 @@ in the passed array

-

+

type(o)

Get the type of the variable passed

@@ -585,203 +589,26 @@ in the passed array

-
-
-

- h(str) -

- -

Parameters

-
    -
  • str - : - - - -
  • -
-

Static members

-
- +
+ - .arrayPluck(arr, key) + .upperCaseFirst(str) -

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

+

Make the first letter of the string uppercase

-

- arrayPluck(arr, key) +

+ upperCaseFirst(str)

-

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

- -

Parameters

-
    -
  • Array arr - : - -

    The array of objects to search

    - -
    -
  • -
  • String key - : - -

    The key of the object to get

    - -
    -
  • -
-

Returns

- Array - - - - -
-
-
-
- - - .isScalar(obj) - - -

Determine whether an object is scalar

- -
-
-
-
-

- isScalar(obj) -

-

Determine whether an object is scalar

- -

Parameters

-
    -
  • mixed obj - : - - - -
  • -
-

Returns

- bool - - - - -
-
-
-
- - - .regexInArray(arr, pattern) - - -

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

- -
-
-
-
-

- regexInArray(arr, pattern) -

-

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

- -

Parameters

-
    -
  • Array arr - : - -

    The array to search

    - -
    -
  • -
  • RegExp pattern - : - -

    The pattern to match

    - -
    -
  • -
-

Returns

- Boolean - : - -

If an array item matches the pattern

- -
-
-
-
-
- - - .stringTrim(str) - - -

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

- -
-
-
-
-

- stringTrim(str) -

-

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

+

Make the first letter of the string uppercase

Parameters

  • String str - : - -

    The string to trim

    - -
    -
  • -
-

Returns

- String - : - -

The trimmed string

- -
-
-
-
-
- - - .type(o) - - -

Get the type of the variable passed

- -
-
-
-
-

- type(o) -

-

Get the type of the variable passed

- -

Parameters

-
    -
  • mixed o : @@ -822,6 +649,34 @@ in the passed array

+
+

+ constructor(Driver, driver) +

+ +

Parameters

+
    +
  • Driver + : + +

    The driver object for the database in use

    + +
    +
  • +
  • driver + : + + + +
  • +
+

Returns

+ void + + + + +

delete(table, [where], callback) @@ -853,11 +708,10 @@ in the passed array

Returns

- - : + void + -

void

- +
@@ -867,11 +721,32 @@ in the passed array

Closes the database connection for the current adapter

Returns

- - : + void + -

void

+ +
+
+
+

+ filterMatches(array) +

+

Filter matched patterns

+

Parameters

+
    +
  • Array array + : + + + +
  • +
+

Returns

+ Array or + + +
@@ -891,11 +766,10 @@ in the passed array

Returns

- - : + QueryBuilder + -

this

- +
@@ -936,19 +810,12 @@ in the passed array

Returns

- - : + void + -

void

- +
-
-

- getArgs -

- -

getCompiledDelete(table, [reset]) @@ -1036,11 +903,10 @@ in the passed array

Returns

- - : + String + -

String

- +
@@ -1074,19 +940,6 @@ in the passed array

-
-

- getQuery -

-

Return an existing query builder instance

- -

Returns

- queryBuilder - - - - -

groupBy(field) @@ -1103,11 +956,10 @@ in the passed array

Returns

- - : + QueryBuilder + -

this

- +
@@ -1117,11 +969,10 @@ in the passed array

Ends a logical grouping started with one of the groupStart methods

Returns

- - : + QueryBuilder + -

this

- +
@@ -1131,11 +982,10 @@ in the passed array

Adds an open paren to the current query for logical grouping

Returns

- - : + QueryBuilder + -

this

- +
@@ -1186,66 +1036,7 @@ If there are no matches, return null

Returns

- - : - -

this

- -
-
-
-

- helpers(str) -

- -

Parameters

-
    -
  • str - : - - - -
  • -
-
-
-

- init(drivername, driverType, connObject, [connLib]) -

-

Create a query builder object

- -

Parameters

-
    -
  • String drivername - : - -

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

    - -
    -
  • -
  • driverType - : - - - -
  • -
  • Object connObject - : - -

    A connection object from the database library you are connecting with

    - -
    -
  • -
  • [String] connLib - : - -

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

    - -
    -
  • -
-

Returns

- queryBuilder + QueryBuilder @@ -1282,11 +1073,10 @@ If there are no matches, return null

Returns

- - : + void + -

void

- +
@@ -1320,11 +1110,10 @@ If there are no matches, return null

Returns

- - : + void + -

void

- +

Examples

query.insertBatch('foo',[{id:1,val:'bar'},{id:2,val:'baz'}], callbackFunction);
@@ -1361,11 +1150,10 @@ If there are no matches, return null

Returns

- - : + QueryBuilder + -

this

- +
@@ -1400,11 +1188,10 @@ If there are no matches, return null

Returns

- - : + QueryBuilder + -

this

- +
@@ -1431,11 +1218,10 @@ If there are no matches, return null

Returns

- - : + QueryBuilder + -

this

- +
@@ -1470,11 +1256,10 @@ If there are no matches, return null

Returns

- - : + QueryBuilder + -

this

- +
@@ -1485,11 +1270,10 @@ If there are no matches, return null

prefixed with 'OR'

Returns

- - : + QueryBuilder + -

this

- +
@@ -1516,11 +1300,10 @@ prefixed with 'OR'

Returns

- - : + QueryBuilder + -

this

- +
@@ -1555,11 +1338,10 @@ prefixed with 'OR'

Returns

- - : + QueryBuilder + -

this

- +
@@ -1570,11 +1352,10 @@ prefixed with 'OR'

prefixed with 'OR NOT'

Returns

- - : + QueryBuilder + -

this

- +
@@ -1609,11 +1390,10 @@ prefixed with 'OR NOT'

Returns

- - : + QueryBuilder + -

this

- +
@@ -1640,11 +1420,10 @@ prefixed with 'OR NOT'

Returns

- - : + QueryBuilder + -

this

- +
@@ -1671,11 +1450,10 @@ prefixed with 'OR NOT'

Returns

- - : + QueryBuilder + -

this

- +
@@ -1694,11 +1472,10 @@ prefixed with 'OR NOT'

Returns

- - : + QueryBuilder + -

this

- +
@@ -1717,11 +1494,10 @@ prefixed with 'OR NOT'

Returns

- - : + QueryBuilder + -

this

- +
@@ -1748,11 +1524,10 @@ prefixed with 'OR NOT'

Returns

- - : + QueryBuilder + -

this

- +
@@ -1780,11 +1555,10 @@ prefixed with 'OR NOT'

Returns

- - : + QueryBuilder + -

this

- +
@@ -1817,7 +1591,7 @@ prefixed with 'OR NOT'

Parameters

    -
  • Object driver +
  • Driver driver : @@ -1845,11 +1619,10 @@ prefixed with 'OR NOT'

    Reset the object state for a new query

    Returns

    - - : + void + -

    void

    - +
@@ -1869,11 +1642,10 @@ prefixed with 'OR NOT'

Returns

- - : + QueryBuilder + -

this

- +
@@ -1900,11 +1672,10 @@ prefixed with 'OR NOT'

Returns

- - : + QueryBuilder + -

this

- +
@@ -1938,11 +1709,10 @@ prefixed with 'OR NOT'

Returns

- - : + void + -

void

- +
@@ -1969,11 +1739,10 @@ prefixed with 'OR NOT'

Returns

- - : + QueryBuilder + -

this

- +
@@ -2000,11 +1769,10 @@ prefixed with 'OR NOT'

Returns

- - : + QueryBuilder + -

this

- +
@@ -2023,11 +1791,10 @@ prefixed with 'OR NOT'

Returns

- - : + QueryBuilder + -

this

- +
@@ -2047,11 +1814,10 @@ prefixed with 'OR NOT'

Returns

- - : + QueryBuilder + -

this

- +
@@ -2078,11 +1844,10 @@ prefixed with 'OR NOT'

Returns

- - : + QueryBuilder + -

this

- +
diff --git a/gulpfile.js b/gulpfile.js index 6e118ce..4011e1f 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -1,30 +1,123 @@ -var gulp = require('gulp'), +var babel = require('gulp-babel'), + concat = require('gulp-concat'), documentation = require('gulp-documentation'), + eslint = require('gulp-eslint'), + gulp = require('gulp'), + istanbul = require('gulp-babel-istanbul'), nodeunit_runner = require('gulp-nodeunit-runner'), - istanbul = require('gulp-istanbul'); + sloc = require('gulp-sloc'), + sourcemaps = require('gulp-sourcemaps'); -gulp.task('default', ['docs', 'test']); +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')); +}); -gulp.task('docs', function() { - gulp.src('./lib/node-query.js') +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'], 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', ['transpile'], function() { + gulp.src(['src/**/*.js']) + .pipe(sloc()); + gulp.src(['lib/**/*.js']) + .pipe(sloc()); +}) + +gulp.task('docs', ['transpile'], function() { + gulp.src('./src/QueryBuilder.js') .pipe(documentation({format: 'html'})) .pipe(gulp.dest('docs')); - gulp.src('./lib/node-query.js') + /*gulp.src('./lib/QueryBuilder.js') .pipe(documentation({format: 'md'})) - .pipe(gulp.dest('api-docs')); + .pipe(gulp.dest('api-docs'));*/ }); -gulp.task('pre-test', function() { +gulp.task('nodeunit', ['transpile', 'lint-tests'], function() { + return gulp.src(['tests/**/*_test.js']) + .pipe(nodeunit_runner()); +}); + +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()); + .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', ['pre-test'], function() { - return gulp.src(['tests/**/*_test.js']) - .pipe(nodeunit_runner()) - .pipe(istanbul.writeReports({ - dir: './coverage', - reporters: ['lcov', 'lcovonly', 'html', 'text'] - })); -}); \ No newline at end of file +gulp.task('default', ['lint', 'sloc', 'docs', 'test']); \ No newline at end of file diff --git a/lib/Adapter.js b/lib/Adapter.js new file mode 100755 index 0000000..bd29e2a --- /dev/null +++ b/lib/Adapter.js @@ -0,0 +1,42 @@ +'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 () { + /** + * Invoke an adapter + * + * @param {Object} instance - The connection objec + * @return {void} + */ + + function Adapter(instance) { + _classCallCheck(this, Adapter); + + 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 diff --git a/lib/Adapter.js.map b/lib/Adapter.js.map new file mode 100644 index 0000000..06bc225 --- /dev/null +++ b/lib/Adapter.js.map @@ -0,0 +1 @@ +{"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 new file mode 100755 index 0000000..11ddacf --- /dev/null +++ b/lib/DriverBase.js @@ -0,0 +1,157 @@ +'use strict'; + +var _helpers = require('./helpers'); + +var _helpers2 = _interopRequireDefault(_helpers); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +/** + * 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 _quote(str) { + return _helpers2.default.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; + + if (_helpers2.default.isNumber(offset)) { + sql += " OFFSET " + offset; + } + + return sql; + }, + + /** + * Quote database table name, and set prefix + * + * @param {String} table + * @return {String} + */ + quoteTable: function quoteTable(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'); + + // Recurse for arrays of identifiiers + if (Array.isArray(str)) { + return str.map(d.quoteIdentifiers); + } + + // Handle commas + if (str.includes(',')) { + var parts = str.split(',').map(_helpers2.default.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 truncate(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 insertBatch(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; +//# sourceMappingURL=DriverBase.js.map diff --git a/lib/DriverBase.js.map b/lib/DriverBase.js.map new file mode 100644 index 0000000..fdbcf48 --- /dev/null +++ b/lib/DriverBase.js.map @@ -0,0 +1 @@ +{"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 new file mode 100644 index 0000000..3029d7a --- /dev/null +++ b/lib/DriverClass.js @@ -0,0 +1,22 @@ +'use strict'; + +var _DriverBase = require('./DriverBase'); + +var _DriverBase2 = _interopRequireDefault(_DriverBase); + +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 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 diff --git a/lib/DriverClass.js.map b/lib/DriverClass.js.map new file mode 100644 index 0000000..278b095 --- /dev/null +++ b/lib/DriverClass.js.map @@ -0,0 +1 @@ +{"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 new file mode 100755 index 0000000..2531bd4 --- /dev/null +++ b/lib/NodeQuery.js @@ -0,0 +1,103 @@ +"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; + +/** + * @module NodeQuery + */ + +var NodeQuery = (function () { + + /** + * Constructor + */ + + function NodeQuery() { + _classCallCheck(this, NodeQuery); + + 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} + */ + + _createClass(NodeQuery, [{ + key: 'init', + value: function init(driverType, connObject, connLib) { + connLib = connLib || driverType; + + 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"); + } + + return this.instance; + } + }]); + + return NodeQuery; +})(); + +; + +module.exports = new NodeQuery(); +//# sourceMappingURL=NodeQuery.js.map diff --git a/lib/NodeQuery.js.map b/lib/NodeQuery.js.map new file mode 100644 index 0000000..00d5f09 --- /dev/null +++ b/lib/NodeQuery.js.map @@ -0,0 +1 @@ +{"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 new file mode 100755 index 0000000..3efc825 --- /dev/null +++ b/lib/QueryBuilder.js @@ -0,0 +1,1063 @@ +'use strict' + +/** @module QueryBuilder */ +; + +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 () { + /* + * 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); + + this.driver = driver; + this.adapter = adapter; + this.parser = new _QueryParser2.default(this.driver); + this.state = new _State2.default(); + } + + /** + * Complete the sql building based on the type provided + * + * @param {String} type + * @param {String} table + * @private + * @return {String} + */ + + _createClass(QueryBuilder, [{ + key: '_compile', + value: function _compile(type, table) { + var _this = this; + + // 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 + }); + }); + + // 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); + } + + //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(); + } + + // ---------------------------------------------------------------------------- + // ! Miscellaneous Methods + // ---------------------------------------------------------------------------- + + /** + * Reset the object state for a new query + * + * @memberOf QueryBuilder + * @return {void} + */ + + }, { + key: 'resetQuery', + value: function resetQuery() { + this._resetState(); + } + + /** + * Returns the current class state for testing or other purposes + * + * @private + * @return {Object} + */ + + }, { + key: 'getState', + value: function getState() { + return this.state; + } + + /** + * Closes the database connection for the current adapter + * + * @return {void} + */ + + }, { + key: 'end', + value: function 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} + */ + + }, { + 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); + } + }); + + var safeArray = this.driver.quoteIdentifiers(fields); + + // Join the strings back together + safeArray.forEach(function (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} + */ + + }, { + 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; + } + + /** + * 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} + */ + + }, { + key: 'like', + value: function 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} + */ + + }, { + 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} + */ + + }, { + key: 'orLike', + value: function 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} + */ + + }, { + key: 'orNotLike', + value: function 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} + */ + + }, { + key: 'having', + value: function having() /*key, [val]*/{ + var args = (0, _getargs2.default)('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} + */ + + }, { + key: 'orHaving', + value: function orHaving() /*key, [val]*/{ + var args = (0, _getargs2.default)('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} + */ + + }, { + key: 'where', + value: function 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} + */ + + }, { + 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)); + } + + 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} + */ + + }, { + 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); + } + + 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} + */ + + }, { + key: 'insert', + value: function insert() /* table, data, callback */{ + var args = (0, _getargs2.default)('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} + */ + + }, { + 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); + } + + /** + * 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} + */ + + }, { + key: 'update', + value: function update() /*table, data, callback*/{ + var args = (0, _getargs2.default)('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} + */ + + }, { + key: 'delete', + value: function _delete() /*table, [where], callback*/{ + var args = (0, _getargs2.default)('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} + */ + + }, { + 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); + } + + /** + * 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} + */ + + }, { + key: 'getCompiledInsert', + value: function 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} + */ + + }, { + key: 'getCompiledUpdate', + value: function 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} + */ + + }, { + key: 'getCompiledDelete', + value: function getCompiledDelete(table, reset) { + return this._getCompile('delete', this.driver.quoteTable(table), reset); + } + }]); + + return QueryBuilder; +})(); +//# sourceMappingURL=QueryBuilder.js.map diff --git a/lib/QueryBuilder.js.map b/lib/QueryBuilder.js.map new file mode 100644 index 0000000..b9903ee --- /dev/null +++ b/lib/QueryBuilder.js.map @@ -0,0 +1 @@ +{"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 new file mode 100644 index 0000000..13bd592 --- /dev/null +++ b/lib/QueryParser.js @@ -0,0 +1,278 @@ +'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"); } } + +// -------------------------------------------------------------------------- + +/** + * @constructor + * @param {Driver} - The driver object for the database in use + * @module query-parser + */ +module.exports = (function () { + /** + * @constructor + * @param {Driver} - The driver object for the database in use + * @return {void} + */ + + function QueryParser(driver) { + _classCallCheck(this, QueryParser); + + this.driver = driver; + + var 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} + */ + + _createClass(QueryParser, [{ + key: 'filterMatches', + value: function filterMatches(array) { + var 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(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} + */ + + }, { + key: 'hasOperator', + value: function hasOperator(string) { + return this.filterMatches(string.match(this.matchPatterns.operator)); + } + + /** + * Tokenize the sql into parts for additional processing + * + * @param {String} sql + * @return {Object} + */ + + }, { + key: 'parseJoin', + value: function parseJoin(sql) { + var _this = this; + + var matches = {}; + var 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(function (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 + */ + + }, { + key: 'compileJoin', + value: function compileJoin(condition) { + var _this2 = this; + + var parts = this.parseJoin(condition); + var count = parts.identifiers.length; + var i = undefined; + + // 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); + } + }); + + 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 + */ + + }, { + 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]; + } + + // Separate the clause into separate pieces + var parts = _this3.parseJoin(fullClause); + + // 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; + + // 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(function (item) { + var isInCombinedMatches = parts.combined.indexOf(item) !== -1; + var isNotInBlackList = _this3.identifierBlacklist.indexOf(item.toLowerCase()) === -1; + + return isInCombinedMatches && isNotInBlackList; + }, _this3); + + // 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 diff --git a/lib/QueryParser.js.map b/lib/QueryParser.js.map new file mode 100644 index 0000000..5a11709 --- /dev/null +++ b/lib/QueryParser.js.map @@ -0,0 +1 @@ +{"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 new file mode 100644 index 0000000..f290f7d --- /dev/null +++ b/lib/State.js @@ -0,0 +1,34 @@ +'use strict' + +/** @module State */ +; + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +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 diff --git a/lib/State.js.map b/lib/State.js.map new file mode 100644 index 0000000..25d0f9b --- /dev/null +++ b/lib/State.js.map @@ -0,0 +1 @@ +{"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 5a77f23..3640241 100644 --- a/lib/adapters/dblite.js +++ b/lib/adapters/dblite.js @@ -1,29 +1,50 @@ 'use strict'; -var adapter = require('../adapter'), - getArgs = require('getargs'); +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 _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 */ -var Dblite = function(instance) { +module.exports = (function (_Adapter) { + _inherits(dblite, _Adapter); - // That 'new' keyword is annoying - if ( ! (this instanceof Dblite)) return new Dblite(instance); + function dblite() { + _classCallCheck(this, dblite); - /** - * 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 - */ - adapter.execute = function(/*sql, params, callback*/) { - var args = getArgs('sql:string, [params]:array, callback:function', arguments); + return _possibleConstructorReturn(this, Object.getPrototypeOf(dblite).apply(this, arguments)); + } - instance.query(args.sql, args.params, args.callback); - }; + _createClass(dblite, [{ + key: 'execute', - return 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 + */ + 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); + } + }]); -module.exports = Dblite; \ No newline at end of file + return dblite; +})(_Adapter3.default); +//# sourceMappingURL=dblite.js.map diff --git a/lib/adapters/dblite.js.map b/lib/adapters/dblite.js.map new file mode 100644 index 0000000..9579bdd --- /dev/null +++ b/lib/adapters/dblite.js.map @@ -0,0 +1 @@ +{"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 55135a6..9e934ab 100644 --- a/lib/adapters/mysql.js +++ b/lib/adapters/mysql.js @@ -1,27 +1,45 @@ 'use strict'; -var adapter = require('../adapter.js'); -var conn; +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 _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 */ -var MySQL = function(instance) { +module.exports = (function (_Adapter) { + _inherits(mysql, _Adapter); - // That 'new' keyword is annoying - if ( ! (this instanceof MySQL)) return new MySQL(instance); + function mysql() { + _classCallCheck(this, mysql); - /** - * 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 - */ - adapter.execute = function(sql, params, callback) { - instance.query.apply(instance, arguments); - }; + return _possibleConstructorReturn(this, Object.getPrototypeOf(mysql).apply(this, arguments)); + } - return adapter; -}; + _createClass(mysql, [{ + key: 'execute', -module.exports = MySQL; \ No newline at end of file + /** + * 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 diff --git a/lib/adapters/mysql.js.map b/lib/adapters/mysql.js.map new file mode 100644 index 0000000..4d83827 --- /dev/null +++ b/lib/adapters/mysql.js.map @@ -0,0 +1 @@ +{"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 051c310..ed73744 100644 --- a/lib/adapters/mysql2.js +++ b/lib/adapters/mysql2.js @@ -1,26 +1,45 @@ 'use strict'; -var adapter = require('../adapter.js'); +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 _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 */ -var MySQL2 = function(instance) { +module.exports = (function (_Adapter) { + _inherits(mysql2, _Adapter); - // That 'new' keyword is annoying - if ( ! (this instanceof MySQL2)) return new MySQL2(instance); + function mysql2() { + _classCallCheck(this, mysql2); - /** - * 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 - */ - adapter.execute = function(sql, params, callback) { - instance.execute.apply(instance, arguments); - }; + return _possibleConstructorReturn(this, Object.getPrototypeOf(mysql2).apply(this, arguments)); + } - return adapter; -} + _createClass(mysql2, [{ + key: 'execute', -module.exports = MySQL2; \ No newline at end of file + /** + * 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 diff --git a/lib/adapters/mysql2.js.map b/lib/adapters/mysql2.js.map new file mode 100644 index 0000000..7be47b9 --- /dev/null +++ b/lib/adapters/mysql2.js.map @@ -0,0 +1 @@ +{"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 bd02b38..a8a2ff5 100644 --- a/lib/adapters/node-firebird.js +++ b/lib/adapters/node-firebird.js @@ -1,29 +1,50 @@ 'use strict'; -var adapter = require('../adapter'), - getArgs = require('getargs'); +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 _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 */ -var NodeFirebird = function(instance) { +module.exports = (function (_Adapter) { + _inherits(nodefirebird, _Adapter); - // That 'new' keyword is annoying - if ( ! (this instanceof NodeFirebird)) return new NodeFirebird(instance); + function nodefirebird() { + _classCallCheck(this, nodefirebird); - /** - * 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 - */ - adapter.execute = function(sql, params, callback) { - var args = getArgs('sql:string, [params], callback:function', arguments); + return _possibleConstructorReturn(this, Object.getPrototypeOf(nodefirebird).apply(this, arguments)); + } - instance.execute(args.sql, args.params, args.callback); - }; + _createClass(nodefirebird, [{ + key: 'execute', - return 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 + */ + 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); + } + }]); -module.exports = NodeFirebird; \ No newline at end of file + return nodefirebird; +})(_Adapter3.default); +//# sourceMappingURL=node-firebird.js.map diff --git a/lib/adapters/node-firebird.js.map b/lib/adapters/node-firebird.js.map new file mode 100644 index 0000000..b29163d --- /dev/null +++ b/lib/adapters/node-firebird.js.map @@ -0,0 +1 @@ +{"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 0d4d164..05a211e 100644 --- a/lib/adapters/pg.js +++ b/lib/adapters/pg.js @@ -1,36 +1,58 @@ 'use strict'; -var adapter = require('../adapter'), - getArgs = require('getargs'); +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 _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/pg */ -var Pg = function(instance) { +module.exports = (function (_Adapter) { + _inherits(pg, _Adapter); - // That 'new' keyword is annoying - if ( ! (this instanceof Pg)) return new Pg(instance); + function pg() { + _classCallCheck(this, pg); - /** - * 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 - */ - adapter.execute = function(/*sql, params, callback*/) { - var args = getArgs('sql:string, [params]:array, callback:function', arguments); + return _possibleConstructorReturn(this, Object.getPrototypeOf(pg).apply(this, 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; - }); + _createClass(pg, [{ + key: 'execute', - instance.query(args.sql, args.params, args.callback); - }; + /** + * 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); - return adapter; -} + // Replace question marks with numbered placeholders, because this adapter is different... + var count = 0; + args.sql = args.sql.replace(/\?/g, function () { + count++; + return '$' + count; + }); -module.exports = Pg; \ No newline at end of file + this.instance.query(args.sql, args.params, args.callback); + } + }]); + + return pg; +})(_Adapter3.default); +//# sourceMappingURL=pg.js.map diff --git a/lib/adapters/pg.js.map b/lib/adapters/pg.js.map new file mode 100644 index 0000000..fa33d5f --- /dev/null +++ b/lib/adapters/pg.js.map @@ -0,0 +1 @@ +{"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 new file mode 100644 index 0000000..8b9143f --- /dev/null +++ b/lib/drivers/Firebird.js @@ -0,0 +1,78 @@ +"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); + +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, { + 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} + */ + + _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); + } + + /** + * 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} + */ + + }, { + key: 'insertBatch', + value: function insertBatch() { + throw new Error("Not Implemented"); + } + }]); + + return Firebird; +})(_DriverClass2.default); + +module.exports = new Firebird(); +//# sourceMappingURL=Firebird.js.map diff --git a/lib/drivers/Firebird.js.map b/lib/drivers/Firebird.js.map new file mode 100644 index 0000000..51e756b --- /dev/null +++ b/lib/drivers/Firebird.js.map @@ -0,0 +1 @@ +{"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 new file mode 100755 index 0000000..a52450e --- /dev/null +++ b/lib/drivers/Mysql.js @@ -0,0 +1,54 @@ +"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); + +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, { + 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; + } + }]); + + return Mysql; +})(_DriverClass2.default); + +module.exports = new Mysql(); +//# sourceMappingURL=Mysql.js.map diff --git a/lib/drivers/Mysql.js.map b/lib/drivers/Mysql.js.map new file mode 100644 index 0000000..0db23ec --- /dev/null +++ b/lib/drivers/Mysql.js.map @@ -0,0 +1 @@ +{"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 new file mode 100755 index 0000000..2de962c --- /dev/null +++ b/lib/drivers/Pg.js @@ -0,0 +1,15 @@ +"use strict"; + +var _DriverClass = require("../DriverClass"); + +var _DriverClass2 = _interopRequireDefault(_DriverClass); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +/** + * Driver for PostgreSQL databases + * + * @module drivers/pg + */ +module.exports = new _DriverClass2.default(); +//# sourceMappingURL=Pg.js.map diff --git a/lib/drivers/Pg.js.map b/lib/drivers/Pg.js.map new file mode 100644 index 0000000..4537eb6 --- /dev/null +++ b/lib/drivers/Pg.js.map @@ -0,0 +1 @@ +{"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 new file mode 100644 index 0000000..c748268 --- /dev/null +++ b/lib/drivers/Sqlite.js @@ -0,0 +1,94 @@ +"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); + +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; + } + + _createClass(Sqlite, [{ + key: 'insertBatch', + value: function insertBatch(table, data) { + var _this2 = this; + + // 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); + }); + + 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)); + }); + + sql += "SELECT " + cols.join(', ') + "\n"; + + 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 diff --git a/lib/drivers/Sqlite.js.map b/lib/drivers/Sqlite.js.map new file mode 100644 index 0000000..3306c4d --- /dev/null +++ b/lib/drivers/Sqlite.js.map @@ -0,0 +1 @@ +{"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/drivers/mysql.js b/lib/drivers/mysql.js deleted file mode 100755 index 66663c5..0000000 --- a/lib/drivers/mysql.js +++ /dev/null @@ -1,31 +0,0 @@ -"use strict"; - -/** - * Driver for MySQL databases - * - * @module drivers/mysql - */ -module.exports = (function() { - delete require.cache[require.resolve('../driver')]; - var driver = require('../driver'); - var driver = require('../driver'), - helpers = require('../helpers'); - - driver.identifierStartChar = '`'; - driver.identifierEndChar = '`'; - - /** - * Override default limit method because mysql likes to be different - */ - driver.limit = function(sql, limit, offset) { - if ( ! helpers.isNumber(offset)) - { - return sql += " LIMIT " + limit; - } - - return sql += " LIMIT " + offset + "," + limit; - }; - - return driver; - -}()); \ No newline at end of file diff --git a/lib/drivers/pg.js b/lib/drivers/pg.js deleted file mode 100755 index f2a3956..0000000 --- a/lib/drivers/pg.js +++ /dev/null @@ -1,13 +0,0 @@ -"use strict"; - -/** - * Driver for PostgreSQL databases - * - * @module drivers/pg - */ -module.exports = (function() { - delete require.cache[require.resolve('../driver')]; - var driver = require('../driver'); - - return driver; -}()); \ No newline at end of file diff --git a/lib/drivers/sqlite.js b/lib/drivers/sqlite.js deleted file mode 100644 index 1725df9..0000000 --- a/lib/drivers/sqlite.js +++ /dev/null @@ -1,71 +0,0 @@ -"use strict"; - -/** - * Driver for Sqlite databases - * - * @module drivers/sqlite - */ -module.exports = (function() { - delete require.cache[require.resolve('../driver')]; - var driver = require('../driver'), - helpers = require('../helpers'); - - // Sqlite doesn't have a truncate command - driver.hasTruncate = false; - - /** - * SQL to insert a group of rows - * Override default to have better compatibility - * - * @param {String} table - The table to insert to - * @param {Array} [data] - The array of object containing data to insert - * @return {String} - */ - driver.insertBatch = function(table, data) { - - // 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); - }); - - sql += "INSERT INTO " + driver.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("'" + driver._quote(first[key]) + "' AS " + driver.quoteIdentifiers(key)); - }); - - sql += "SELECT " + cols.join(', ') + "\n"; - - 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 driver; -}()); \ No newline at end of file diff --git a/lib/helpers.js b/lib/helpers.js index 3432849..d5aefd3 100755 --- a/lib/helpers.js +++ b/lib/helpers.js @@ -1,68 +1,66 @@ -"use strict"; +"use strict" -require('es6-shim'); -/** @module helpers */ +//require('es6-shim'); -/** @alias module:helpers */ -var h = { +; +var 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(str) { + * 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(); }, /** - * 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 (o) { - var type = Object.prototype.toString.call(o).slice(8, -1).toLowerCase(); + * 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'; - } - } + // handle NaN and Infinity + if (type === 'number') { + if (isNaN(o)) { + return 'nan'; + } + if (!isFinite(o)) { + return 'infinity'; + } + } - return type; + return type; }, /** - * Determine whether an object is scalar - * - * @param {mixed} obj - * @return {bool} - */ - isScalar: function(obj) { + * Determine whether an object is scalar + * + * @param {mixed} obj + * @return {bool} + */ + isScalar: function isScalar(obj) { var scalar = ['string', 'number', 'boolean']; - return scalar.indexOf(h.type(obj)) !== -1; + 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(arr, key) { + * 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 = []; // Empty case if (arr.length === 0) return output; - arr.forEach(function(obj) { - if ( ! h.isUndefined(obj[key])) - { + arr.forEach(function (obj) { + if (!helpers.isUndefined(obj[key])) { output.push(obj[key]); } }); @@ -70,51 +68,62 @@ var h = { 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(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: function regexInArray(arr, pattern) { // Empty case(s) - if ( ! h.isArray(arr)) return false; + if (!helpers.isArray(arr)) return false; if (arr.length === 0) return false; - var i, l = arr.length; + var i = undefined, + 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; } return false; + }, + /** + * Make the first letter of the string uppercase + * + * @param {String} str + * @return {String} + */ + upperCaseFirst: function upperCaseFirst(str) { + str += ''; + var 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']; +var types = ['Null', 'Undefined', 'Object', 'Array', 'String', 'Number', 'Boolean', 'Function', 'RegExp', 'NaN', 'Infinite']; types.forEach(function (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} - */ - h['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 h.type(o) === t.toLowerCase(); - }; + return helpers.type(o) === t.toLowerCase(); + }; }); -module.exports = h; \ No newline at end of file +module.exports = helpers; +//# sourceMappingURL=helpers.js.map diff --git a/lib/helpers.js.map b/lib/helpers.js.map new file mode 100644 index 0000000..567c6aa --- /dev/null +++ b/lib/helpers.js.map @@ -0,0 +1 @@ +{"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/lib/node-query.js b/lib/node-query.js deleted file mode 100755 index da9146f..0000000 --- a/lib/node-query.js +++ /dev/null @@ -1,58 +0,0 @@ -"use strict"; - -/** @module node-query */ -var NodeQuery = function() { - - var instance = null; - - /** - * Create a query builder object - * - * @alias module:node-query - * @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} - */ - this.init = function (driverType, connObject, connLib) { - connLib = connLib || driverType; - - var fs = require('fs'), - qb = require('./query-builder'); - - var paths = { - driver: __dirname + '/drivers/' + driverType + '.js', - adapter: __dirname + '/adapters/' + connLib + '.js' - }; - - Object.keys(paths).forEach(function(type) { - if ( ! fs.existsSync(paths[type])) - { - console.log(paths[type]); - throw new Error('Selected ' + type + ' does not exist!'); - } - }); - - instance = qb(require(paths.driver), require(paths.adapter)(connObject)); - - return instance; - }; - - /** - * Return an existing query builder instance - * - * @return {queryBuilder} - */ - this.getQuery = function () { - if ( ! instance) { - throw new Error("No Query Builder instance to return"); - } - - return instance; - }; - -}; - - - -module.exports = new NodeQuery(); \ No newline at end of file diff --git a/lib/query-builder.js b/lib/query-builder.js deleted file mode 100755 index 59a8e70..0000000 --- a/lib/query-builder.js +++ /dev/null @@ -1,928 +0,0 @@ -'use strict'; - -/** @module query-builder */ -var getArgs = require('getargs'), - helpers = require('./helpers'), - State = require('./state'); - -/** - * Variables controlling the sql building - * - * @private - */ -var state = new State(); - -/* - * SQL generation object - * - * @param {driver} - The syntax driver for the database - * @param {adapter} - The database module adapter for running queries - * @constructor - */ -var QueryBuilder = function(driver, adapter) { - - // That 'new' keyword is annoying - if ( ! (this instanceof QueryBuilder)) return new QueryBuilder(driver, adapter); - - var parser = require('./query-parser')(driver); - - this.driver = driver; - this.adapter = adapter; - - /** - * "Private" methods - * - * @private - */ - var _p = { - /** - * Complete the sql building based on the type provided - * - * @param {String} type - * @param {String} table - * @private - * @return {String} - */ - compile: function (type, table) { - // Put together the basic query - var sql = _p.compileType(type, table); - - // Set each subClause - ['queryMap', 'groupString', 'orderString', 'havingMap'].forEach(function(clause) { - var param = state[clause]; - - if ( ! helpers.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 (helpers.isNumber(state.limit)) - { - sql = driver.limit(sql, state.limit, state.offset); - } - - return sql; - }, - compileType: function (type, table) { - var sql = ''; - - switch(type) { - case "insert": - var params = new Array(state.setArrayKeys.length); - params.fill('?'); - - sql = "INSERT INTO " + table + " ("; - sql += state.setArrayKeys.join(','); - sql += ") VALUES ("; - sql += params.join(',') + ')'; - break; - - case "update": - sql = "UPDATE " + table + " SET " + state.setString; - break; - - case "delete": - sql = "DELETE FROM " + table; - break; - - default: - sql = "SELECT * FROM " + state.fromString; - - // Set the select string - if (state.selectString.length > 0) - { - // Replace the star with the selected fields - sql = sql.replace('*', state.selectString); - } - break; - } - - return sql; - }, - like: function (field, val, pos, like, conj) { - field = driver.quoteIdentifiers(field); - - like = field + " " + like + " ?"; - - if (pos == 'before') - { - val = "%" + val; - } - else if (pos == 'after') - { - val = val + "%"; - } - else - { - val = "%" + val + "%"; - } - - conj = (state.queryMap.length < 1) ? ' WHERE ' : ' ' + conj + ' '; - _p.appendMap(conj, like, 'like'); - - state.whereValues.push(val); - }, - /** - * Append a clause to the query map - * - * @param {String} conjunction - * @param {String} string - * @param {String} type - * @return void - */ - appendMap: function(conjunction, string, type) { - 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: 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]; - }, - whereMixedSet: function(/*key, val*/) { - var args = getArgs('key:string|object, [val]', arguments); - - state.whereMap = []; - state.rawWhereValues = []; - - _p.mixedSet('whereMap', 'both', args.key, args.val); - _p.mixedSet('rawWhereValues', 'value', args.key, args.val); - }, - fixConjunction: function(conj) { - var lastItem = state.queryMap[state.queryMap.length - 1]; - var conjunctionList = helpers.arrayPluck(state.queryMap, 'conjunction'); - - if (state.queryMap.length === 0 || ( ! helpers.regexInArray(conjunctionList, /^ ?WHERE/i))) - { - conj = " WHERE "; - } - else if (lastItem.type === 'groupStart') - { - conj = ''; - } - else - { - conj = ' ' + conj + ' '; - } - - return conj; - }, - where: function(key, val, defaultConj) { - // Normalize key and value and insert into state.whereMap - _p.whereMixedSet(key, val); - - // Parse the where condition to account for operators, - // functions, identifiers, and literal values - state = parser.parseWhere(driver, state); - - state.whereMap.forEach(function(clause) { - var conj = _p.fixConjunction(defaultConj); - _p.appendMap(conj, clause, 'where'); - }); - - state.whereMap = {}; - }, - whereNull: function(field, stmt, conj) { - field = driver.quoteIdentifiers(field); - var item = field + ' ' + stmt; - - _p.appendMap(_p.fixConjunction(conj), item, 'whereNull'); - }, - having: function(/*key, val, conj*/) { - var 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 - _p.whereMixedSet(args.key, args.val); - - // Parse the having condition to account for operators, - // functions, identifiers, and literal values - state = parser.parseWhere(driver, state); - - state.whereMap.forEach(function(clause) { - // Put in the having map - state.havingMap.push({ - conjunction: (state.havingMap.length > 0) ? " " + args.conj + " " : ' HAVING ', - string: clause - }); - }); - - // Clear the where Map - state.whereMap = {}; - }, - whereIn: function(/*key, val, inClause, conj*/) { - var args = getArgs('key:string, val:array, inClause:string, conj:string', arguments); - - args.key = driver.quoteIdentifiers(args.key); - var params = new Array(args.val.length); - params.fill('?'); - - args.val.forEach(function(value) { - state.whereValues.push(value); - }); - - args.conj = (state.queryMap.length > 0) ? " " + args.conj + " " : ' WHERE '; - var str = args.key + " " + args.inClause + " (" + params.join(',') + ") "; - - _p.appendMap(args.conj, str, 'whereIn'); - }, - run: function(type, table, callback, sql, vals) { - - if ( ! sql) - { - sql = _p.compile(type, table); - } - - if ( ! vals) - { - vals = state.values.concat(state.whereValues); - } - -//console.log(state); -//console.log(sql); -//console.log(vals); -//console.log(callback); -//console.log('------------------------'); - - // Reset the state so another query can be built - _p.resetState(); - - // Pass the sql and values to the adapter to run on the database - adapter.execute(sql, vals, callback); - - }, - getCompile: function(type, table, reset) { - reset = reset || false; - - var sql = _p.compile(type, table); - - if (reset) _p.resetState(); - - return sql; - }, - resetState: function() { - state = new State(); - } - }; - - // ---------------------------------------------------------------------------- - // ! Miscellaneous Methods - // ---------------------------------------------------------------------------- - - /** - * Reset the object state for a new query - * - * @memberOf query-builder - * @return void - */ - this.resetQuery = function() { - _p.resetState(); - }; - - /** - * Returns the current class state for testing or other purposes - * - * @private - * @return {Object} - */ - this.getState = function() { - return state; - }; - - /** - * Closes the database connection for the current adapter - * - * @return void - */ - this.end = function() { - adapter.close(); - }; - - // ------------------------------------------------------------------------ - // ! Query Builder Methods - // ------------------------------------------------------------------------ - - /** - * Specify rows to select in the query - * - * @param {String|Array} fields - The fields to select from the current table - * @return this - */ - this.select = function(fields) { - - // Split/trim fields by comma - fields = (Array.isArray(fields)) ? fields : fields.split(",").map(helpers.stringTrim); - - // Split on 'As' - fields.forEach(function (field, index) { - if (field.match(/as/i)) - { - fields[index] = field.split(/ as /i).map(helpers.stringTrim); - } - }); - - var safeArray = driver.quoteIdentifiers(fields); - - // Join the strings back together - safeArray.forEach(function (field, index) { - if (Array.isArray(field)) - { - safeArray[index] = safeArray[index].join(' AS '); - } - }); - - 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 this - */ - this.from = function(tableName) { - // Split identifiers on spaces - var identArray = tableName.trim().split(' ').map(helpers.stringTrim); - - // Quote/prefix identifiers - identArray[0] = driver.quoteTable(identArray[0]); - identArray = driver.quoteIdentifiers(identArray); - - // Put it back together - 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 this - */ - this.like = function(field, val, pos) { - _p.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 this - */ - this.notLike = function(field, val, pos) { - _p.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 this - */ - this.orLike = function(field, val, pos) { - _p.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 this - */ - this.orNotLike = function(field, val, pos) { - _p.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 this - */ - this.having = function(/*key, [val]*/) { - var args = getArgs('key:string|object, [val]:string|number', arguments); - - _p.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 this - */ - this.orHaving = function(/*key, [val]*/) { - var args = getArgs('key:string|object, [val]:string|number', arguments); - - _p.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 this - */ - this.where = function(key, val) { - _p.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 this - */ - this.orWhere = function(key, val) { - _p.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 this - */ - this.whereIsNull = function(field) { - _p.whereNull(field, 'IS NULL', 'AND'); - return this; - } - - /** - * Specify that a field IS NOT NULL - * - * @param {String} field - * @return this - */ - this.whereIsNotNull = function(field) { - _p.whereNull(field, 'IS NOT NULL', 'AND'); - return this; - } - - /** - * Field is null prefixed with 'OR' - * - * @param {String} field - * @return this - */ - this.orWhereIsNull = function(field) { - _p.whereNull(field, 'IS NULL', 'OR'); - return this; - } - - /** - * Field is not null prefixed with 'OR' - * - * @param {String} field - * @return this - */ - this.orWhereIsNotNull = function(field) { - _p.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 this - */ - this.whereIn = function(key, val) { - _p.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 this - */ - this.orWhereIn = function(key, val) { - _p.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 this - */ - this.whereNotIn = function(key, val) { - _p.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 this - */ - this.orWhereNotIn = function(key, val) { - _p.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 this - */ - this.set = function(/* $key, [$val] */) { - var args = getArgs('$key, [$val]', arguments); - - // Set the appropriate state variables - _p.mixedSet('setArrayKeys', 'key', args.$key, args.$val); - _p.mixedSet('values', 'value', args.$key, args.$val); - - // Use the keys of the array to make the insert/update string - // and escape the field names - state.setArrayKeys = state.setArrayKeys.map(driver._quote); - - // Generate the "set" string - state.setString = state.setArrayKeys.join('=?,'); - 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 this - */ - this.join = function(table, cond, type) { - type = type || "inner"; - - // Prefix/quote table name - var table = table.split(' ').map(helpers.stringTrim); - table[0] = driver.quoteTable(table[0]); - table = table.map(driver.quoteIdentifiers); - table = table.join(' '); - - // Parse out the join condition - var parsedCondition = parser.compileJoin(cond); - var condition = table + ' ON ' + parsedCondition; - - // Append the join condition to the query map - _p.appendMap("\n" + type.toUpperCase() + ' JOIN ', condition, 'join'); - - return this; - }; - - /** - * Group the results by the selected field(s) - * - * @param {String|Array} field - * @return this - */ - this.groupBy = function(field) { - if ( ! helpers.isScalar(field)) - { - var newGroupArray = field.map(driver.quoteIdentifiers); - state.groupArray = state.groupArray.concat(newGroupArray); - } - else - { - state.groupArray.push(driver.quoteIdentifiers(field)); - } - - state.groupString = ' GROUP BY ' + 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 this - */ - this.orderBy = function(field, type) { - type = type || 'ASC'; - - // Set the fields for later manipulation - field = driver.quoteIdentifiers(field); - - state.orderArray[field] = type; - - var orderClauses = []; - - // Flatten key/val pairs into an array of space-separated pairs - Object.keys(state.orderArray).forEach(function(key) { - orderClauses.push(key + ' ' + state.orderArray[key].toUpperCase()); - }); - - // Set the final string - 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 this - */ - this.limit = function(limit, offset) { - state.limit = limit; - state.offset = offset || null; - - return this; - }; - - /** - * Adds an open paren to the current query for logical grouping - * - * @return this - */ - this.groupStart = function() { - var conj = (state.queryMap.length < 1) ? ' WHERE ' : ' AND '; - _p.appendMap(conj, '(', 'groupStart'); - - return this; - }; - - /** - * Adds an open paren to the current query for logical grouping, - * prefixed with 'OR' - * - * @return this - */ - this.orGroupStart = function() { - _p.appendMap('', ' OR (', 'groupStart'); - - return this; - }; - - /** - * Adds an open paren to the current query for logical grouping, - * prefixed with 'OR NOT' - * - * @return this - */ - this.orNotGroupStart = function() { - _p.appendMap('', ' OR NOT (', 'groupStart'); - - return this; - }; - - /** - * Ends a logical grouping started with one of the groupStart methods - * - * @return this - */ - this.groupEnd = function() { - _p.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 - */ - this.get = function(/* [table], [limit], [offset], callback */) { - var 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 - _p.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 - */ - this.insert = function(/* table, data, callback */) { - var args = getArgs('table:string, [data]:object, callback:function', arguments); - - if (args.data) { - this.set(args.data); - } - - // Run the query - _p.run('insert', 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 - */ - this.insertBatch = function(/* table, data, callback */) { - var args = getArgs('table:string, data:array, callback:function', arguments); - var batch = driver.insertBatch(args.table, args.data); - - // Run the query - _p.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 - */ - this.update = function(/*table, data, callback*/) { - var args = getArgs('table:string, [data]:object, callback:function', arguments); - - if (args.data) { - this.set(args.data); - } - - // Run the query - _p.run('update', 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 - */ - this.delete = function (/*table, [where], callback*/) { - var args = getArgs('table:string, [where]:object, callback:function', arguments); - - if (args.where) - { - this.where(args.where); - } - - // Run the query - _p.run('delete', 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 - */ - this.getCompiledSelect = function(/*table, reset*/) { - var args = getArgs('[table]:string, [reset]:boolean', arguments); - if (args.table) - { - this.from(args.table); - } - - return _p.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} - */ - this.getCompiledInsert = function(table, reset) { - return _p.getCompile('insert', 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} - */ - this.getCompiledUpdate = function(table, reset) { - return _p.getCompile('update', 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} - */ - this.getCompiledDelete = function(table, reset) { - return _p.getCompile('delete', driver.quoteTable(table), reset); - }; - - return this; -}; - -module.exports = QueryBuilder; \ No newline at end of file diff --git a/lib/state.js b/lib/state.js deleted file mode 100644 index 549f491..0000000 --- a/lib/state.js +++ /dev/null @@ -1,29 +0,0 @@ -'use strict'; - -/** @module State */ -module.exports = function State() { - return { - // Arrays/Maps - queryMap: [], - values: [], - whereValues: [], - setArrayKeys: [], - orderArray: [], - groupArray: [], - havingMap: [], - whereMap: {}, - rawWhereValues: [], - - // Partials - selectString: '', - fromString: '', - setString: '', - orderString: '', - groupString: '', - - // Other various values - limit: null, - offset: null - }; -}; -// End of module State \ No newline at end of file diff --git a/package.json b/package.json index 86eb2fb..715dfda 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,10 @@ "mysql2": "^0.15.8", "node-firebird": "^0.7.0", "pg": "^4.4.3", - "es6-shim": "" + "es6-shim": "", + "babel": "", + "babel-preset-es2015": "", + "babel-plugin-transform-es2015-modules-commonjs": "" }, "optionalDependencies": { "dblite": "*", @@ -49,14 +52,19 @@ "documentation": "", "nodeunit": "", "gulp": "", + "gulp-babel": "", + "gulp-babel-istanbul": "", + "gulp-sourcemaps": "", + "gulp-concat": "", "gulp-documentation": "", - "gulp-istanbul": "", "gulp-nodeunit-runner": "", - "jsdoc": "", + "gulp-sloc": "", + "gulp-eslint": "", + "eslint": "", "istanbul": "" }, "license": "MIT", "scripts": { - "test": "grunt tests" + "test": "gulp nodeunit" } } diff --git a/src/Adapter.js b/src/Adapter.js new file mode 100755 index 0000000..bb1627d --- /dev/null +++ b/src/Adapter.js @@ -0,0 +1,26 @@ +'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/lib/driver.js b/src/DriverBase.js similarity index 98% rename from lib/driver.js rename to src/DriverBase.js index 4c7a3ee..c4a257e 100755 --- a/lib/driver.js +++ b/src/DriverBase.js @@ -1,6 +1,6 @@ 'use strict'; -var helpers = require('./helpers'); +import helpers from './helpers' /** * Base Database Driver diff --git a/src/DriverClass.js b/src/DriverClass.js new file mode 100644 index 0000000..bc80b23 --- /dev/null +++ b/src/DriverClass.js @@ -0,0 +1,13 @@ +'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 new file mode 100755 index 0000000..fc8abe3 --- /dev/null +++ b/src/NodeQuery.js @@ -0,0 +1,74 @@ +"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 new file mode 100755 index 0000000..7d4d120 --- /dev/null +++ b/src/QueryBuilder.js @@ -0,0 +1,924 @@ +'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/lib/query-parser.js b/src/QueryParser.js similarity index 51% rename from lib/query-parser.js rename to src/QueryParser.js index 84d33b5..822a3b6 100644 --- a/lib/query-parser.js +++ b/src/QueryParser.js @@ -1,48 +1,7 @@ 'use strict'; -var helpers = require('./helpers'); +import helpers from './helpers'; -var 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'); - -var identifierBlacklist = ['true','false','null']; - -var filterMatches = function(array) { - var output = []; - - // Return non-array matches - if (helpers.isNull(array)) return null; - if (helpers.isScalar(array) || helpers.isUndefined(array)) return output; - - array.forEach(function(item) { - output.push(item); - }); - - return output; -}; // -------------------------------------------------------------------------- @@ -51,10 +10,63 @@ var filterMatches = function(array) { * @param {Driver} - The driver object for the database in use * @module query-parser */ -var QueryParser = function(driver) { +module.exports = class QueryParser { + /** + * @constructor + * @param {Driver} - The driver object for the database in use + * @return {void} + */ + constructor(driver) { + this.driver = driver; - // That 'new' keyword is annoying - if ( ! (this instanceof QueryParser)) return new QueryParser(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). @@ -63,8 +75,8 @@ var QueryParser = function(driver) { * @param {String} string - the string to check * @return {Array|null} */ - this.hasOperator = function(string) { - return filterMatches(string.match(matchPatterns.operator)); + hasOperator(string) { + return this.filterMatches(string.match(this.matchPatterns.operator)); } /** @@ -73,26 +85,31 @@ var QueryParser = function(driver) { * @param {String} sql * @return {Object} */ - this.parseJoin = function(sql) { - var matches = {}; - var output = {}; + parseJoin(sql) { + let matches = {}; + let output = { + functions: [], + identifiers: [], + operators: [], + literals: [] + }; // Get clause components - matches.functions = sql.match(new RegExp(matchPatterns['function'].source, 'ig')); - matches.identifiers = sql.match(matchPatterns.identifier); - matches.operators = sql.match(matchPatterns.operator); - matches.literals = sql.match(matchPatterns.literal); + 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(matchPatterns.joinCombined); + matches.combined = sql.match(this.matchPatterns.joinCombined); // Flatten the matches to increase relevance - Object.keys(matches).forEach(function(key) { - output[key] = filterMatches(matches[key]); + Object.keys(matches).forEach((key) => { + output[key] = this.filterMatches(matches[key]); }); return output; - }; + } /** * Return the output of the parsing of the join condition @@ -100,40 +117,40 @@ var QueryParser = function(driver) { * @param {String} condition - The join condition to evalate * @return {String} - The parsed/escaped join condition */ - this.compileJoin = function(condition) { - var parts = this.parseJoin(condition); - var count = parts.identifiers.length; - var i; + compileJoin(condition) { + let parts = this.parseJoin(condition); + let count = parts.identifiers.length; + let i; // Quote the identifiers - parts.combined.forEach(function(part, i) { + parts.combined.forEach((part, i) => { if (parts.identifiers.indexOf(part) !== -1 && ! helpers.isNumber(part)) { - parts.combined[i] = driver.quoteIdentifiers(part); + parts.combined[i] = this.driver.quoteIdentifiers(part); } }); return parts.combined.join(' '); - }; + } /** * Parse a where clause to separate functions from values * - * @param {Object} driver + * @param {Driver} driver * @param {State} state * @return {String} - The parsed/escaped where condition */ - this.parseWhere = function(driver, state) { - var whereMap = state.whereMap, - whereValues = state.rawWhereValues; + parseWhere(driver, state) { + let whereMap = state.whereMap; + let whereValues = state.rawWhereValues; - var outputMap = []; - var outputValues = []; + let outputMap = []; + let outputValues = []; - Object.keys(whereMap).forEach(function(key) { + Object.keys(whereMap).forEach((key) => { // Combine fields, operators, functions and values into a full clause // to have a common starting flow - var fullClause = ''; + let fullClause = ''; // Add an explicit = sign where one is inferred if ( ! this.hasOperator(key)) @@ -150,17 +167,17 @@ var QueryParser = function(driver) { } // Separate the clause into separate pieces - var parts = this.parseJoin(fullClause); + let parts = this.parseJoin(fullClause); // Filter explicit literals from lists of matches if (whereValues.indexOf(whereMap[key]) !== -1) { - var value = whereMap[key]; - var identIndex = (helpers.isArray(parts.identifiers)) ? parts.identifiers.indexOf(value) : -1; - var litIndex = (helpers.isArray(parts.literals)) ? parts.literals.indexOf(value) : -1; - var combIndex = (helpers.isArray(parts.combined)) ? parts.combined.indexOf(value) : -1; - var funcIndex = (helpers.isArray(parts.functions)) ? parts.functions.indexOf(value) : -1; - var inOutputArray = outputValues.indexOf(value) !== -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 @@ -207,18 +224,19 @@ var QueryParser = function(driver) { } // Filter false positive identifiers - parts.identifiers = parts.identifiers.filter(function(item) { - var isInCombinedMatches = parts.combined.indexOf(item) !== -1; - var isNotInBlackList = identifierBlacklist.indexOf(item.toLowerCase()) === -1; + 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(function(ident) { - var index = parts.combined.indexOf(ident); + parts.identifiers.forEach((ident) => { + let index = parts.combined.indexOf(ident); if (index !== -1) { parts.combined[index] = driver.quoteIdentifiers(ident); @@ -233,8 +251,8 @@ var QueryParser = function(driver) { // a where condition, if (helpers.isArray(parts.literals)) { - parts.literals.forEach(function(lit) { - var litIndex = parts.combined.indexOf(lit); + parts.literals.forEach((lit) => { + let litIndex = parts.combined.indexOf(lit); if (litIndex !== -1) { @@ -245,14 +263,12 @@ var QueryParser = function(driver) { } outputMap.push(parts.combined.join(' ')); - }, this); + }); state.rawWhereValues = []; state.whereValues = state.whereValues.concat(outputValues); state.whereMap = outputMap; return state; - }; -}; - -module.exports = QueryParser; \ No newline at end of file + } +} \ No newline at end of file diff --git a/src/State.js b/src/State.js new file mode 100644 index 0000000..1e6a889 --- /dev/null +++ b/src/State.js @@ -0,0 +1,29 @@ +'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 new file mode 100644 index 0000000..337f9fd --- /dev/null +++ b/src/adapters/dblite.js @@ -0,0 +1,20 @@ +'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/lib/adapter.js b/src/adapters/mysql.js old mode 100755 new mode 100644 similarity index 59% rename from lib/adapter.js rename to src/adapters/mysql.js index 2784ad1..2a07ccb --- a/lib/adapter.js +++ b/src/adapters/mysql.js @@ -1,8 +1,9 @@ 'use strict'; -/** @module adapter */ -module.exports = { +import Adapter from '../Adapter'; +/** @module adapters/mysql */ +module.exports = class mysql extends Adapter { /** * Run the sql query as a prepared statement * @@ -11,7 +12,7 @@ module.exports = { * @param {Function} callback - Callback to run when a response is recieved * @return void */ - execute: function(/*sql, params, callback*/) { - throw new Error("Correct adapter not defined for query execution"); + execute(sql, params, callback) { + this.instance.query.apply(instance, arguments); } -}; \ No newline at end of file +} \ No newline at end of file diff --git a/src/adapters/mysql2.js b/src/adapters/mysql2.js new file mode 100644 index 0000000..85daf2c --- /dev/null +++ b/src/adapters/mysql2.js @@ -0,0 +1,18 @@ +'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 new file mode 100644 index 0000000..d60fae4 --- /dev/null +++ b/src/adapters/node-firebird.js @@ -0,0 +1,20 @@ +'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 new file mode 100644 index 0000000..9ca4bb0 --- /dev/null +++ b/src/adapters/pg.js @@ -0,0 +1,28 @@ +'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/lib/drivers/firebird.js b/src/drivers/Firebird.js similarity index 57% rename from lib/drivers/firebird.js rename to src/drivers/Firebird.js index 58abafa..92f7bc4 100644 --- a/lib/drivers/firebird.js +++ b/src/drivers/Firebird.js @@ -1,17 +1,19 @@ "use strict"; -var helpers = require('../helpers'); +import helpers from '../helpers'; +import Driver from '../DriverClass'; /** * Driver for Firebird databases * * @module drivers/firebird */ -module.exports = (function() { - delete require.cache[require.resolve('../driver')]; - var driver = require('../driver'); - - driver.hasTruncate = false; +class Firebird extends Driver { + constructor() { + super({ + hasTruncate: false + }); + } /** * Generate a limit clause for firebird, which uses the syntax closest to the SQL standard @@ -21,16 +23,16 @@ module.exports = (function() { * @param {Number} offset * @return {String} */ - driver.limit = function(origSql, limit, offset) { - var sql = 'FIRST ' + limit; + limit(origSql, limit, offset) { + let sql = `FIRST ${limit}`; if (helpers.isNumber(offset)) { - sql += ' SKIP ' + offset; + sql += ` SKIP ${offset}`; } - return origSql.replace(/SELECT/i, "SELECT " + sql);; - }; + return origSql.replace(/SELECT/i, "SELECT " + sql); + } /** * SQL to insert a group of rows @@ -39,9 +41,9 @@ module.exports = (function() { * @param {Array} [data] - The array of object containing data to insert * @return {String} */ - driver.insertBatch = function(table, data) { + insertBatch() { throw new Error("Not Implemented"); - }; + } +} - return driver; -}()); \ No newline at end of file +module.exports = new Firebird(); \ No newline at end of file diff --git a/src/drivers/Mysql.js b/src/drivers/Mysql.js new file mode 100755 index 0000000..1146231 --- /dev/null +++ b/src/drivers/Mysql.js @@ -0,0 +1,29 @@ +"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 new file mode 100755 index 0000000..43e2fd7 --- /dev/null +++ b/src/drivers/Pg.js @@ -0,0 +1,10 @@ +"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 new file mode 100644 index 0000000..0dd7872 --- /dev/null +++ b/src/drivers/Sqlite.js @@ -0,0 +1,64 @@ +"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 new file mode 100755 index 0000000..1458e0d --- /dev/null +++ b/src/helpers.js @@ -0,0 +1,127 @@ +"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/adapters/dblite_test.js b/tests/adapters/dblite_test.js index 8bf0415..6af9260 100644 --- a/tests/adapters/dblite_test.js +++ b/tests/adapters/dblite_test.js @@ -18,13 +18,13 @@ try { // Export an empty testsuite if module not loaded console.log(e); console.log("Database adapter dblite not found"); - return {}; + //return {}; } if (connection) { // Set up the query builder object - var nodeQuery = require('../../lib/node-query'); + var nodeQuery = require('../../lib/NodeQuery'); var qb = nodeQuery.init('sqlite', connection, adapterName); // Set up the sqlite database diff --git a/tests/adapters/mysql2_test.js b/tests/adapters/mysql2_test.js index 5db53e1..324071a 100644 --- a/tests/adapters/mysql2_test.js +++ b/tests/adapters/mysql2_test.js @@ -16,7 +16,7 @@ var mysql2 = require(adapterName); var connection = mysql2.createConnection(config.conn); // Set up the query builder object -var nodeQuery = require('../../lib/node-query'); +var nodeQuery = require('../../lib/NodeQuery'); var qb = nodeQuery.init('mysql', connection, adapterName); diff --git a/tests/adapters/mysql_test.js b/tests/adapters/mysql_test.js index c6d3a32..e46e02e 100644 --- a/tests/adapters/mysql_test.js +++ b/tests/adapters/mysql_test.js @@ -16,7 +16,7 @@ var mysql = require(adapterName); var connection = mysql.createConnection(config.conn); // Set up the query builder object -var nodeQuery = require('../../lib/node-query'); +var nodeQuery = require('../../lib/NodeQuery'); var qb = nodeQuery.init('mysql', connection); // Set up the test base diff --git a/tests/adapters/node-firebird_test.js b/tests/adapters/node-firebird_test.js index 7d4f586..d8774c7 100644 --- a/tests/adapters/node-firebird_test.js +++ b/tests/adapters/node-firebird_test.js @@ -7,7 +7,7 @@ var testBase = require('../query-builder-base'); var adapterName = 'node-firebird'; var config = require('../config.json')[adapterName]; config.conn.database = __dirname + config.conn.database; -var nodeQuery = require('../../lib/node-query'); +var nodeQuery = require('../../lib/NodeQuery'); // Skip on TravisCi if (process.env.CI || process.env.JENKINS_HOME) diff --git a/tests/adapters/pg_test.js b/tests/adapters/pg_test.js index 2af084a..e787420 100644 --- a/tests/adapters/pg_test.js +++ b/tests/adapters/pg_test.js @@ -21,7 +21,7 @@ connection.connect(function(err) { }); // Set up the query builder object -var nodeQuery = require('../../lib/node-query'); +var nodeQuery = require('../../lib/NodeQuery'); var qb = nodeQuery.init('pg', connection); diff --git a/tests/base_test.js b/tests/base_test.js index 51d6fa5..8406519 100755 --- a/tests/base_test.js +++ b/tests/base_test.js @@ -2,13 +2,13 @@ var modules = { helpers: require('../lib/helpers'), - driver: require('../lib/driver'), - qb: require('../lib/query-builder'), - 'node-query': require('../lib/node-query'), - 'state': require('../lib/state'), - 'drivers/pg': require('../lib/drivers/pg'), - 'drivers/mysql': require('../lib/drivers/mysql'), - 'drivers/sqlite': require('../lib/drivers/sqlite'), + 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'), diff --git a/tests/query-builder-base.js b/tests/query-builder-base.js index 88f1ee3..8349236 100644 --- a/tests/query-builder-base.js +++ b/tests/query-builder-base.js @@ -1,7 +1,7 @@ 'use strict'; var helpers = require('../lib/helpers'); -var State = require('../lib/state'); +var State = require('../lib/State'); module.exports = (function QueryBuilderTestBase() { diff --git a/tests/query-parser_test.js b/tests/query-parser_test.js index a1ccf02..a7fd249 100644 --- a/tests/query-parser_test.js +++ b/tests/query-parser_test.js @@ -1,12 +1,14 @@ 'use strict'; // Use the base driver as a mock for testing -delete require.cache[require.resolve('../lib/driver')]; var getArgs = require('getargs'); var helpers = require('../lib/helpers'); -var driver = require('../lib/driver'); -var parser = require('../lib/query-parser')(driver); -var State = require('../lib/state'); +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(); @@ -63,7 +65,7 @@ var whereMock = function() { // ! Start Tests // ----------------------------------------------------------------------------- -module.exports = { +var tests = { 'Has operator tests': { 'Has operator': function(test) { var matches = parser.hasOperator('foo <> 2'); @@ -171,4 +173,6 @@ module.exports = { test.done(); } } -}; \ No newline at end of file +}; + +module.exports = tests; \ No newline at end of file