├── .gitignore ├── .travis.yml ├── HISTORY.md ├── Makefile ├── README.md ├── build └── config.gypi ├── drivers ├── QueryExecError.js ├── WrapperPromise.js ├── drivers.json ├── mssql │ ├── adapter.js │ ├── adapters │ │ ├── cluster.js │ │ ├── pool.js │ │ └── single.js │ ├── query_builder.js │ └── query_exec.js ├── mysql │ ├── adapter.js │ ├── adapters │ │ ├── cluster.js │ │ ├── pool.js │ │ └── single.js │ ├── query_builder.js │ └── query_exec.js ├── query_builder.js └── template │ └── connect.js ├── examples ├── connection_pool_example.js └── connection_single_example.js ├── index.js ├── package-lock.json ├── package.json ├── test ├── 05-multiple-drivers.js ├── configs.js ├── create_mock_dbs.sh ├── mssql │ ├── 00-AA-tests-general.js │ ├── 00-tests-adapter.js │ ├── 01-tests-distinct.js │ ├── 01-tests-from.js │ ├── 01-tests-get.js │ ├── 01-tests-groupby.js │ ├── 01-tests-having.js │ ├── 01-tests-join.js │ ├── 01-tests-like.js │ ├── 01-tests-limit.js │ ├── 01-tests-offset.js │ ├── 01-tests-orderby.js │ ├── 01-tests-returning.js │ ├── 01-tests-select.js │ ├── 01-tests-set.js │ ├── 01-tests-where.js │ ├── 01-tests-where_in.js │ ├── 02-tests-compilation_methods.js │ ├── 03-tests-count.js │ ├── 03-tests-delete.js │ ├── 03-tests-empty_table.js │ ├── 03-tests-insert.js │ ├── 03-tests-insert_batch.js │ ├── 03-tests-truncate.js │ ├── 03-tests-update.js │ ├── 03-tests-update_batch.js │ ├── 04-tests-query-promise.js │ ├── 04-tests-query-response.js │ ├── 05-tests-multiple-pools.js │ ├── 05-tests-multiple-queries.js │ ├── create_mssql_mock.sh │ ├── mock_data.sql │ └── mock_data2.sql └── mysql │ ├── 00-AA-tests-general.js │ ├── 00-tests-adapter.js │ ├── 01-tests-distinct.js │ ├── 01-tests-from.js │ ├── 01-tests-get.js │ ├── 01-tests-groupby.js │ ├── 01-tests-having.js │ ├── 01-tests-join.js │ ├── 01-tests-like.js │ ├── 01-tests-limit.js │ ├── 01-tests-offset.js │ ├── 01-tests-orderby.js │ ├── 01-tests-select.js │ ├── 01-tests-set.js │ ├── 01-tests-where.js │ ├── 01-tests-where_in.js │ ├── 02-tests-compilation_methods.js │ ├── 03-tests-count.js │ ├── 03-tests-delete.js │ ├── 03-tests-empty_table.js │ ├── 03-tests-insert.js │ ├── 03-tests-insert_batch.js │ ├── 03-tests-truncate.js │ ├── 03-tests-update.js │ ├── 03-tests-update_batch.js │ ├── 04-tests-query-promise.js │ ├── 04-tests-query-response.js │ ├── 05-tests-multiple-pools.js │ ├── 05-tests-multiple-queries.js │ ├── create_mysql_mock.sh │ ├── mock_data.sql │ └── mock_data2.sql └── tests.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | npm-debug* 4 | index2.js 5 | build 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | before_script: 2 | - sudo docker run --name=mssql-server-linux-latest -e 'ACCEPT_EULA=Y' -e 'MSSQL_SA_PASSWORD=Password123' -p 1433:1433 -d microsoft/mssql-server-linux:2017-latest 3 | - sudo docker cp ./test/mssql/mock_data.sql mssql-server-linux-latest:mock_data.sql 4 | - sudo docker cp ./test/mssql/mock_data2.sql mssql-server-linux-latest:mock_data2.sql 5 | - ./test/mysql/create_mysql_mock.sh 6 | - ./test/mssql/create_mssql_mock.sh 7 | language: node_js 8 | dist: bionic 9 | node_js: 10 | - "12" 11 | - "11" 12 | - "10" 13 | - "9" 14 | - "8" 15 | services: 16 | - mysql 17 | - docker 18 | test: 19 | adapter: mysql2 20 | database: mock_db 21 | username: travis 22 | encoding: utf8 23 | -------------------------------------------------------------------------------- /HISTORY.md: -------------------------------------------------------------------------------- 1 | # Changes 2 | 3 | This file is a manually maintained list of changes for each release. Feel free 4 | to add your changes here when sending pull requests. Also send corrections if 5 | you spot any mistakes. 6 | 7 | ## v2.1.1 (2019-11-08) 8 | 9 | * Updated documentation to reflect new Promise API support. 10 | 11 | ## v2.1.0 (2019-11-08) 12 | 13 | * Added Promise support. Closes [#18](https://github.com/kylefarris/node-querybuilder/pull/52). Thanks for the contribution @AbhijetPokhrel! 14 | * Added new tests to verify that Promise support is working. 15 | 16 | ## v2.0.2 (2019-10-28) 17 | 18 | * Finally got testing working on Travis CI for MySQL and MSSQL. 19 | 20 | ## v2.0.0 (2018-06-15) 21 | 22 | ### Breaking Changes 23 | * Changed the Query Builder instantiation syntax 24 | * Passing an empty array to `where_in` and `where_not_in` no longer throws an error ([#34](https://github.com/kylefarris/node-querybuilder/issues/34)) 25 | 26 | # General Enhancements/Changes/Features 27 | * Added mssql (t-sql) support using `tedious` as the underlying driver 28 | * Updated class files to use new ES6 class syntax for easier-maintainability 29 | * Added new options: 30 | ** `pool_min` (minimum number of pooled connections (`mssql` driver only)) 31 | ** `acquireTimeout` (milliseconds before a timeout occurs during the connection acquisition) 32 | * Added new query building method: `returning()` to allow for insert IDs to be returned. See docs for more info. 33 | * Added new tests 34 | 35 | ### Bug Fixes 36 | * Fixed [#18](https://github.com/kylefarris/node-querybuilder/issues/18) 37 | * Fixed [#23](https://github.com/kylefarris/node-querybuilder/issues/23) 38 | * Fixed [#26](https://github.com/kylefarris/node-querybuilder/issues/26) 39 | * Fixed [#28](https://github.com/kylefarris/node-querybuilder/issues/28) 40 | * Fixed [#30](https://github.com/kylefarris/node-querybuilder/issues/30) 41 | * Fixed [#33](https://github.com/kylefarris/node-querybuilder/issues/33) 42 | 43 | 44 | ## v1.2.0 (2018-05-18) 45 | 46 | * retroactive fix of this change log 47 | * updated the mysql escape string to use the proper mysql method. 48 | 49 | ## v1.1.1 (2018-05-17) 50 | 51 | * Updated package.json to always use the latest version of mysql 2.x 52 | 53 | ## v1.1.0 (2018-02-20) 54 | 55 | * Fixed a bug where you could not insert with leading zeros. #20 56 | 57 | ## v1.0.3 (2017-08-22) 58 | 59 | * Fixed a bug in how it's checking for valid connection types. Also updated tests to allow for an empty driver string in QueryBuilder constructor (defaults to 'mysql'). 60 | 61 | ## v1.0.2 (2017-08-22) 62 | 63 | * Fixed a bug dealing with the default 'driver' param value in the QueryBuilder contructor 64 | 65 | ## v1.0.1 (2017-08-10) 66 | 67 | * Fixed a bug where non-strings or non-array-of-strings could be passed to the method and cause issues 68 | 69 | ## v1.0.0 (2017-07-26) 70 | 71 | * Updated codebase to ES6 72 | 73 | ## v0.15.0 (2017-04-27) 74 | 75 | * Fixed and documented the escape property of the `join` method. 76 | 77 | ## v0.9.0 (2015-02-05) 78 | 79 | * Added this history file 80 | * Added the ability to do `SELECT DISTINCT` queries 81 | * We're doing better escaping of identifiers now 82 | * Added the ability to use table/view prefixes directly (ex. `select * from users u`) 83 | * Added the ability to do `OR WHERE` statements with `or_where()` method. 84 | * Added the ability to do `LIKE` statements directly (new methods: `like()`, `not_like()`, `or_like()`, `or_not_like()`) 85 | * Restored ability to do `WHERE IN(...)` statements by passing an array as the second param to `where()` 86 | * Added the ability to do `[OR] WHERE [NOT] IN(...)` statements directly (new methods: `where_in()`, `or_where_in()`, `where_not_in()`, `or_where_not_in()`) 87 | * Added the ability to do `FROM` statements directly for `SELECT` and `DELETE` queries (new method: `from()`) (ex. db.from('foo').get(() => ...)) 88 | * Identifiers will now be properly escaped in `JOIN` statements. 89 | * Added the ability to call `get_where()` as a shorthand to `get()` and `where()` (ex. `db.get_where('table',{foo: 'bar'},() => ...);`) 90 | * Added the ability to call `select_min()`, `select_max()`, `select_avg()`, and `select_sum()`. 91 | * Significanly improved security, helping to prevent SQL injection attacks. 92 | * Added ability to do `OR HAVING` statements with `or_having()` method 93 | * Added ability to add an offset directly without using the `limit()` method by using the `offset()` method. 94 | * Added ability to set `SET` values for updates and inserts using the `set()` method. 95 | * `UPDATE` statements now support `ORDER BY` clauses which can be added to the query using the `order_by()` method. 96 | * The `update()` method's 3rd parameter can now either be the callback (as always) or a `WHERE` clause (ex. 'foo = "bar"' or {foo:'bar', id: 3}). If a where clause is provided, the callback is now the 4th parameter. This change is fully backwards-compatible with the previous version of this module. 97 | * New package dependencies (for testing): chai and mocha. 98 | * Tests have been written for better-ensuring future enhancements and fixes to not break functionality 99 | * Library has been broken into 3 objects to allow for prpoer testing. This won't affect the API and is fully-backwards compatible with the previous version. 100 | * Officially announcing that how third parameter of the `where()` method works is deprecated... starting with v1.0.0, third param will be `(bool) escape` and not `(bool) isRaw`. So, all calls to this method using the 3rd parameter will, in future, have to be changed by converting `true` to `false` and vice versa. This is so that we have a consistent API throughout the library. 101 | * Name officially changed to node-mysql-querybuilder. 102 | * Author officially changed to Kyle Farris due to the substantial changes to this fork and Martin Tajur demoted to primary contributor (thanks for the great starting place Martin!!) 103 | * Name of internal methods and properties have been normalized to use the "lower_case" syntax. 104 | * Dependency for node-mysql upgraded to 2.5. 105 | * travis-ci functionality added to repository 106 | * Added public `escape()` method 107 | * Added funtional `update_batch()` method. 108 | * Added `truncate()` and `empty_table()` methods. 109 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | TESTS = test/mysql/*.js test/mssql/*.js test/*.js 2 | #TESTS = test/mssql/01-tests-where_in.js 3 | #TESTS = test/05-multiple-drivers.js 4 | test: 5 | mocha --exit --timeout 5000 --reporter spec $(TESTS) 6 | 7 | .PHONY: test 8 | -------------------------------------------------------------------------------- /build/config.gypi: -------------------------------------------------------------------------------- 1 | # Do not edit. File was generated by node-gyp's "configure" step 2 | { 3 | "target_defaults": { 4 | "cflags": [], 5 | "default_configuration": "Release", 6 | "defines": [], 7 | "include_dirs": [], 8 | "libraries": [] 9 | }, 10 | "variables": { 11 | "clang": 0, 12 | "gcc_version": 48, 13 | "host_arch": "x64", 14 | "node_install_npm": "false", 15 | "node_prefix": "/usr", 16 | "node_shared_cares": "true", 17 | "node_shared_http_parser": "true", 18 | "node_shared_libuv": "true", 19 | "node_shared_openssl": "true", 20 | "node_shared_v8": "true", 21 | "node_shared_zlib": "true", 22 | "node_tag": "", 23 | "node_unsafe_optimizations": 0, 24 | "node_use_dtrace": "false", 25 | "node_use_etw": "false", 26 | "node_use_openssl": "true", 27 | "node_use_perfctr": "false", 28 | "node_use_systemtap": "false", 29 | "openssl_no_asm": 0, 30 | "python": "/usr/bin/python", 31 | "target_arch": "x64", 32 | "v8_enable_gdbjit": 0, 33 | "v8_no_strict_aliasing": 1, 34 | "v8_use_snapshot": "true", 35 | "want_separate_host_toolset": 0, 36 | "nodedir": "/usr/share/node", 37 | "copy_dev_lib": "true", 38 | "standalone_static_library": 1 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /drivers/QueryExecError.js: -------------------------------------------------------------------------------- 1 | /** 2 | * List of Error of query execution 3 | * 4 | */ 5 | 6 | module.exports = { 7 | NO_TBL_NAME_ERR: new Error("Table name not specified"), 8 | NO_WHERE_CLAUSE_ERR: new Error("Where clause is not defined"), 9 | NO_CONN_OBJ_ERR: new Error("No connection object supplied to the Query Exec Library!"), 10 | FIRST_PARAM_OF_GET_WHERE_ERR: new Error("First parameter of get_where() must be a string or an array of strings."), 11 | SECOND_PARAM_OF_GET_WHERE_ERR: new Error("Second parameter of get_where() must be an object with key:value pairs."), 12 | NO_VALID_RESULTS_HANDLER: new Error('QueryBuilder is unsure how to respond since no callback or Promise resolve/reject methods were provided.'), 13 | } 14 | -------------------------------------------------------------------------------- /drivers/WrapperPromise.js: -------------------------------------------------------------------------------- 1 | class WrapperPromise { 2 | /** 3 | * 4 | * @param {String} sql - A sql command 5 | * @param {function} exec - A sql execution function 6 | * @param {function} cb - A callback function 7 | * 8 | * @description 9 | * Wraps the execute command in promise 10 | */ 11 | constructor(sql, exec, cb) { 12 | this.sql = sql; 13 | this.exec = exec; 14 | if (typeof cb === "function") this.cb = cb.bind(this); 15 | } 16 | 17 | /** 18 | * Promisify 19 | */ 20 | promisify() { 21 | return new Promise((resolve, reject) => { 22 | this.resolve = resolve; 23 | this.reject = reject; 24 | this.invoke(); 25 | }); 26 | } 27 | 28 | /** 29 | * Executet the query and resolve as the result 30 | */ 31 | invoke() { 32 | this.exec(this.sql, (err, res) => { 33 | if (err) { 34 | this.reject(err); 35 | return; 36 | } 37 | 38 | /** 39 | * If theres is a cb function, let the cb function resolve and reject the promise 40 | */ 41 | if (typeof this.cb === "function") { 42 | this.cb(err, res); 43 | return; 44 | } 45 | 46 | this.resolve(res); 47 | }); 48 | } 49 | } 50 | 51 | module.exports = WrapperPromise; 52 | -------------------------------------------------------------------------------- /drivers/drivers.json: -------------------------------------------------------------------------------- 1 | { 2 | "mysql": { 3 | "connection_types": { "single": true, "pool": true, "cluster": false }, 4 | "versions": { 5 | "2.5.4": { 6 | "path": "./drivers/mysql", 7 | "active": true 8 | }, 9 | "default": { 10 | "version": "2.5.4" 11 | } 12 | } 13 | }, 14 | "mssql": { 15 | "connection_types": { "single": true, "pool": true, "cluster": false }, 16 | "versions": { 17 | "4.1.0": { 18 | "path": "./drivers/mssql", 19 | "active": true 20 | }, 21 | "default": { 22 | "version": "4.1.0" 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /drivers/mssql/adapter.js: -------------------------------------------------------------------------------- 1 | const QueryExec = require('./query_exec'); 2 | 3 | class Adapter extends QueryExec { 4 | constructor(settings) { 5 | super(settings); 6 | 7 | // Verify that an instance of Node QueryBuilder was passed in 8 | if (!settings || typeof settings !== 'object') { 9 | throw new Error("No connection settings provided to initialize QueryBuilder!"); 10 | } 11 | 12 | this._original_settings = settings; 13 | this._connection_settings = settings; 14 | 15 | // Enable debugging if necessary 16 | this.debugging = false; 17 | if (this._connection_settings.hasOwnProperty('debug') && this._connection_settings.debug === true) { 18 | this.debugging = true; 19 | delete this._connection_settings.debug; 20 | } 21 | 22 | // Verify that required fields are provided... 23 | if (Object.keys(this._connection_settings).length === 0) throw new Error("No connection information provided!"); 24 | if (!this._connection_settings.hasOwnProperty('host')) this._connection_settings.host = 'localhost'; 25 | if (!this._connection_settings.hasOwnProperty('user')) { console.log("Settings:", this._connection_settings); throw new Error("No user property provided. Hint: It can be NULL"); } 26 | 27 | this.map_connection_settings(); 28 | } 29 | 30 | // **************************************************************************** 31 | // Map generic NQB connection settings to mssql's format 32 | // **************************************************************************** 33 | map_connection_settings() { 34 | const settings = Object.assign({}, this._connection_settings); 35 | 36 | this._connection_settings = { 37 | server: settings.host, 38 | userName: settings.user, 39 | password: settings.password, 40 | options: { 41 | port: 1433, 42 | encrypt: false, 43 | rowCollectionOnRequestCompletion: true, 44 | fallbackToDefaultDb: false, 45 | debug: { 46 | packet: this.debugging, 47 | data: this.debugging, 48 | payload: this.debugging, 49 | token: this.debugging, 50 | } 51 | } 52 | }; 53 | 54 | if (settings.hasOwnProperty('database')) { 55 | this._connection_settings.options.database = settings.database; 56 | delete settings.database; 57 | } 58 | if (settings.hasOwnProperty('port')) { 59 | this._connection_settings.options.port = settings.port; 60 | delete settings.port; 61 | } 62 | 63 | // Remove mapped connection settings: 64 | delete settings.host; 65 | delete settings.user; 66 | delete settings.password; 67 | 68 | // Set default pool settings 69 | this.pool_settings = { 70 | min: 10, 71 | max: 10, 72 | acquireTimeout: 60000, 73 | log: this.debugging, 74 | }; 75 | 76 | // Override default pool settings 77 | if (settings.hasOwnProperty('pool_size')) { 78 | this.pool_settings.max = settings.pool_size; 79 | delete settings.pool_size; 80 | } 81 | if (settings.hasOwnProperty('pool_min')) { 82 | this.pool_settings.min = settings.pool_min; 83 | delete settings.pool_min; 84 | } 85 | if (settings.hasOwnProperty('acquireTimeout')) { 86 | this.pool_settings.acquireTimeout = settings.acquireTimeout; 87 | delete settings.acquireTimeout; 88 | } 89 | 90 | 91 | if (settings.hasOwnProperty('options') && typeof settings.options === 'object') { 92 | let options = this._connection_settings.options; 93 | options = Object.assign(options, settings.options); 94 | options.debug = this._connection_settings.options.debug; 95 | this._connection_settings.options = options; 96 | delete settings.options; 97 | } 98 | 99 | // Merge any additional driver-specific settings into connection settings 100 | this._connection_settings = Object.assign(this._connection_settings, settings); 101 | } 102 | } 103 | 104 | module.exports = Adapter; 105 | -------------------------------------------------------------------------------- /drivers/mssql/adapters/cluster.js: -------------------------------------------------------------------------------- 1 | const Adapter = require('../adapter.js'); 2 | 3 | class Cluster extends Adapter { 4 | constructor(settings) { 5 | super(settings); 6 | return {}; 7 | } 8 | } 9 | 10 | module.exports = Cluster; 11 | -------------------------------------------------------------------------------- /drivers/mssql/adapters/pool.js: -------------------------------------------------------------------------------- 1 | // Load Tedious connection pool library 2 | const ConnectionPool = require('tedious-connection-pool'); 3 | const Adapter = require('../adapter.js'); 4 | const Single = require('./single.js'); 5 | 6 | class Pool extends Adapter { 7 | constructor(settings) { 8 | super(settings); 9 | 10 | // Create pool for node-querybuilder object if it doesn't already have one. 11 | if (!this.hasOwnProperty('pool') || this._pool.length === 0) { 12 | // Create connection Pool 13 | const ps = Object.assign({}, this.pool_settings); 14 | const cs = Object.assign({}, this._connection_settings); 15 | this._pool = new ConnectionPool(ps, cs); 16 | this._pool.on('error', err => { 17 | if (this.debugging === true) console.error(err); 18 | }); 19 | } 20 | } 21 | 22 | pool() { 23 | return this._pool; 24 | } 25 | 26 | get_connection(cb) { 27 | if (!this._pool) { 28 | const error_msg = "Connection pool not available!"; 29 | if (self.debugging === true) console.error(error_msg); 30 | throw new Error(error_msg); 31 | } 32 | 33 | const self = this; 34 | const handler = (reject, resolve) => { 35 | self._pool.acquire((err, connection) => { 36 | if (err) throw err; 37 | 38 | const adapter = new Single(self._original_settings, { 39 | pool: { 40 | pool: self._pool, 41 | connection, 42 | } 43 | }); 44 | 45 | if ((!cb || typeof cb !== 'function') && (typeof resolve === 'function' && typeof reject === 'function')) return resolve(adapter) 46 | else if (cb && typeof cb === 'function') return cb(adapter); 47 | throw ERRORS.NO_VALID_RESULTS_HANDLER; 48 | }); 49 | } 50 | 51 | if (!cb || (cb && typeof cb !== 'function')) { 52 | return new Promise(handler); 53 | } else { 54 | handler(); 55 | } 56 | } 57 | 58 | disconnect(cb) { 59 | if (!cb || (cb && typeof cb !== 'function')) { 60 | return new Promise((resolve, reject) => { 61 | this._pool.drain((err) => { 62 | if (err) { 63 | reject(err); 64 | } else { 65 | resolve(); 66 | } 67 | }); 68 | }); 69 | } else { 70 | this._pool.drain(cb); 71 | } 72 | } 73 | } 74 | 75 | module.exports = Pool; 76 | -------------------------------------------------------------------------------- /drivers/mssql/adapters/single.js: -------------------------------------------------------------------------------- 1 | // Load Tedious connection library 2 | const Connection = require('tedious').Connection; 3 | const Adapter = require('../adapter.js'); 4 | const tsqlstring = require('tsqlstring'); 5 | 6 | class Single extends Adapter { 7 | constructor(settings, pool) { 8 | super(settings, pool); 9 | 10 | // Set defaults 11 | this.pool = null; 12 | this._connection = null; 13 | 14 | // If the Pool object is instatiating this Adapter, use it's connection 15 | if (pool && pool.pool) { 16 | this.pool = pool.pool.pool; // NOTE: That truely is insane looking... ¯\_(ツ)_/¯ 17 | this._connection = pool.pool.connection; 18 | } 19 | // Otherwise, let's create a new connection 20 | else { 21 | const self = this; 22 | function SQLConnection() {}; 23 | SQLConnection.prototype.connect = function(cb) { 24 | this.connection = new Connection(self._connection_settings); 25 | this.connection.on('error', cb); 26 | this.connection.on('connect', cb); 27 | return this.connection; 28 | } 29 | this.sql_connection = new SQLConnection(); 30 | } 31 | } 32 | 33 | connection_settings() { 34 | return {connection_settings: this._connection_settings, pool_settings: this.pool_settings}; 35 | } 36 | 37 | connect(cb) { 38 | if (!cb || (cb && typeof cb !== 'function')) { 39 | return new Promise((resolve, reject) => { 40 | if (this._connection) return resolve(); 41 | this._connection = this.sql_connection.connect((err) => { 42 | if (err) return reject(err); 43 | resolve(); 44 | }); 45 | }); 46 | } else { 47 | if (this._connection) return cb(); 48 | this._connection = this.sql_connection.connect(cb); 49 | } 50 | } 51 | 52 | connection() { 53 | return this._connection; 54 | } 55 | 56 | escape_id(str) { 57 | return tsqlstring.escapeId(str); 58 | } 59 | 60 | disconnect(cb) { 61 | if (this.pool) { 62 | this.pool.drain(); 63 | } else { 64 | this._connection.close(); 65 | } 66 | 67 | if (cb && typeof cb === 'function') { 68 | cb(null); 69 | } else { 70 | return new Promise((resolve, reject) => { 71 | resolve(); 72 | }); 73 | } 74 | } 75 | 76 | release() { 77 | if (!this.pool) throw new Error("You cannot release a non-pooled connection from a connection pool!"); 78 | this.pool.release(this._connection); 79 | } 80 | } 81 | 82 | module.exports = Single; 83 | -------------------------------------------------------------------------------- /drivers/mysql/adapter.js: -------------------------------------------------------------------------------- 1 | const QueryExec = require('./query_exec'); 2 | 3 | class Adapter extends QueryExec { 4 | constructor(settings) { 5 | super(settings); 6 | 7 | // Verify that an instance of Node QueryBuilder was passed in 8 | if (!settings || typeof settings !== 'object') { 9 | throw new Error("No connection settings provided to initialize QueryBuilder!"); 10 | } 11 | 12 | this._original_settings = settings; 13 | this._connection_settings = settings; 14 | 15 | // Enable debugging if necessary 16 | this.debugging = false; 17 | if (this._connection_settings.hasOwnProperty('debug') && this._connection_settings.debug === true) { 18 | this.debugging = true; 19 | delete this._connection_settings.debug; 20 | } 21 | 22 | // Verify that required fields are provided... 23 | if (Object.keys(this._connection_settings).length === 0) throw new Error("No connection information provided!"); 24 | if (!this._connection_settings.hasOwnProperty('host')) this._connection_settings.host = 'localhost'; 25 | if (!this._connection_settings.hasOwnProperty('user')) { console.log("Settings:", this._connection_settings); throw new Error("No user property provided. Hint: It can be NULL"); } 26 | 27 | this.map_connection_settings(); 28 | } 29 | 30 | // **************************************************************************** 31 | // Map generic NQB connection settings to node-mysql's format 32 | // ---- 33 | // NOTE: MySQL connection settings names are the same as Node Querybuilder, 34 | // it's just good practice to go ahead and do this in case things change. 35 | // **************************************************************************** 36 | map_connection_settings() { 37 | const settings = JSON.parse(JSON.stringify(this._connection_settings)); 38 | 39 | this._connection_settings = { 40 | host: settings.host, 41 | user: settings.user, 42 | password: settings.password, 43 | }; 44 | 45 | if (settings.hasOwnProperty('database')) { 46 | this._connection_settings.database = settings.database; 47 | delete settings.database; 48 | } 49 | if (settings.hasOwnProperty('port')) { 50 | this._connection_settings.port = settings.port; 51 | delete settings.port; 52 | } 53 | 54 | // Remove mapped settings: 55 | delete settings.host; 56 | delete settings.user; 57 | delete settings.password; 58 | 59 | // Merge any driver-specific settings into connection settings 60 | this._connection_settings = Object.assign(this._connection_settings, settings); 61 | } 62 | } 63 | 64 | module.exports = Adapter; 65 | -------------------------------------------------------------------------------- /drivers/mysql/adapters/cluster.js: -------------------------------------------------------------------------------- 1 | const Adapter = require('../adapter.js'); 2 | 3 | class Cluster extends Adapter { 4 | constructor(settings) { 5 | super(settings); 6 | return {}; 7 | } 8 | } 9 | 10 | module.exports = Cluster; 11 | -------------------------------------------------------------------------------- /drivers/mysql/adapters/pool.js: -------------------------------------------------------------------------------- 1 | const mysql = require('mysql'); 2 | const Adapter = require('../adapter.js'); 3 | const Single = require('./single.js'); 4 | 5 | class Pool extends Adapter { 6 | constructor(settings) { 7 | super(settings); 8 | 9 | // Create pool for node-querybuild object if it doesn't already have one. 10 | if (!this.hasOwnProperty('pool') || this._pool.length === 0) { 11 | // Create connection Pool 12 | this._pool = mysql.createPool(this._connection_settings); 13 | 14 | // Test connection pool (asynchronous -- this shouldn't prevent the pool from initially loading) 15 | if (this.debugging === true) { 16 | this._pool.getConnection((err, connection) => { 17 | connection.query('SELECT 1 + 1 AS solution', err => { 18 | connection.release(); 19 | if (err) { 20 | console.error(err); 21 | } else { 22 | console.log('mysql connection pool created'); 23 | } 24 | }); 25 | }); 26 | } 27 | } 28 | } 29 | 30 | pool() { 31 | return this._pool; 32 | } 33 | 34 | get_connection(cb) { 35 | if (!this._pool) { 36 | const error_msg = "Connection pool not available!"; 37 | if (console && console.hasOwnProperty('error')) console.error(error_msg); 38 | throw new Error(error_msg); 39 | } 40 | 41 | const self = this; 42 | const handler = (resolve, reject) => { 43 | self._pool.getConnection((err, connection) => { 44 | if (err) throw err; 45 | 46 | const adapter = new Single(self._original_settings, { 47 | pool: { 48 | pool: self._pool, 49 | connection 50 | } 51 | }); 52 | 53 | if ((!cb || typeof cb !== 'function') && (typeof resolve === 'function' && typeof reject === 'function')) return resolve(adapter); 54 | else if (cb && typeof cb === 'function') return cb(adapter); 55 | throw ERRORS.NO_VALID_RESULTS_HANDLER; 56 | }); 57 | } 58 | 59 | if (!cb || (cb && typeof cb !== 'function')) { 60 | return new Promise(handler); 61 | } else { 62 | handler(); 63 | } 64 | } 65 | 66 | disconnect(cb) { 67 | if (!cb || (cb && typeof cb !== 'function')) { 68 | return new Promise((resolve, reject) => { 69 | this._pool.end((err) => { 70 | if (err) { 71 | reject(err); 72 | } else { 73 | resolve(); 74 | } 75 | }); 76 | }); 77 | } else { 78 | this._pool.end(cb); 79 | } 80 | } 81 | } 82 | 83 | module.exports = Pool; 84 | -------------------------------------------------------------------------------- /drivers/mysql/adapters/single.js: -------------------------------------------------------------------------------- 1 | // Load MySQL Driver 2 | const mysql = require('mysql'); 3 | const Adapter = require('../adapter.js'); 4 | 5 | class Single extends Adapter { 6 | constructor(settings, pool) { 7 | super(settings, pool); 8 | 9 | // Set defaults 10 | this.pool = null; 11 | this._connection = null; 12 | 13 | // If the Pool object is instatiating this Adapter, use it's connection 14 | if (pool && pool.pool) { 15 | this.pool = pool.pool.pool; // NOTE: That truely is insane looking... ¯\_(ツ)_/¯ 16 | this._connection = pool.pool.connection; 17 | } 18 | // Otherwise, let's create a new connection 19 | else { 20 | this._connection = new mysql.createConnection(this._connection_settings); 21 | } 22 | 23 | if (!this._connection) throw new Error("No connection could be established!"); 24 | 25 | // this.qb = this.get_query_builder(); 26 | // this.qe = this.get_query_exec(this.qb, this._connection); 27 | } 28 | 29 | connection_settings() { 30 | return this._connection_settings; 31 | } 32 | 33 | connect(cb) { 34 | if (!cb || (cb && typeof cb !== 'function')) { 35 | return new Promise((resolve, reject) => { 36 | return this._connection.connect((err) => { 37 | if (err) { 38 | reject(err); 39 | } else { 40 | resolve(); 41 | } 42 | }); 43 | }); 44 | } else { 45 | this._connection.connect(cb); 46 | } 47 | } 48 | 49 | connection() { 50 | return this._connection; 51 | } 52 | 53 | escape_id(str) { 54 | return this._connection.escapeId(str); 55 | } 56 | 57 | disconnect(cb) { 58 | if (!cb || (cb && typeof cb !== 'function')) { 59 | return new Promise((resolve, reject) => { 60 | this._connection.end((err) => { 61 | if (err) { 62 | reject(err); 63 | } else { 64 | resolve(); 65 | } 66 | }); 67 | }); 68 | } else { 69 | this._connection.end(cb); 70 | } 71 | } 72 | 73 | release() { 74 | if (!this.pool) throw new Error("You cannot release a non-pooled connection from a connection pool!"); 75 | this.pool.releaseConnection(this._connection); 76 | } 77 | } 78 | 79 | module.exports = Single; 80 | -------------------------------------------------------------------------------- /drivers/template/connect.js: -------------------------------------------------------------------------------- 1 | /** 2 | * [DRIVERNAME] Connection Library 3 | * Version 0.0.1 4 | * 5 | * [PUT DESCRIPTION HERE] 6 | * 7 | * Supported connection types: 8 | * - Single 9 | * - Pool 10 | * - PoolCluster 11 | * 12 | * Dependencies: 13 | * 14 | **/ 15 | 16 | let connect, Standard, Pool, PoolCluster; 17 | 18 | // **************************************************************************** 19 | // Sets up a standard one-time connection (no pooling). This one is used by the 20 | // other two methods to stay DRY. 21 | // ----- 22 | // @param Object settings Connection settings 23 | // @return Object Connection handle 24 | // **************************************************************************** 25 | Standard = settings => { 26 | 27 | }; 28 | 29 | // **************************************************************************** 30 | // Sets up a connection pool 31 | // ----- 32 | // @param Object settings Connection settings 33 | // @return Object Connection handle 34 | // **************************************************************************** 35 | Pool = settings => { 36 | 37 | }; 38 | 39 | // **************************************************************************** 40 | // Sets up a cluster of pooled connections to different servers for load 41 | // balancing and failover 42 | // ----- 43 | // @param Object settings Connection settings 44 | // @return Object Connection handle 45 | // **************************************************************************** 46 | PoolCluster = settings => { 47 | 48 | }; 49 | 50 | // **************************************************************************** 51 | // Generic function for creating connections to databases 52 | // ----- 53 | // @param Object settings Connection settings (including the type) 54 | // @return Object Connection handle 55 | // **************************************************************************** 56 | connect = (settings,type) => { 57 | type = type || 'single'; 58 | 59 | let connection = null; 60 | 61 | switch(type) { 62 | case 'single': 63 | connection = Standard(settings); 64 | break; 65 | case 'pool': 66 | connection = Pool(settings); 67 | break; 68 | case 'cluster': 69 | connection = PoolCluster(settings); 70 | break; 71 | default: 72 | throw new Error("Invalid connection type specified!"); 73 | break; 74 | } 75 | 76 | if (connection === null) { 77 | throw new Error("A connection could not be established!"); 78 | } 79 | return connection; 80 | } 81 | 82 | exports.connect = connect; -------------------------------------------------------------------------------- /examples/connection_pool_example.js: -------------------------------------------------------------------------------- 1 | const settings = { 2 | host: 'localhost', 3 | database: 'mydatabase', 4 | user: 'myuser', 5 | password: 'MyP@ssw0rd' 6 | }; 7 | const nqb = require('node-querybuilder'); 8 | const pool = new QueryBuilder(settings, 'mysql', 'pool'); 9 | 10 | pool.get_connection(qb => { 11 | qb.select('name', 'position') 12 | .where({type: 'rocky', 'diameter <': 12000}) 13 | .get('planets', (err,response) => { 14 | if (err) return console.error("Uh oh! Couldn't get results: " + err.msg); 15 | 16 | // SELECT `name`, `position` FROM `planets` WHERE `type` = 'rocky' AND `diameter` < 12000 17 | console.log("Query Ran: " + qb.last_query()); 18 | 19 | // [{name: 'Mercury', position: 1}, {name: 'Mars', position: 4}] 20 | console.dir(response); 21 | } 22 | ); 23 | }); -------------------------------------------------------------------------------- /examples/connection_single_example.js: -------------------------------------------------------------------------------- 1 | const settings = { 2 | host: 'localhost', 3 | database: 'mydatabase', 4 | user: 'myuser', 5 | password: 'MyP@ssw0rd' 6 | }; 7 | const nqb = require('node-querybuilder'); 8 | const qb = new QueryBuilder(settings, 'mysql', 'single'); 9 | 10 | qb.select('name', 'position') 11 | .where({type: 'rocky', 'diameter <': 12000}) 12 | .get('planets', (err,response) => { 13 | if (err) return console.error("Uh oh! Couldn't get results: " + err.msg); 14 | 15 | // SELECT `name`, `position` FROM `planets` WHERE `type` = 'rocky' AND `diameter` < 12000 16 | console.log("Query Ran: " + qb.last_query()); 17 | 18 | // [{name: 'Mercury', position: 1}, {name: 'Mars', position: 4}] 19 | console.dir(response); 20 | } 21 | ); -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * QueryBuilder for Node.js 3 | * (C) Kyle Farris 2014-2018 4 | * kyle@chomponllc.com 5 | * 6 | * A generic Query Builder for any SQL or NOSQL database adapter. 7 | * 8 | * Current adapters: 9 | * - MySQL 10 | * 11 | * Requested Adapters: 12 | * - MSSQL 13 | * - postgres 14 | * - sqlite 15 | * - sqlite3 16 | * - oracle 17 | * - mongo 18 | * 19 | * Dual licensed under the MIT and GPL licenses. 20 | * 21 | * Permission is hereby granted, free of charge, to any person obtaining a 22 | * copy of this software and associated documentation files (the 23 | * "Software"), to deal in the Software without restriction, including 24 | * without limitation the rights to use, copy, modify, merge, publish, 25 | * distribute, sublicense, and/or sell copies of the Software, and to 26 | * permit persons to whom the Software is furnished to do so, subject to 27 | * the following conditions: 28 | * 29 | * The above copyright notice and this permission notice shall be included 30 | * in all copies or substantial portions of the Software. 31 | * 32 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 33 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 34 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 35 | * IN NO EVENT SHALL KEVIN VAN ZONNEVELD BE LIABLE FOR ANY CLAIM, DAMAGES 36 | * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 37 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 38 | * OTHER DEALINGS IN THE SOFTWARE. 39 | * 40 | **/ 41 | class QueryBuilder { 42 | constructor(settings, driver, type) { 43 | this.settings = (settings ? Object.assign({}, settings) : {}); 44 | this.driver = driver || 'mysql'; 45 | this.connection_type = type || 'single'; 46 | this.drivers = require('./drivers/drivers.json'); 47 | this.driver_version = 'default'; 48 | this.driver_info = null; 49 | this.pool = []; 50 | 51 | this.get_driver_info(); 52 | this.get_connection_type(); 53 | return this.get_adapter(); 54 | } 55 | 56 | // **************************************************************************** 57 | // Get information about the driver the user wants to use and modify QB object 58 | // ----- 59 | // @return Object Modified QueryBuilder object 60 | // **************************************************************************** 61 | get_driver_info() { 62 | // A driver must be specified 63 | if (typeof this.driver !== 'string') { 64 | throw new Error("No database driver specified!"); 65 | } 66 | 67 | this.driver = this.driver.toLowerCase(); 68 | 69 | // Verify that the driver is one we fundamentally support 70 | if (Object.keys(this.drivers).indexOf(this.driver) === -1) { 71 | throw new Error("Invalid driver specified!"); 72 | } 73 | 74 | // Determine version of driver to use 75 | if (this.settings.hasOwnProperty('version') && /^(string|number)$/i.test(typeof this.settings.version)) { 76 | this.driver_version = this.settings.version; 77 | delete this.settings.version; 78 | } 79 | 80 | // Retrieve info about driver if available, error if not 81 | if (this.drivers[this.driver].versions.hasOwnProperty(this.driver_version)) { 82 | if (this.drivers[this.driver].versions[this.driver_version].hasOwnProperty('version')) { 83 | this.driver_info = this.drivers[this.driver].versions[this.drivers[this.driver].versions[this.driver_version].version]; 84 | } else { 85 | this.driver_info = this.drivers[this.driver].versions[this.driver_version]; 86 | } 87 | } else { 88 | throw new Error(`${this.driver_version} is not a version of the ${this.driver} driver that this library specifically supports. Try being more generic.`); 89 | } 90 | 91 | // Fail if specified driver is inactive 92 | if (this.driver_info.active === false) { 93 | const err = (this.driver_version == 'default' ? 'The default version' : "Version " + this.driver_version) 94 | + ` of the ${this.driver} driver you are attempting to load is not currently available!`; 95 | throw new Error(err); 96 | } 97 | } 98 | 99 | // **************************************************************************** 100 | // Determine the type of connection (single, pool, cluster, etc...) 101 | // ----- 102 | // @return Object Modified QueryBuilder object 103 | // **************************************************************************** 104 | get_connection_type() { 105 | if (!Object.keys(this.drivers[this.driver].connection_types).includes(this.connection_type)) { 106 | throw new Error(`You have specified a invalid database connection method: ${this.connection_type}`); 107 | } 108 | if (this.drivers[this.driver].connection_types[this.connection_type] !== true) { 109 | throw new Error(`You cannot connect to a ${this.driver} database using the ${this.connection_type} connection type using this library.`); 110 | } 111 | return this; 112 | } 113 | 114 | // **************************************************************************** 115 | // Returns the single, pool, or cluster adapter 116 | // ----- 117 | // @return VOID This method responds asychronously via a callback 118 | // **************************************************************************** 119 | get_adapter() { 120 | const settings = Object.assign({}, this.settings); 121 | 122 | let Single; 123 | 124 | try { 125 | switch (this.connection_type) { 126 | case 'cluster': 127 | const Cluster = require(`${this.driver_info.path}/adapters/cluster.js`); 128 | return new Cluster(settings); 129 | case 'pool': 130 | const Pool = require(`${this.driver_info.path}/adapters/pool.js`) 131 | return new Pool(settings); 132 | case 'single': 133 | Single = require(`${this.driver_info.path}/adapters/single.js`) 134 | return new Single(settings, {}); 135 | default: 136 | Single = require(`${this.driver_info.path}/adapters/single.js`) 137 | return new Single(settings, {}); 138 | } 139 | } catch(e) { 140 | throw new Error(`Couldn't load the "${this.connection_type}" Adapter library for ${this.driver} (${JSON.stringify(this.settings)}): ${e}`); 141 | } 142 | } 143 | } 144 | 145 | module.exports = QueryBuilder; 146 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-querybuilder", 3 | "version": "2.1.0", 4 | "author": "Kyle Farris ", 5 | "description": "Modeled after Codeigniter's QueryBuilder. Build and execute queries in a safe and database-agnostic way.", 6 | "keywords": [ 7 | "nodejs", 8 | "node", 9 | "mysql", 10 | "active record", 11 | "activerecord", 12 | "querybuilder", 13 | "query builder", 14 | "codeigniter", 15 | "postgres", 16 | "mssql", 17 | "mongo", 18 | "sqlite", 19 | "translator" 20 | ], 21 | "bugs": "https://github.com/kylefarris/node-querybuilder/issues", 22 | "scripts": { 23 | "test": "make test" 24 | }, 25 | "homepage": "http://kylefarris.github.io/node-querybuilder/", 26 | "repository": { 27 | "type": "git", 28 | "url": "git@github.com:kylefarris/node-querybuilder.git" 29 | }, 30 | "contributors": [ 31 | "Martin Tajur ", 32 | "Daniel Bretoi ", 33 | "Daehyub Kim " 34 | ], 35 | "dependencies": { 36 | "mysql": "^2.10.1", 37 | "tedious": "^2.6.3", 38 | "tedious-connection-pool": "^1.0.5", 39 | "tsqlstring": "^1.0.0" 40 | }, 41 | "devDependencies": { 42 | "chai": "^4.1.2", 43 | "mocha": "*" 44 | }, 45 | "main": "index.js", 46 | "engines": { 47 | "node": ">=8.0" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /test/05-multiple-drivers.js: -------------------------------------------------------------------------------- 1 | const should = require('chai').should(); 2 | const expect = require('chai').expect; 3 | const configs = require('./configs'); 4 | 5 | const QueryBuilder = require('../index.js'); 6 | const my_pool = new QueryBuilder(configs.mysql, 'mysql', 'pool'); 7 | const ms_pool = new QueryBuilder(configs.mssql, 'mssql', 'pool'); 8 | let my_pool_settings, ms_pool_settings; 9 | 10 | const compare_connections = (done) => { 11 | try { 12 | expect(my_pool_settings, 'should have port property').to.have.property('port'); 13 | expect(ms_pool_settings, 'should have connection_settings property').to.have.property('connection_settings'); 14 | 15 | const port1 = my_pool_settings.port; 16 | const port2 = ms_pool_settings.connection_settings.options.port; 17 | 18 | port1.should.not.be.eql(port2); 19 | 20 | done(); 21 | } catch(e) { 22 | done(e); 23 | } 24 | }; 25 | 26 | describe('Multiple Drivers', () => { 27 | it('should not get confused by what pool/settings to use', done => { 28 | let connections_established = 0; 29 | 30 | my_pool.get_connection(qb1 => { 31 | my_pool_settings = qb1.connection_settings(); 32 | connections_established++; 33 | if (connections_established >= 2) compare_connections(done); 34 | }); 35 | ms_pool.get_connection(qb2 => { 36 | ms_pool_settings = qb2.connection_settings(); 37 | connections_established++; 38 | if (connections_established >= 2) compare_connections(done); 39 | }); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /test/configs.js: -------------------------------------------------------------------------------- 1 | const configs = { 2 | mysql: { 3 | host: '127.0.0.1', 4 | database: 'mock_db', 5 | user: 'travis', 6 | version: '2.5.4', 7 | port: 3306, 8 | debug: false, 9 | }, 10 | mssql: { 11 | host: 'localhost', 12 | database: 'mock_db', 13 | user: 'travis', 14 | password: 'Password123', 15 | version: '4.1.0', 16 | port: 1433, 17 | options: { 18 | encrypt: false 19 | } 20 | }, 21 | }; 22 | module.exports = configs; 23 | -------------------------------------------------------------------------------- /test/create_mock_dbs.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | ./test/mysql/create_mysql_mock.sh 3 | ./test/mssql/create_mssql_mock.sh -------------------------------------------------------------------------------- /test/mssql/00-AA-tests-general.js: -------------------------------------------------------------------------------- 1 | const should = require('chai').should(); 2 | 3 | describe('MSSQL: QueryBuilder', () => { 4 | it('actually exists and can be initialized', () => { 5 | const QueryBuilder = require('../../drivers/mssql/query_builder.js'); 6 | const qb = new QueryBuilder(); 7 | qb.should.be.instanceOf(QueryBuilder); 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /test/mssql/01-tests-distinct.js: -------------------------------------------------------------------------------- 1 | const should = require('chai').should(); 2 | const expect = require('chai').expect; 3 | const QueryBuilder = require('../../drivers/mssql/query_builder.js'); 4 | const qb = new QueryBuilder(); 5 | 6 | describe('MSSQL: distinct()', () => { 7 | it('should exist', () => { 8 | should.exist(qb.distinct); 9 | }); 10 | it('should be a function', () => { 11 | qb.distinct.should.be.a('function'); 12 | }); 13 | it('should override the default distinct_clause with the "DISTINCT " keyword', () => { 14 | qb.reset_query(); 15 | qb.distinct(); 16 | qb.distinct_clause.should.eql(['DISTINCT ']); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /test/mssql/01-tests-from.js: -------------------------------------------------------------------------------- 1 | const should = require('chai').should(); 2 | const QueryBuilder = require('../../drivers/mssql/query_builder.js'); 3 | const qb = new QueryBuilder(); 4 | 5 | describe('MSSQL: from()', () => { 6 | it('should exist', () => { 7 | should.exist(qb.from); 8 | }); 9 | it('should be a function', () => { 10 | qb.from.should.be.a('function'); 11 | }); 12 | it('should have an array to put fields into', () => { 13 | qb.should.have.property('from_array'); 14 | }); 15 | it('should have an empty array to put fields into at the beginning', () => { 16 | qb.from_array.should.be.empty; 17 | }); 18 | it('should add an item to an array and escape it properly', () => { 19 | qb.from('universe'); 20 | qb.from_array.should.eql(['[universe]']); 21 | }) 22 | it('should accept a comma-delimited string of items and trim and escape each properly', () => { 23 | qb.reset_query(); 24 | qb.from('universe,galaxy , star_system, planet'); 25 | qb.from_array.should.eql(['[universe]','[galaxy]','[star_system]','[planet]']); 26 | }); 27 | it('should have an empty array after resetting', () => { 28 | qb.reset_query(); 29 | qb.from_array.should.be.empty; 30 | }); 31 | it('should be allowed to be called multiple times to add multiple items to the from array', () => { 32 | qb.reset_query(); 33 | qb.from('universe').from('galaxy').from('star_system').from('planet'); 34 | qb.from_array.should.eql(['[universe]','[galaxy]','[star_system]','[planet]']); 35 | }); 36 | it('should accept an array of items and add them individually to the from array', () => { 37 | qb.reset_query(); 38 | qb.from(['universe','galaxy','star_system','planet']); 39 | qb.from_array.should.eql(['[universe]','[galaxy]','[star_system]','[planet]']); 40 | }); 41 | it('should not double-escape an item', () => { 42 | qb.reset_query(); 43 | qb.from('[do]'); 44 | qb.from_array.should.eql(['[do]']); 45 | }); 46 | it('should not double-escape items when provided with an array of pre-escaped items', () => { 47 | qb.reset_query(); 48 | qb.from(['[universe]','[galaxy]','[star_system]']); 49 | qb.from_array.should.eql(['[universe]','[galaxy]','[star_system]']); 50 | }); 51 | it('should not double-escape items when provided with an array of pre-escaped items but should escpae non-pre-escaped items', () => { 52 | qb.reset_query(); 53 | qb.from(['[universe]','galaxy','[star_system]']); 54 | qb.from_array.should.eql(['[universe]','[galaxy]','[star_system]']); 55 | }); 56 | it('should allow for aliases and it should escape them properly', () => { 57 | qb.reset_query(); 58 | qb.from('universe u'); 59 | qb.from_array.should.eql(['[universe] [u]']); 60 | }); 61 | it('should allow for the word AS to be used to alias an item', () => { 62 | qb.reset_query(); 63 | qb.from('universe as u'); 64 | qb.from_array.should.eql(['[universe] as [u]']); 65 | }); 66 | it('should allow for an array of item + aliases and it should escape them all properly', () => { 67 | qb.reset_query(); 68 | qb.from(['universe u', 'galaxy g']); 69 | qb.from_array.should.eql(['[universe] [u]','[galaxy] [g]']); 70 | }); 71 | it('should allow for an array of item + aliases that are pre-escaped and it should not double-escape them', () => { 72 | qb.reset_query(); 73 | qb.from(['[universe] [u]', '[galaxy] [g]']); 74 | qb.from_array.should.eql(['[universe] [u]','[galaxy] [g]']); 75 | }); 76 | it('should allow for an array of item + aliases where some are pre-escaped and it should not double-escape pre-escaped items', () => { 77 | qb.reset_query(); 78 | qb.from(['[universe] u', 'galaxy [g]']); 79 | qb.from_array.should.eql(['[universe] [u]','[galaxy] [g]']); 80 | }); 81 | it('should add aliases to alias-tracking array', () => { 82 | qb.reset_query(); 83 | qb.from(['[universe] [u]', '[galaxy] [g]']); 84 | qb.aliased_tables.should.eql(['u','g']); 85 | }); 86 | it('should allow for an comma-delimited list of item + aliases and it should escape them all properly', () => { 87 | qb.reset_query(); 88 | qb.from(['universe u, galaxy g']); 89 | qb.from_array.should.eql(['[universe] [u]','[galaxy] [g]']); 90 | }); 91 | it('should allow for namespacing in field name (host.db.table)', () => { 92 | qb.reset_query(); 93 | qb.from('star_system.planet'); 94 | qb.from_array.should.eql(['[star_system].[planet]']); 95 | 96 | qb.reset_query(); 97 | qb.from('galaxy.star_system.planet'); 98 | qb.from_array.should.eql(['[galaxy].[star_system].[planet]']); 99 | }); 100 | it('should allow for namespacing in field name (host.db.table.column) + alias', () => { 101 | qb.reset_query(); 102 | qb.from('universe.galaxy.star_system planet'); 103 | qb.from_array.should.eql(['[universe].[galaxy].[star_system] [planet]']); 104 | }); 105 | it('should allow for namespacing in field name (host.db.table.column) + alias (declare with AS)', () => { 106 | qb.reset_query(); 107 | qb.from('universe.galaxy.star_system as planet'); 108 | qb.from_array.should.eql(['[universe].[galaxy].[star_system] as [planet]']); 109 | }); 110 | it('should accept but ignore empty strings and empty strings within arrays', () => { 111 | qb.reset_query(); 112 | qb.from(''); 113 | qb.from_array.should.be.empty; 114 | 115 | qb.reset_query(); 116 | qb.from(['','']); 117 | qb.from_array.should.be.empty; 118 | 119 | qb.reset_query(); 120 | qb.from(['','foobar']); 121 | qb.from_array.should.eql(['[foobar]']); 122 | }); 123 | }); 124 | -------------------------------------------------------------------------------- /test/mssql/01-tests-groupby.js: -------------------------------------------------------------------------------- 1 | const should = require('chai').should(); 2 | const expect = require('chai').expect; 3 | const QueryBuilder = require('../../drivers/mssql/query_builder.js'); 4 | const qb = new QueryBuilder(); 5 | 6 | describe('MSSQL: group_by()', () => { 7 | it('should exist', () => { 8 | should.exist(qb.group_by); 9 | }); 10 | it('should be a function', () => { 11 | qb.group_by.should.be.a('function'); 12 | }); 13 | it('should have an array to put fields into', () => { 14 | qb.should.have.property('group_by_array'); 15 | }); 16 | it('should have an empty array to put fields into at the beginning', () => { 17 | qb.group_by_array.should.be.empty; 18 | }); 19 | it('should accept a single field in string form', () => { 20 | qb.reset_query(); 21 | qb.group_by('planet_type'); 22 | qb.group_by_array.should.eql(['[planet_type]']); 23 | }); 24 | it('should accept a multiple fields delimited by commas', () => { 25 | qb.reset_query(); 26 | qb.group_by('planet_type, planet_position'); 27 | qb.group_by_array.should.eql(['[planet_type]','[planet_position]']); 28 | }); 29 | it('should accept an array of fields', () => { 30 | qb.reset_query(); 31 | qb.group_by(['planet_type', 'planet_position']); 32 | qb.group_by_array.should.eql(['[planet_type]','[planet_position]']); 33 | }); 34 | it('should not accept anything but a string or an array of strings', () => { 35 | qb.reset_query(); 36 | expect(() => qb.group_by(), 'nothing provided').to.throw(Error); 37 | expect(() => qb.group_by(null), 'null provided').to.throw(Error); 38 | expect(() => qb.group_by(false), 'false provided').to.throw(Error); 39 | expect(() => qb.group_by(true), 'true provided').to.throw(Error); 40 | expect(() => qb.group_by({}), 'empty object provided').to.throw(Error); 41 | expect(() => qb.group_by(3), 'integer provided').to.throw(Error); 42 | expect(() => qb.group_by(3.5), 'float provided').to.throw(Error); 43 | expect(() => qb.group_by([]), 'empty array provided').to.throw(Error); 44 | expect(() => qb.group_by([1,2]), 'array of numbers provided').to.throw(Error); 45 | expect(() => qb.group_by(''), 'empty string provided').to.throw(Error); 46 | 47 | // valid string 48 | expect(() => qb.group_by('planet_type'), 'valid string provided').to.not.throw(Error); 49 | expect(() => qb.group_by(['planet_type']), 'array of string(s) provided').to.not.throw(Error); 50 | 51 | }); 52 | }); 53 | -------------------------------------------------------------------------------- /test/mssql/01-tests-having.js: -------------------------------------------------------------------------------- 1 | const should = require('chai').should(); 2 | const expect = require('chai').expect; 3 | const QueryBuilder = require('../../drivers/mssql/query_builder.js'); 4 | const qb = new QueryBuilder(); 5 | 6 | describe('MSSQL: having()', () => { 7 | it('should exist', () => { 8 | should.exist(qb.having); 9 | }); 10 | it('should be a function', () => { 11 | qb.having.should.be.a('function'); 12 | }); 13 | it('should have an array to put fields into', () => { 14 | qb.should.have.property('having_array'); 15 | }); 16 | it('should accept a string only in this format: a [>|<|<>|>=|<=|=|!=] b for the first parameter', () => { 17 | qb.reset_query(); 18 | qb.having('planet_class > "M"'); 19 | qb.having_array.should.eql(["[planet_class] > 'M'"]); 20 | 21 | qb.reset_query(); 22 | qb.having('planet_class < "M"'); 23 | qb.having_array.should.eql(["[planet_class] < 'M'"]); 24 | 25 | qb.reset_query(); 26 | qb.having('planet_class <> "M"'); 27 | qb.having_array.should.eql(["[planet_class] <> 'M'"]); 28 | 29 | qb.reset_query(); 30 | qb.having('planet_class >= "M"'); 31 | qb.having_array.should.eql(["[planet_class] >= 'M'"]); 32 | 33 | qb.reset_query(); 34 | qb.having('planet_class <= "M"'); 35 | qb.having_array.should.eql(["[planet_class] <= 'M'"]); 36 | 37 | qb.reset_query(); 38 | qb.having('planet_class = "M"'); 39 | qb.having_array.should.eql(["[planet_class] = 'M'"]); 40 | 41 | qb.reset_query(); 42 | qb.having('planet_class != "M"'); 43 | qb.having_array.should.eql(["[planet_class] != 'M'"]); 44 | }); 45 | it('should not accept compound conditions in this format: a [>|<|<>|>=|<=|=|!=] b[, repeat[, etc...]]', () => { 46 | qb.reset_query(); 47 | expect(() => qb.having('planet_class = "M", sentient_life = 1'), 'two conditions provided').to.throw(Error); 48 | }); 49 | it('should accept an array of conditions and prepend AND to each condition following the first one', () => { 50 | qb.reset_query(); 51 | qb.having(["planet_class = 'M'", 'sentient_life = 1']); 52 | qb.having_array.should.eql(["[planet_class] = 'M'", 'AND [sentient_life] = 1']); 53 | }); 54 | it('should accept an object of conditions and prepend AND to each condition following the first one', () => { 55 | qb.reset_query(); 56 | const object = {planet_class: 'M', sentient_life: 1}; 57 | object['planet_order <='] = 3; 58 | qb.having(object); 59 | qb.having_array.should.eql(["[planet_class] = 'M'", 'AND [sentient_life] = 1','AND [planet_order] <= 3']); 60 | }); 61 | it('should not accept anything but a non-empty array, object, or string', () => { 62 | qb.reset_query(); 63 | expect(() => qb.group_by(), 'nothing provided').to.throw(Error); 64 | expect(() => qb.group_by(null), 'null provided').to.throw(Error); 65 | expect(() => qb.group_by(false), 'false provided').to.throw(Error); 66 | expect(() => qb.group_by(true), 'true provided').to.throw(Error); 67 | expect(() => qb.group_by({}), 'empty object provided').to.throw(Error); 68 | expect(() => qb.group_by(3), 'integer provided').to.throw(Error); 69 | expect(() => qb.group_by(3.5), 'float provided').to.throw(Error); 70 | expect(() => qb.group_by([]), 'empty array provided').to.throw(Error); 71 | expect(() => qb.group_by([1,2]), 'array of numbers provided').to.throw(Error); 72 | expect(() => qb.group_by(''), 'empty string provided').to.throw(Error); 73 | 74 | // valid string 75 | expect(() => qb.group_by('planet_type = "M"'), 'valid string provided').to.not.throw(Error); 76 | expect(() => qb.group_by(['planet_type = "M"']), 'array of string(s) provided').to.not.throw(Error); 77 | }); 78 | it('should accept 2 parameters where the first one is the field with optional condition and the second one is the value', () => { 79 | qb.reset_query(); 80 | qb.having('planet_class','M'); 81 | qb.having_array.should.eql(["[planet_class] = 'M'"]); 82 | }); 83 | it('should not escape conditions if asked not to', () => { 84 | qb.reset_query(); 85 | qb.having(["planet_class = 'M'", 'sentient_life = 1'], null, false); 86 | qb.having_array.should.eql(["planet_class = 'M'", 'AND sentient_life = 1']); 87 | }); 88 | it('should be chainable', () => { 89 | qb.reset_query(); 90 | qb.having('planet_class','M').having('sentient_life',true).having('planet_order <=',3); 91 | qb.having_array.should.eql(["[planet_class] = 'M'", 'AND [sentient_life] = 1','AND [planet_order] <= 3']); 92 | }); 93 | }); 94 | 95 | describe('MSSQL: or_having()', () => { 96 | it('should exist', () => { 97 | should.exist(qb.or_having); 98 | }); 99 | it('should be a function', () => { 100 | qb.or_having.should.be.a('function'); 101 | }); 102 | it('should accept an array of conditions and prepend OR to each condition following the first one', () => { 103 | qb.reset_query(); 104 | qb.or_having(["planet_class = 'M'", 'sentient_life = 1']); 105 | qb.having_array.should.eql(["[planet_class] = 'M'", 'OR [sentient_life] = 1']); 106 | }); 107 | it('should be chainable with normal having', () => { 108 | qb.reset_query(); 109 | qb.having('planet_class','M').having('sentient_life',true).or_having('planet_order <=',3); 110 | qb.having_array.should.eql(["[planet_class] = 'M'", 'AND [sentient_life] = 1','OR [planet_order] <= 3']); 111 | }); 112 | }); 113 | -------------------------------------------------------------------------------- /test/mssql/01-tests-limit.js: -------------------------------------------------------------------------------- 1 | const should = require('chai').should(); 2 | const expect = require('chai').expect; 3 | const QueryBuilder = require('../../drivers/mssql/query_builder.js'); 4 | const qb = new QueryBuilder(); 5 | 6 | describe('MSSQL: limit()', () => { 7 | it('should exist', () => { 8 | should.exist(qb.limit); 9 | }); 10 | it('should be a function', () => { 11 | qb.limit.should.be.a('function'); 12 | }); 13 | it('should have an array to put fields into', () => { 14 | qb.should.have.property('limit_to'); 15 | }); 16 | it('should have an empty array to put fields into at the beginning', () => { 17 | qb.limit_to.should.be.empty; 18 | }); 19 | it('should require an integer (or integer in string form) in first parameter', () => { 20 | qb.reset_query(); 21 | expect(() => qb.limit(5), 'integer provided').to.not.throw(Error); 22 | expect(() => qb.limit('5'), '5 in string form provided').to.not.throw(Error); 23 | expect(() => qb.limit(5.7), 'float provided').to.throw(Error); 24 | expect(() => qb.limit('5.7'), 'float provided').to.throw(Error); 25 | expect(() => qb.limit('abc'), 'alpha provided').to.throw(Error); 26 | expect(() => qb.limit('abc7'), 'alpha numerics provided').to.throw(Error); 27 | expect(() => qb.limit(), 'nothing provided').to.throw(Error); 28 | expect(() => qb.limit(null), 'null provided').to.throw(Error); 29 | expect(() => qb.limit(true), 'true provided').to.throw(Error); 30 | expect(() => qb.limit(false), 'false provided').to.throw(Error); 31 | expect(() => qb.limit(''), 'empty string provided').to.throw(Error); 32 | expect(() => qb.limit({}), 'empty object provided').to.throw(Error); 33 | expect(() => qb.limit([]), 'empty array provided').to.throw(Error); 34 | expect(() => qb.limit([5]), 'array with integer in it provided').to.throw(Error); 35 | }); 36 | it('should allow an integer (or integer in string form) in second parameter. Nothing else is allowed.', () => { 37 | qb.reset_query(); 38 | expect(() => qb.limit(10,5), 'integer provided').to.not.throw(Error); 39 | expect(() => qb.limit(10,'5'), '5 in string form provided').to.not.throw(Error); 40 | expect(() => qb.limit(10,5.7), 'float provided').to.throw(Error); 41 | expect(() => qb.limit(10,'5.7'), 'float provided').to.throw(Error); 42 | expect(() => qb.limit(10,'abc'), 'alpha provided').to.throw(Error); 43 | expect(() => qb.limit(10,'abc7'), 'alphanumerics provided').to.throw(Error); 44 | expect(() => qb.limit(10,null), 'null provided').to.throw(Error); 45 | expect(() => qb.limit(10,true), 'true provided').to.throw(Error); 46 | expect(() => qb.limit(10,false), 'false provided').to.throw(Error); 47 | expect(() => qb.limit(10,''), 'empty string provided').to.throw(Error); 48 | expect(() => qb.limit(10,{}), 'empty object provided').to.throw(Error); 49 | expect(() => qb.limit(10,[]), 'empty array provided').to.throw(Error); 50 | expect(() => qb.limit(10,[5]), 'array with integer in it provided').to.throw(Error); 51 | }); 52 | it('should override the default limit_to value when a limit is provided', () => { 53 | qb.reset_query(); 54 | qb.limit(10); 55 | qb.limit_to.should.eql([10]); 56 | }); 57 | it('should override the default limit_to and offset_val values when a limit and an offset are provided', () => { 58 | qb.reset_query(); 59 | qb.limit(10,20); 60 | qb.limit_to.should.eql([10]); 61 | qb.offset_val.should.eql([20]); 62 | }); 63 | it('should trim string values that are provided', () => { 64 | qb.reset_query(); 65 | qb.limit('10 '); 66 | qb.limit_to.should.eql([10]); 67 | }); 68 | it('should trim string values that are provided', () => { 69 | qb.reset_query(); 70 | qb.limit(' 10 ',' 12'); 71 | qb.limit_to.should.eql([10]); 72 | qb.offset_val.should.eql([12]); 73 | }); 74 | it('should override values set by any previous calls to itself', () => { 75 | qb.reset_query(); 76 | qb.limit(10); 77 | qb.limit_to.should.eql([10]); 78 | qb.limit(20); 79 | qb.limit_to.should.eql([20]); 80 | }); 81 | it('should be chainable whereby the last call to the method will contain the value(s) used', () => { 82 | qb.reset_query(); 83 | qb.limit(10,5).limit(20).limit(100,30); 84 | qb.limit_to.should.eql([100]); 85 | qb.offset_val.should.eql([30]); 86 | }); 87 | }); 88 | -------------------------------------------------------------------------------- /test/mssql/01-tests-offset.js: -------------------------------------------------------------------------------- 1 | const should = require('chai').should(); 2 | const expect = require('chai').expect; 3 | const QueryBuilder = require('../../drivers/mssql/query_builder.js'); 4 | const qb = new QueryBuilder(); 5 | 6 | describe('MSSQL: offset()', () => { 7 | it('should exist', () => { 8 | should.exist(qb.offset); 9 | }); 10 | it('should be a function', () => { 11 | qb.offset.should.be.a('function'); 12 | }); 13 | it('should have an array to put fields into', () => { 14 | qb.should.have.property('offset_val'); 15 | }); 16 | it('should have an empty array to put fields into at the beginning', () => { 17 | qb.offset_val.should.be.empty; 18 | }); 19 | it('should require an integer (or integer in string form) in first parameter', () => { 20 | qb.reset_query(); 21 | expect(() => qb.offset(5), 'integer provided').to.not.throw(Error); 22 | expect(() => qb.offset('5'), '5 in string form provided').to.not.throw(Error); 23 | expect(() => qb.offset(5.7), 'float provided').to.throw(Error); 24 | expect(() => qb.offset('5.7'), 'float provided').to.throw(Error); 25 | expect(() => qb.offset('abc'), 'alpha provided').to.throw(Error); 26 | expect(() => qb.offset('abc7'), 'alpha numerics provided').to.throw(Error); 27 | expect(() => qb.offset(), 'nothing provided').to.throw(Error); 28 | expect(() => qb.offset(null), 'null provided').to.throw(Error); 29 | expect(() => qb.offset(true), 'true provided').to.throw(Error); 30 | expect(() => qb.offset(false), 'false provided').to.throw(Error); 31 | expect(() => qb.offset(''), 'empty string provided').to.throw(Error); 32 | expect(() => qb.offset({}), 'empty object provided').to.throw(Error); 33 | expect(() => qb.offset([]), 'empty array provided').to.throw(Error); 34 | expect(() => qb.offset([5]), 'array with integer in it provided').to.throw(Error); 35 | }); 36 | it('should override the default offset_val value when a offset is provided', () => { 37 | qb.reset_query(); 38 | qb.offset(10); 39 | qb.offset_val.should.eql([10]); 40 | }); 41 | it('should trim string values that are provided', () => { 42 | qb.reset_query(); 43 | qb.offset('10 '); 44 | qb.offset(' 10'); 45 | qb.offset(' 10 '); 46 | qb.offset_val.should.eql([10]); 47 | }); 48 | it('should override values set by any previous calls to itself', () => { 49 | qb.reset_query(); 50 | qb.offset(10); 51 | qb.offset_val.should.eql([10]); 52 | qb.offset(20); 53 | qb.offset_val.should.eql([20]); 54 | }); 55 | it('should be chainable whereby the last call to the method will contain the value used', () => { 56 | qb.reset_query(); 57 | qb.offset(10).offset(20).offset(100); 58 | qb.offset_val.should.eql([100]); 59 | }); 60 | }); 61 | -------------------------------------------------------------------------------- /test/mssql/01-tests-orderby.js: -------------------------------------------------------------------------------- 1 | const should = require('chai').should(); 2 | const expect = require('chai').expect; 3 | const QueryBuilder = require('../../drivers/mssql/query_builder.js'); 4 | const qb = new QueryBuilder(); 5 | 6 | describe('MSSQL: order_by()', () => { 7 | it('should exist', () => { 8 | should.exist(qb.order_by); 9 | }); 10 | it('should be a function', () => { 11 | qb.order_by.should.be.a('function'); 12 | }); 13 | it('should have an array to put fields into', () => { 14 | qb.should.have.property('order_by_array'); 15 | }); 16 | it('should have an empty array to put fields into at the beginning', () => { 17 | qb.order_by_array.should.be.empty; 18 | }); 19 | it('should require non-empty string or array as first param unless random is provided as second parameter', () => { 20 | expect(() => qb.order_by(), 'nothing provided').to.throw(Error); 21 | expect(() => qb.order_by(null), 'null provided').to.throw(Error); 22 | expect(() => qb.order_by(false), 'false provided').to.throw(Error); 23 | expect(() => qb.order_by(true), 'true provided').to.throw(Error); 24 | expect(() => qb.order_by({}), 'empty object provided').to.throw(Error); 25 | expect(() => qb.order_by(3), 'integer provided').to.throw(Error); 26 | expect(() => qb.order_by(3.5), 'float provided').to.throw(Error); 27 | expect(() => qb.order_by([]), 'empty array provided').to.throw(Error); 28 | expect(() => qb.order_by(''), 'empty string provided').to.throw(Error); 29 | // If random 30 | expect(() => qb.order_by('','rand'), 'empty string and random direction provided').to.not.throw(Error); 31 | expect(() => qb.order_by(undefined,'rand'), 'undefined and random direction provided').to.not.throw(Error); 32 | expect(() => qb.order_by(null,'rand'), 'null and random direction provided').to.not.throw(Error); 33 | expect(() => qb.order_by(false,'rand'), 'false and random direction provided').to.not.throw(Error); 34 | expect(() => qb.order_by([],'rand'), 'empty array and random direction provided').to.not.throw(Error); 35 | }); 36 | it('should accept a field and direction separated by a space as first parameter and escape the field', () => { 37 | qb.reset_query(); 38 | qb.order_by('planet_position asc'); 39 | qb.order_by_array.should.eql(['[planet_position] ASC']); 40 | }); 41 | it('should accept a field and direction as seperate parameters and escape the field', () => { 42 | qb.reset_query(); 43 | qb.order_by('planet_position', 'asc'); 44 | qb.order_by_array.should.eql(['[planet_position] ASC']); 45 | }); 46 | it('should add additional order_by calls to teh order by array', () => { 47 | qb.reset_query(); 48 | qb.order_by('planet_position', 'asc'); 49 | qb.order_by('planet_size', 'desc'); 50 | qb.order_by_array.should.eql(['[planet_position] ASC', '[planet_size] DESC']); 51 | }); 52 | it('should be chainable', () => { 53 | qb.reset_query(); 54 | qb.order_by('planet_position', 'asc').order_by('planet_size', 'desc'); 55 | qb.order_by_array.should.eql(['[planet_position] ASC', '[planet_size] DESC']); 56 | }); 57 | it('should assume ASC when no direction is provided', () => { 58 | qb.reset_query(); 59 | qb.order_by('planet_position'); 60 | qb.order_by_array.should.eql(['[planet_position] ASC']); 61 | }); 62 | it('should only accept valid ordering directions (ASC, DESC, random)', () => { 63 | qb.reset_query(); 64 | expect(() => qb.order_by('planet_position')).to.not.throw(Error); 65 | expect(() => qb.order_by('planet_position','ASC')).to.not.throw(Error); 66 | expect(() => qb.order_by('planet_position','DESC')).to.not.throw(Error); 67 | expect(() => qb.order_by('planet_position','random')).to.not.throw(Error); 68 | expect(() => qb.order_by('planet_position',null)).to.not.throw(Error); 69 | expect(() => qb.order_by('planet_position',undefined)).to.not.throw(Error); 70 | expect(() => qb.order_by('planet_position',false)).to.not.throw(Error); 71 | expect(() => qb.order_by('planet_position',3)).to.not.throw(Error); 72 | expect(() => qb.order_by('planet_position',true)).to.not.throw(Error); 73 | expect(() => qb.order_by('planet_position',[])).to.not.throw(Error); 74 | expect(() => qb.order_by('planet_position',{})).to.not.throw(Error); 75 | expect(() => qb.order_by('planet_position','')).to.not.throw(Error); 76 | 77 | // Only an invalid string will throw an error 78 | expect(() => qb.order_by('planet_position','FAKE')).to.throw(Error); 79 | }); 80 | it('should accept a comma-separated list of fields to order by with a single direction at the end', () => { 81 | qb.reset_query(); 82 | qb.order_by('planet_position, planet_size asc'); 83 | qb.order_by_array.should.eql(['[planet_position] ASC', '[planet_size] ASC']); 84 | }); 85 | it('should accept a comma-separated list of field + direction pairs', () => { 86 | qb.reset_query(); 87 | qb.order_by('planet_position desc, planet_size asc'); 88 | qb.order_by_array.should.eql(['[planet_position] DESC', '[planet_size] ASC']); 89 | }); 90 | it('should accept a random direction in three forms: "random", "RAND", "RAND()" (case-insensitively) and remove field(s) from statement', () => { 91 | qb.reset_query(); 92 | qb.order_by('planet_position', 'random'); 93 | qb.order_by_array.should.eql(['NEWID()']); 94 | 95 | qb.reset_query(); 96 | qb.order_by('planet_size', 'RAND'); 97 | qb.order_by_array.should.eql(['NEWID()']); 98 | 99 | qb.reset_query(); 100 | qb.order_by('planet_position, planet_size', 'rand'); 101 | qb.order_by_array.should.eql(['NEWID()']); 102 | 103 | qb.reset_query(); 104 | qb.order_by(null, 'RAND()'); 105 | qb.order_by_array.should.eql(['NEWID()']); 106 | 107 | qb.reset_query(); 108 | qb.order_by('', 'rand'); 109 | qb.order_by_array.should.eql(['NEWID()']); 110 | }); 111 | it('should accept an array of field + direction pairs', () => { 112 | qb.reset_query(); 113 | qb.order_by(['planet_position DESC', 'planet_size ASC']); 114 | qb.order_by_array.should.eql(['[planet_position] DESC', '[planet_size] ASC']); 115 | }); 116 | it('should use direction parameter as default when an array of field + direction pairs is provided (when a pair does not contain a direction)', () => { 117 | qb.reset_query(); 118 | qb.order_by(['planet_position', 'planet_size'], 'desc'); 119 | qb.order_by_array.should.eql(['[planet_position] DESC', '[planet_size] DESC']); 120 | 121 | qb.reset_query(); 122 | qb.order_by(['planet_position DESC', 'planet_size'], 'desc'); 123 | qb.order_by_array.should.eql(['[planet_position] DESC', '[planet_size] DESC']); 124 | }); 125 | it('should accept a simple array of fields as first param and default to ASC for the direction if none is provided', () => { 126 | qb.reset_query(); 127 | qb.order_by(['planet_position', 'planet_size']); 128 | qb.order_by_array.should.eql(['[planet_position] ASC', '[planet_size] ASC']); 129 | 130 | }); 131 | }); 132 | -------------------------------------------------------------------------------- /test/mssql/01-tests-returning.js: -------------------------------------------------------------------------------- 1 | const should = require('chai').should(); 2 | const expect = require('chai').expect; 3 | const QueryBuilder = require('../../drivers/mssql/query_builder.js'); 4 | const qb = new QueryBuilder(); 5 | 6 | describe('MSSQL: returning()', () => { 7 | it('should exist', () => { 8 | should.exist(qb.returning); 9 | }); 10 | it('should be a function', () => { 11 | qb.returning.should.be.a('function'); 12 | }); 13 | it('should have an array to put ids into', () => { 14 | qb.should.have.property('returning_ids'); 15 | }); 16 | it('should have an empty array to put ids into at the beginning', () => { 17 | qb.returning_ids.should.be.empty; 18 | }); 19 | it('should not accept anything but a non-empty string or a non-empty array', () => { 20 | qb.reset_query(); 21 | expect(() => qb.returning(), 'nothing provided').to.throw(Error); 22 | expect(() => qb.returning(null), 'null provided').to.throw(Error); 23 | expect(() => qb.returning(false), 'false provided').to.throw(Error); 24 | expect(() => qb.returning(true), 'true provided').to.throw(Error); 25 | expect(() => qb.returning({}), 'empty object provided').to.throw(Error); 26 | expect(() => qb.returning(3), 'integer provided').to.throw(Error); 27 | expect(() => qb.returning(3.5), 'float provided').to.throw(Error); 28 | expect(() => qb.returning([]), 'empty array provided').to.throw(Error); 29 | expect(() => qb.returning([1,2]), 'array of numbers provided').to.throw(Error); 30 | expect(() => qb.returning(''), 'empty string provided').to.throw(Error); 31 | 32 | expect(() => qb.returning('id'), 'valid string provided').to.not.throw(Error); 33 | expect(() => qb.returning(['id', 'other_id']), 'valid array provided').to.not.throw(Error); 34 | 35 | }); 36 | it('should accept a column name in the form of a string as the first parameter', () => { 37 | qb.reset_query(); 38 | qb.returning('planet'); 39 | qb.returning_ids.should.eql(['INSERTED.[planet]']); 40 | }); 41 | it('should accept an array of column names in the form of strings', () => { 42 | qb.reset_query(); 43 | qb.returning(['planet', 'id']); 44 | qb.returning_ids.should.eql(['INSERTED.[planet]', 'INSERTED.[id]']); 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /test/mssql/01-tests-set.js: -------------------------------------------------------------------------------- 1 | const should = require('chai').should(); 2 | const expect = require('chai').expect; 3 | const QueryBuilder = require('../../drivers/mssql/query_builder.js'); 4 | const qb = new QueryBuilder(); 5 | 6 | describe('MSSQL: set()', () => { 7 | it('should exist', () => { 8 | should.exist(qb.set); 9 | }); 10 | it('should be a function', () => { 11 | qb.set.should.be.a('function'); 12 | }); 13 | it('should have an object to put fields into', () => { 14 | qb.should.have.property('set_array'); 15 | }); 16 | it('should have an empty array to put fields into at the beginning', () => { 17 | qb.set_array.should.be.empty; 18 | }); 19 | it('should not accept anything but a non-empty string or a non-empty object as first param', () => { 20 | qb.reset_query(); 21 | expect(() => qb.set(), 'nothing provided').to.throw(Error); 22 | expect(() => qb.set(null), 'null provided').to.throw(Error); 23 | expect(() => qb.set(false), 'false provided').to.throw(Error); 24 | expect(() => qb.set(true), 'true provided').to.throw(Error); 25 | expect(() => qb.set({}), 'empty object provided').to.throw(Error); 26 | expect(() => qb.set(3), 'integer provided').to.throw(Error); 27 | expect(() => qb.set(3.5), 'float provided').to.throw(Error); 28 | expect(() => qb.set(NaN), 'NaN provided').to.throw(Error); 29 | expect(() => qb.set(Infinity), 'Infinity provided').to.throw(Error); 30 | expect(() => qb.set([]), 'empty array provided').to.throw(Error); 31 | expect(() => qb.set([1,2]), 'array of numbers provided').to.throw(Error); 32 | expect(() => qb.set(''), 'empty string provided').to.throw(Error); 33 | expect(() => qb.set(' '), 'string full of spaces provided').to.throw(Error); 34 | expect(() => qb.set(/foobar/), 'regex provided').to.throw(Error); 35 | 36 | expect(() => qb.set('planet_position',3), 'valid string provided').to.not.throw(Error); 37 | expect(() => qb.set({planet_position: 3}), 'valid object provided').to.not.throw(Error); 38 | }); 39 | it('should not accept anything but a string, number, date, null, or boolean as second param if first param is a string.', () => { 40 | qb.reset_query(); 41 | expect(() => qb.set('planet_position'), 'nothing provided').to.throw(Error); 42 | expect(() => qb.set('planet_position',{}), 'empty object provided').to.throw(Error); 43 | expect(() => qb.set('planet_position',NaN), 'NaN provided').to.throw(Error); 44 | expect(() => qb.set('planet_position',Infinity), 'Infinity provided').to.throw(Error); 45 | expect(() => qb.set('planet_position',[]), 'empty array provided').to.throw(Error); 46 | expect(() => qb.set('planet_position',[1,2]), 'array of numbers provided').to.throw(Error); 47 | expect(() => qb.set('planet_position',/foobar/), 'regex provided').to.throw(Error); 48 | 49 | expect(() => qb.set('planet_position',new Date()), 'date provided').to.not.throw(Error); 50 | expect(() => qb.set('planet_position',null), 'null provided').to.not.throw(Error); 51 | expect(() => qb.set('planet_position',3), 'Integer provided').to.not.throw(Error); 52 | expect(() => qb.set('planet_position',3.5), 'float provided').to.not.throw(Error); 53 | expect(() => qb.set('planet_position',false), 'false provided').to.not.throw(Error); 54 | expect(() => qb.set('planet_position',true), 'true provided').to.not.throw(Error); 55 | expect(() => qb.set('planet_position',''), 'empty string provided').to.not.throw(Error); 56 | expect(() => qb.set('planet_position',' '), 'string full of spaces provided').to.not.throw(Error); 57 | expect(() => qb.set('planet_position','Three'), 'non-empty string provided').to.not.throw(Error); 58 | }); 59 | it('should add first param (key) and second param (value) to hash and escape them properly', () => { 60 | qb.reset_query(); 61 | qb.set('galaxy_name','Milky Way'); 62 | qb.set_array.should.eql([{"[galaxy_name]": "'Milky Way'"}]); 63 | }); 64 | it('should merge passed object into set_array and escape items properly', () => { 65 | qb.reset_query(); 66 | qb.set({galaxy_name: 'Milky Way'}); 67 | qb.set_array.should.eql([{"[galaxy_name]": "'Milky Way'"}]); 68 | 69 | qb.reset_query(); 70 | qb.set({galaxy_name: 'Milky Way', galaxy_class: 'C'}); 71 | qb.set_array.should.eql([{"[galaxy_name]": "'Milky Way'"}, {"[galaxy_class]": "'C'"}]); 72 | }); 73 | it('should not escape items if asked not to', () => { 74 | qb.reset_query(); 75 | qb.set({galaxy_name: 'Milky Way'}, null, false); 76 | qb.set_array.should.eql([{galaxy_name: 'Milky Way'}]); 77 | }); 78 | it('should append more items to set_array as set() is called', () => { 79 | qb.reset_query(); 80 | qb.set({galaxy_name: 'Milky Way'}, null, false); 81 | qb.set({galaxy_class: 'C'}, null, false); 82 | qb.set('galaxy_size','D'); 83 | qb.set_array.should.eql([{galaxy_name: 'Milky Way'}, {galaxy_class: 'C'}, {"[galaxy_size]": "'D'"}]); 84 | }); 85 | it('should be chainable', () => { 86 | qb.reset_query(); 87 | qb.set({galaxy_name: 'Milky Way', galaxy_class: 'C'}, null, false).set('galaxy_size','D'); 88 | qb.set_array.should.eql([{galaxy_name: 'Milky Way'}, {galaxy_class: 'C'}, {"[galaxy_size]": "'D'"}]); 89 | }); 90 | it('should overwrite values of keys that have been set already', () => { 91 | qb.reset_query(); 92 | qb.set({galaxy_name: 'Milky Way'}, null, false); 93 | qb.set({galaxy_class: 'C'}); 94 | qb.set('galaxy_class','D'); 95 | qb.set_array.should.eql([{galaxy_name: 'Milky Way'}, {"[galaxy_class]": "'D'"}]); 96 | }); 97 | it('should NOT overwrite values of keys that are the same but have different escape flags', () => { 98 | qb.reset_query(); 99 | qb.set({galaxy_name: 'Milky Way'}, null, false); 100 | qb.set({galaxy_class: 'C'}); 101 | qb.set('galaxy_class','D', false); 102 | qb.set_array.should.eql([{galaxy_name: 'Milky Way'}, {"[galaxy_class]": "'C'"}, {galaxy_class: 'D'}]); 103 | }); 104 | }); 105 | -------------------------------------------------------------------------------- /test/mssql/02-tests-compilation_methods.js: -------------------------------------------------------------------------------- 1 | const should = require('chai').should(); 2 | const expect = require('chai').expect; 3 | const QueryBuilder = require('../../drivers/mssql/query_builder.js'); 4 | const qb = new QueryBuilder(); 5 | 6 | describe('MSSQL: get_compiled_select()', () => { 7 | it('should exist', () => { 8 | should.exist(qb.get_compiled_select); 9 | }); 10 | it('should be a function', () => { 11 | qb.get_compiled_select.should.be.a('function'); 12 | }); 13 | it('should add a table to from_array when a table is supplied', () => { 14 | qb.reset_query(); 15 | qb.get_compiled_select('galaxies'); 16 | qb.from_array.should.eql(['[galaxies]']); 17 | }); 18 | it('should add a set of tables to from_array when an array of tables is supplied', () => { 19 | qb.reset_query(); 20 | qb.get_compiled_select(['galaxies','star_systems','planets']); 21 | qb.from_array.should.eql(['[galaxies]','[star_systems]','[planets]']); 22 | }); 23 | it('should return a SQL string', () => { 24 | qb.reset_query(); 25 | const sql = qb.get_compiled_select('galaxies'); 26 | sql.should.eql('SELECT * FROM [galaxies]'); 27 | }); 28 | }); 29 | 30 | describe('MSSQL: get_compiled_insert()', () => { 31 | it('should exist', () => { 32 | should.exist(qb.get_compiled_insert); 33 | }); 34 | it('should be a function', () => { 35 | qb.get_compiled_insert.should.be.a('function'); 36 | }); 37 | it('should return a SQL string', () => { 38 | qb.reset_query(); 39 | const sql = qb.set({foo:'bar'}).get_compiled_insert('galaxies'); 40 | sql.should.eql("INSERT INTO [galaxies] ([foo]) VALUES ('bar')"); 41 | }); 42 | }); 43 | 44 | describe('MSSQL: get_compiled_update()', () => { 45 | it('should exist', () => { 46 | should.exist(qb.get_compiled_update); 47 | }); 48 | it('should be a function', () => { 49 | qb.get_compiled_update.should.be.a('function'); 50 | }); 51 | it('should return a SQL string', () => { 52 | qb.reset_query(); 53 | const sql = qb.set({foo:'bar'}).where('id',45).get_compiled_update('galaxies'); 54 | sql.should.eql("UPDATE [galaxies] SET [foo] = 'bar' WHERE [id] = 45"); 55 | }); 56 | }); 57 | 58 | describe('MSSQL: get_compiled_delete()', () => { 59 | it('should exist', () => { 60 | should.exist(qb.get_compiled_delete); 61 | }); 62 | it('should be a function', () => { 63 | qb.get_compiled_delete.should.be.a('function'); 64 | }); 65 | it('should return a SQL string', () => { 66 | qb.reset_query(); 67 | const sql = qb.where('id',45).get_compiled_delete('galaxies'); 68 | sql.should.eql("DELETE FROM [galaxies] WHERE [id] = 45"); 69 | }); 70 | }); 71 | -------------------------------------------------------------------------------- /test/mssql/03-tests-count.js: -------------------------------------------------------------------------------- 1 | const should = require('chai').should(); 2 | const expect = require('chai').expect; 3 | const QueryBuilder = require('../../drivers/mssql/query_builder.js'); 4 | const qb = new QueryBuilder(); 5 | 6 | describe('MSSQL: count()', () => { 7 | it('should exist', () => { 8 | should.exist(qb.count); 9 | }); 10 | it('should be a function', () => { 11 | qb.count.should.be.a('function'); 12 | }); 13 | it('should require that an item already exists in the from_array if one is not provided as the first parameter', () => { 14 | qb.reset_query(); 15 | expect(() => qb.count(), 'no tables supplied in chain').to.throw(Error); 16 | expect(() => qb.from('galaxies').count(), 'table supplied by from()').to.not.throw(Error); 17 | expect(() => qb.count('galaxies'), 'table supplied as first parameter').to.not.throw(Error); 18 | }); 19 | it('should add a table to from_array when a table is supplied', () => { 20 | qb.reset_query(); 21 | const sql = qb.count('galaxies'); 22 | qb.from_array.should.eql(['[galaxies]']); 23 | }); 24 | it('should return a string', () => { 25 | qb.reset_query(); 26 | const sql = qb.count('galaxies'); 27 | expect(sql).to.be.a('string'); 28 | expect(sql).to.exist; 29 | expect(sql).to.not.eql(''); 30 | }); 31 | it('should create a properly-escaped SELECT query', () => { 32 | qb.reset_query(); 33 | const sql = qb.count('galaxies'); 34 | sql.should.eql("SELECT COUNT(*) AS [numrows] FROM [galaxies]"); 35 | }); 36 | it('should include WHERE statements', () => { 37 | qb.reset_query(); 38 | const sql = qb.where({type:'spiral'}).count('galaxies'); 39 | sql.should.eql("SELECT COUNT(*) AS [numrows] FROM [galaxies] WHERE [type] = 'spiral'"); 40 | }); 41 | it('should work when table/view/procedure is provided earlier in chain but not in count() method', () => { 42 | qb.reset_query(); 43 | const sql = qb.from('galaxies').count(); 44 | sql.should.eql("SELECT COUNT(*) AS [numrows] FROM [galaxies]"); 45 | }); 46 | it('should work with multiple tables/views/stored procedures', () => { 47 | qb.reset_query(); 48 | const sql = qb.from(['planets','galaxies']).count(); 49 | sql.should.eql("SELECT COUNT(*) AS [numrows] FROM [planets], [galaxies]"); 50 | }); 51 | it('should include any joins that were added in the chain', () => { 52 | qb.reset_query(); 53 | const sql = qb.join('galaxies g','g.id=s.galaxy_id','left').count('star_systems s'); 54 | sql.should.eql("SELECT COUNT(*) AS [numrows] FROM [star_systems] [s] LEFT JOIN [galaxies] [g] ON [g].[id] = [s].[galaxy_id]"); 55 | }); 56 | }); 57 | -------------------------------------------------------------------------------- /test/mssql/03-tests-delete.js: -------------------------------------------------------------------------------- 1 | const should = require('chai').should(); 2 | const expect = require('chai').expect; 3 | const QueryBuilder = require('../../drivers/mssql/query_builder.js'); 4 | const qb = new QueryBuilder(); 5 | 6 | describe('MSSQL: delete()', () => { 7 | it('should exist', () => { 8 | should.exist(qb.delete); 9 | }); 10 | it('should be a function', () => { 11 | qb.delete.should.be.a('function'); 12 | }); 13 | it('should add a table to from_array when a table is supplied', () => { 14 | qb.reset_query(); 15 | qb.delete('galaxies'); 16 | qb.from_array.should.eql(['[galaxies]']); 17 | }); 18 | it('should only accept nothing or a non-empty-string for the table (first) parameter', () => { 19 | qb.reset_query(); 20 | 21 | expect(() => qb.delete([]), 'empty array provided').to.throw(Error); 22 | expect(() => qb.delete({}), 'empty object provided').to.throw(Error); 23 | expect(() => qb.delete(3), 'integer provided').to.throw(Error); 24 | expect(() => qb.delete(3.5), 'float provided').to.throw(Error); 25 | expect(() => qb.delete(true), 'true provided').to.throw(Error); 26 | expect(() => qb.delete(Infinity), 'Infinity provided').to.throw(Error); 27 | expect(() => qb.delete([1,2]), 'array of numbers provided').to.throw(Error); 28 | expect(() => qb.delete(/foobar/), 'regex provided').to.throw(Error); 29 | expect(() => qb.delete(NaN), 'NaN provided').to.throw(Error); 30 | expect(() => qb.delete(false), 'false provided').to.throw(Error); 31 | expect(() => qb.delete(''), 'empty string provided').to.throw(Error); 32 | expect(() => qb.delete(' '), 'string full of spaces provided').to.throw(Error); 33 | expect(() => qb.delete(null), 'null provided').to.throw(Error); 34 | 35 | // An undefined/nothing option will only work if a table has already been provided 36 | qb.from('galaxies'); expect(() => qb.delete(undefined),'undefined provided').to.not.throw(Error); 37 | qb.from('galaxies'); expect(() => qb.delete(),'nothing provided').to.not.throw(Error); 38 | }); 39 | it('should only use the first table supplied in a list if an array of table is supplied with the from() method.', () => { 40 | qb.reset_query(); 41 | const sql = qb.from(['galaxies','star_systems','planets']).delete(); 42 | sql.should.eql("DELETE FROM [galaxies]"); 43 | }); 44 | it('should add where conditions to where_array when conditions are supplied', () => { 45 | qb.reset_query(); 46 | qb.delete('planets', {continents: 7, star_system: 'Solar'}); 47 | qb.where_array.should.eql(["[continents] = 7", "AND [star_system] = 'Solar'"]); 48 | }); 49 | it('should return a string', () => { 50 | qb.reset_query(); 51 | const sql = qb.delete('galaxies', {continents: 7, star_system: 'Solar'}); 52 | expect(sql).to.be.a('string'); 53 | expect(sql).to.exist; 54 | expect(sql).to.not.eql(''); 55 | }); 56 | it('should build a properly-escaped delete statement that deletes all records in a table if only a table is given', () => { 57 | qb.reset_query(); 58 | const sql = qb.delete('galaxies'); 59 | sql.should.eql('DELETE FROM [galaxies]'); 60 | }); 61 | it('should build a properly-escaped delete statement that deletes all records in a table that matched passed conditions', () => { 62 | qb.reset_query(); 63 | const sql = qb.delete('galaxies', {class: 'M'}); 64 | sql.should.eql("DELETE FROM [galaxies] WHERE [class] = 'M'"); 65 | }); 66 | it('should use ONLY the FIRST table added previously via the from() method', () => { 67 | qb.reset_query(); 68 | qb.from('galaxies'); 69 | let sql = qb.delete(); 70 | sql.should.eql('DELETE FROM [galaxies]'); 71 | 72 | qb.reset_query(); 73 | sql = qb.from(['galaxies','star_systems','planets']).delete(); 74 | sql.should.eql('DELETE FROM [galaxies]'); 75 | }); 76 | it('should accept where conditions added previously via the where() method', () => { 77 | qb.reset_query(); 78 | const sql = qb.where('created >=',4.6E9).where({class: 'M'}).delete('galaxies'); 79 | sql.should.eql("DELETE FROM [galaxies] WHERE [created] >= 4600000000 AND [class] = 'M'"); 80 | }); 81 | it('should accept a limit on the number of rows deleted', () => { 82 | qb.reset_query(); 83 | const sql = qb.limit(20).delete('galaxies'); 84 | sql.should.eql("DELETE TOP (20) FROM [galaxies]"); 85 | }); 86 | it('should not a allow an offset delete', () => { 87 | qb.reset_query(); 88 | expect(() => qb.limit(20, 10).delete('galaxies'), 'offset provided').to.throw(Error); 89 | }); 90 | }); 91 | -------------------------------------------------------------------------------- /test/mssql/03-tests-empty_table.js: -------------------------------------------------------------------------------- 1 | const should = require('chai').should(); 2 | const expect = require('chai').expect; 3 | const QueryBuilder = require('../../drivers/mssql/query_builder.js'); 4 | const qb = new QueryBuilder(); 5 | 6 | describe('MSSQL: emtpy_table()', () => { 7 | it('should exist', () => { 8 | should.exist(qb.empty_table); 9 | }); 10 | it('should be a function', () => { 11 | qb.empty_table.should.be.a('function'); 12 | }); 13 | it('should return a string', () => { 14 | qb.reset_query(); 15 | const sql = qb.empty_table('galaxies'); 16 | expect(sql).to.be.a('string'); 17 | expect(sql).to.exist; 18 | expect(sql).to.not.eql(''); 19 | }); 20 | it('should build a proper DELETE statement', () => { 21 | qb.reset_query(); 22 | const sql = qb.empty_table('galaxies'); 23 | sql.should.eql('DELETE FROM [galaxies]'); 24 | }); 25 | it('should only accept nothing or a non-empty-string for the table (first) parameter', () => { 26 | qb.reset_query(); 27 | 28 | expect(() => qb.empty_table([]), 'empty array provided').to.throw(Error); 29 | expect(() => qb.empty_table({}), 'empty object provided').to.throw(Error); 30 | expect(() => qb.empty_table(3), 'integer provided').to.throw(Error); 31 | expect(() => qb.empty_table(3.5), 'float provided').to.throw(Error); 32 | expect(() => qb.empty_table(true), 'true provided').to.throw(Error); 33 | expect(() => qb.empty_table(Infinity), 'Infinity provided').to.throw(Error); 34 | expect(() => qb.empty_table([1,2]), 'array of numbers provided').to.throw(Error); 35 | expect(() => qb.empty_table(/foobar/), 'regex provided').to.throw(Error); 36 | expect(() => qb.empty_table(NaN), 'NaN provided').to.throw(Error); 37 | expect(() => qb.empty_table(false), 'false provided').to.throw(Error); 38 | expect(() => qb.empty_table(''), 'empty string provided').to.throw(Error); 39 | expect(() => qb.empty_table(' '), 'string full of spaces provided').to.throw(Error); 40 | expect(() => qb.empty_table(null), 'null provided').to.throw(Error); 41 | 42 | // An undefined/nothing option will only work if a table has already been provided 43 | qb.from('galaxies'); expect(() => qb.empty_table(undefined),'undefined provided').to.not.throw(Error); 44 | qb.from('galaxies'); expect(() => qb.empty_table(),'nothing provided').to.not.throw(Error); 45 | }); 46 | it('should only use the first table supplied in a list if an array of table is supplied with the from() method.', () => { 47 | qb.reset_query(); 48 | const sql = qb.from(['galaxies','star_systems','planets']).empty_table(); 49 | sql.should.eql("DELETE FROM [galaxies]"); 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /test/mssql/03-tests-insert.js: -------------------------------------------------------------------------------- 1 | const should = require('chai').should(); 2 | const expect = require('chai').expect; 3 | const QueryBuilder = require('../../drivers/mssql/query_builder.js'); 4 | const qb = new QueryBuilder(); 5 | 6 | const test_data = {id:3, name:'Milky Way', type: 'spiral'}; 7 | const test_data_set = [{id:3, name:'Milky Way', type: 'spiral'}, {id:4, name: 'Andromeda', type: 'spiral'}]; 8 | 9 | // table, data, callback, ignore, suffix 10 | 11 | describe('MSSQL: insert()', () => { 12 | it('should exist', () => { 13 | should.exist(qb.insert); 14 | }); 15 | it('should be a function', () => { 16 | qb.insert.should.be.a('function'); 17 | }); 18 | it('should add a table to from_array when a table is supplied', () => { 19 | qb.reset_query(); 20 | qb.insert('galaxies', test_data); 21 | qb.from_array.should.eql(['[galaxies]']); 22 | }); 23 | it('should only accept nothing or a string for the table (first) parameter', () => { 24 | qb.reset_query(); 25 | 26 | // Doing these to prevent other errors 27 | qb.from('galaxies'); 28 | 29 | expect(() => qb.insert([], test_data), 'empty array provided').to.throw(Error); 30 | expect(() => qb.insert({}, test_data), 'empty object provided').to.throw(Error); 31 | expect(() => qb.insert(3, test_data), 'integer provided').to.throw(Error); 32 | expect(() => qb.insert(3.5, test_data), 'float provided').to.throw(Error); 33 | expect(() => qb.insert(true, test_data), 'true provided').to.throw(Error); 34 | expect(() => qb.insert(Infinity, test_data), 'Infinity provided').to.throw(Error); 35 | expect(() => qb.insert([1,2], test_data), 'array of numbers provided').to.throw(Error); 36 | expect(() => qb.insert(/foobar/, test_data), 'regex provided').to.throw(Error); 37 | 38 | expect(() => qb.insert(NaN, test_data), 'NaN provided').to.not.throw(Error); 39 | expect(() => qb.insert(false, test_data), 'false provided').to.not.throw(Error); 40 | expect(() => qb.insert('', test_data), 'empty string provided').to.not.throw(Error); 41 | expect(() => qb.insert(' ', test_data), 'string full of spaces provided').to.not.throw(Error); 42 | expect(() => qb.insert(null, test_data), 'null provided').to.not.throw(Error); 43 | expect(() => qb.insert(undefined, test_data),'undefined provided').to.not.throw(Error); 44 | }); 45 | it('should fail if a number, non-standard object, regex, boolean, array of non-objects, or non-empty string is provided in data parameter', () => { 46 | qb.reset_query(); 47 | 48 | expect(() => qb.insert('galaxies',test_data), 'non-empty array provided').to.not.throw(Error); 49 | expect(() => qb.insert('galaxies',[]), 'empty array provided').to.not.throw(Error); 50 | expect(() => qb.insert('galaxies',[test_data,test_data]), 'array of non-empty standard objects provided').to.not.throw(Error); 51 | expect(() => qb.insert('galaxies',{}), 'empty object provided').to.not.throw(Error); 52 | expect(() => qb.insert('galaxies',''), 'empty string provided').to.not.throw(Error); 53 | expect(() => qb.insert('galaxies',null), 'null provided').to.not.throw(Error); 54 | expect(() => qb.insert('galaxies',undefined), 'undefined provided').to.not.throw(Error); 55 | expect(() => qb.insert('galaxies'), 'nothing provided').to.not.throw(Error); 56 | 57 | expect(() => qb.insert('galaxies',3), 'integer provided').to.throw(Error); 58 | expect(() => qb.insert('galaxies',3.5), 'float provided').to.throw(Error); 59 | expect(() => qb.insert('galaxies',true), 'true provided').to.throw(Error); 60 | expect(() => qb.insert('galaxies',Infinity), 'Infinity provided').to.throw(Error); 61 | expect(() => qb.insert('galaxies',[{},{}]), 'array of empty objects provided').to.throw(Error); 62 | expect(() => qb.insert('galaxies',[1,2]), 'array of numbers provided').to.throw(Error); 63 | expect(() => qb.insert('galaxies',['abc',2,{foo:'bar'}]), 'array of mixed values provided').to.throw(Error); 64 | expect(() => qb.insert('galaxies',/foobar/), 'regex provided').to.throw(Error); 65 | expect(() => qb.insert('galaxies',NaN), 'NaN provided').to.throw(Error); 66 | expect(() => qb.insert('galaxies',false), 'false provided').to.throw(Error); 67 | expect(() => qb.insert('galaxies',' '), 'string full of spaces provided').to.throw(Error); 68 | }); 69 | it('should allow for an empty data parameter', () => { 70 | qb.reset_query(); 71 | const sql = qb.insert('galaxies'); 72 | sql.should.eql("INSERT INTO [galaxies] () VALUES ()"); 73 | }); 74 | it('should utilize pre-existing tables set in from_array', () => { 75 | qb.reset_query(); 76 | qb.from('galaxies'); 77 | const sql = qb.insert(); 78 | sql.should.eql("INSERT INTO [galaxies] () VALUES ()"); 79 | }); 80 | it('should utilize pre-existing values set in in set_array', () => { 81 | qb.reset_query(); 82 | qb.set(test_data); 83 | const sql = qb.insert('galaxies'); 84 | sql.should.eql("INSERT INTO [galaxies] ([id], [name], [type]) VALUES (3, 'Milky Way', 'spiral')"); 85 | }); 86 | it('should utilize pre-existing tables and values from from_aray and set_array, respectively', () => { 87 | qb.reset_query(); 88 | qb.from('galaxies').set(test_data); 89 | const sql = qb.insert(); 90 | sql.should.eql("INSERT INTO [galaxies] ([id], [name], [type]) VALUES (3, 'Milky Way', 'spiral')"); 91 | }); 92 | it('should accept a non-empty object for the data parameter', () => { 93 | qb.reset_query(); 94 | const sql = qb.insert('galaxies', test_data); 95 | sql.should.eql("INSERT INTO [galaxies] ([id], [name], [type]) VALUES (3, 'Milky Way', 'spiral')"); 96 | }); 97 | it('should convert call to insert_batch() if an array of non-emtpy objects is passed in the data parameter', () => { 98 | qb.reset_query(); 99 | const sql = qb.insert('galaxies', test_data_set); 100 | const sql_b = qb.insert_batch('galaxies', test_data_set); 101 | sql.should.eql(sql_b); 102 | }); 103 | it('should fail if any invalid values are passed in the data object.', () => { 104 | qb.reset_query(); 105 | const func = () => console.log("foo"); 106 | const regex = /foobar/; 107 | const arr = [1,2,3]; 108 | const obj = {foo: 'bar'}; 109 | 110 | expect(() => qb.insert('galaxies',{id: 1}), 'number in data').to.not.throw(Error); 111 | expect(() => qb.insert('galaxies',{id: 'foo'}), 'string in data').to.not.throw(Error); 112 | expect(() => qb.insert('galaxies',{id: false}), 'boolean in data').to.not.throw(Error); 113 | expect(() => qb.insert('galaxies',{id: null}), 'null in data').to.not.throw(Error); 114 | expect(() => qb.insert('galaxies',{id: undefined}), 'undefined in data').to.not.throw(Error); 115 | expect(() => qb.insert('galaxies',{id: func}), 'function in data').to.throw(Error); 116 | expect(() => qb.insert('galaxies',{id: regex}), 'regex in data').to.throw(Error); 117 | expect(() => qb.insert('galaxies',{id: Infinity}), 'Infinity in data').to.throw(Error); 118 | expect(() => qb.insert('galaxies',{id: NaN}), 'NaN in data').to.throw(Error); 119 | expect(() => qb.insert('galaxies',{id: arr}), 'array in data').to.throw(Error); 120 | expect(() => qb.insert('galaxies',{id: obj}), 'object in data').to.throw(Error); 121 | }); 122 | it('should include the OUTPUT directive when the return() method is called in the chain', () => { 123 | qb.reset_query(); 124 | const sql = qb.returning('id').insert('galaxies', test_data); 125 | sql.should.eql("INSERT INTO [galaxies] ([id], [name], [type]) OUTPUT INSERTED.[id] VALUES (3, 'Milky Way', 'spiral')"); 126 | }); 127 | it('should include the OUTPUT directive with multiple outputs when the return() method is called in the chain with an array', () => { 128 | qb.reset_query(); 129 | const sql = qb.returning(['id', 'name']).insert('galaxies', {name:'Milky Way', type: 'spiral'}); 130 | sql.should.eql("INSERT INTO [galaxies] ([name], [type]) OUTPUT INSERTED.[id], INSERTED.[name] VALUES ('Milky Way', 'spiral')"); 131 | }); 132 | }); 133 | 134 | describe('MSSQL: insert_ignore()', () => { 135 | it('should exist', () => { 136 | should.exist(qb.insert_ignore); 137 | }); 138 | it('should be a function', () => { 139 | qb.insert_ignore.should.be.a('function'); 140 | }); 141 | it('should not allow for insert ignore statements', () => { 142 | qb.reset_query(); 143 | expect(() => qb.insert_ignore('galaxies', test_data)).to.throw(Error); 144 | }); 145 | }); 146 | -------------------------------------------------------------------------------- /test/mssql/03-tests-insert_batch.js: -------------------------------------------------------------------------------- 1 | const should = require('chai').should(); 2 | const expect = require('chai').expect; 3 | const QueryBuilder = require('../../drivers/mssql/query_builder.js'); 4 | const qb = new QueryBuilder(); 5 | 6 | const test_where = {id:3}; 7 | const test_data = [{id:3, name:'Milky Way', type: 'spiral'}, {id:4, name: 'Andromeda', type: 'spiral'}]; 8 | 9 | describe('MSSQL: insert_batch()', () => { 10 | it('should exist', () => { 11 | should.exist(qb.insert_batch); 12 | }); 13 | it('should be a function', () => { 14 | qb.insert_batch.should.be.a('function'); 15 | }); 16 | it('should add a table to from_array when a table is supplied', () => { 17 | qb.reset_query(); 18 | qb.insert_batch('galaxies', test_data); 19 | qb.from_array.should.eql(['[galaxies]']); 20 | }); 21 | it('should only accept nothing or a string for the table (first) parameter', () => { 22 | qb.reset_query(); 23 | 24 | // Doing these to prevent other errors 25 | qb.from('galaxies'); 26 | 27 | expect(() => qb.insert_batch([], test_data), 'empty array provided').to.throw(Error); 28 | expect(() => qb.insert_batch({}, test_data), 'empty object provided').to.throw(Error); 29 | expect(() => qb.insert_batch(3, test_data), 'integer provided').to.throw(Error); 30 | expect(() => qb.insert_batch(3.5, test_data), 'float provided').to.throw(Error); 31 | expect(() => qb.insert_batch(true, test_data), 'true provided').to.throw(Error); 32 | expect(() => qb.insert_batch(Infinity, test_data), 'Infinity provided').to.throw(Error); 33 | expect(() => qb.insert_batch([1,2], test_data), 'array of numbers provided').to.throw(Error); 34 | expect(() => qb.insert_batch(/foobar/, test_data), 'regex provided').to.throw(Error); 35 | 36 | expect(() => qb.insert_batch(NaN, test_data), 'NaN provided').to.not.throw(Error); 37 | expect(() => qb.insert_batch(false, test_data), 'false provided').to.not.throw(Error); 38 | expect(() => qb.insert_batch('', test_data), 'empty string provided').to.not.throw(Error); 39 | expect(() => qb.insert_batch(' ', test_data), 'string full of spaces provided').to.not.throw(Error); 40 | expect(() => qb.insert_batch(null, test_data), 'null provided').to.not.throw(Error); 41 | expect(() => qb.insert_batch(undefined, test_data), 'undefined provided').to.not.throw(Error); 42 | }); 43 | it('should build a proper batch INSERT string', () => { 44 | qb.reset_query(); 45 | const sql = qb.insert_batch('galaxies', test_data); 46 | sql.should.eql("INSERT INTO [galaxies] ([id], [name], [type]) VALUES (3, 'Milky Way', 'spiral'), (4, 'Andromeda', 'spiral')"); 47 | }); 48 | it('should only accept an array as the second parameter', () => { 49 | qb.reset_query(); 50 | 51 | expect(() => qb.insert_batch('galaxies', test_data), 'array of objects provided').to.not.throw(Error); 52 | expect(() => qb.insert_batch('galaxies', []), 'empty array provided').to.not.throw(Error); 53 | 54 | expect(() => qb.insert_batch('galaxies'), 'nothing provided').to.throw(Error); 55 | expect(() => qb.insert_batch('galaxies', [{},{}]), 'array of empty objects provided').to.throw(Error); 56 | expect(() => qb.insert_batch('galaxies', [test_data,test_data]), 'array of arrays provided').to.throw(Error); 57 | expect(() => qb.insert_batch('galaxies', {}), 'empty object provided').to.throw(Error); 58 | expect(() => qb.insert_batch('galaxies', ''), 'empty string provided').to.throw(Error); 59 | expect(() => qb.insert_batch('galaxies', null), 'null provided').to.throw(Error); 60 | expect(() => qb.insert_batch('galaxies', undefined), 'undefined provided').to.throw(Error); 61 | expect(() => qb.insert_batch('galaxies', 3), 'integer provided').to.throw(Error); 62 | expect(() => qb.insert_batch('galaxies', 3.5), 'float provided').to.throw(Error); 63 | expect(() => qb.insert_batch('galaxies', true), 'true provided').to.throw(Error); 64 | expect(() => qb.insert_batch('galaxies', Infinity), 'Infinity provided').to.throw(Error); 65 | expect(() => qb.insert_batch('galaxies', [1,2]), 'array of numbers provided').to.throw(Error); 66 | expect(() => qb.insert_batch('galaxies', [Date, /foobar/, null]), 'array of non-standard objects provided').to.throw(Error); 67 | expect(() => qb.insert_batch('galaxies', ['abc',2,{foo:'bar'}]), 'array of mixed values provided').to.throw(Error); 68 | expect(() => qb.insert_batch('galaxies', /foobar/), 'regex provided').to.throw(Error); 69 | expect(() => qb.insert_batch('galaxies', NaN), 'NaN provided').to.throw(Error); 70 | expect(() => qb.insert_batch('galaxies', false), 'false provided').to.throw(Error); 71 | expect(() => qb.insert_batch('galaxies', ' '), 'string full of spaces provided').to.throw(Error); 72 | }); 73 | it('should allow for an empty data parameter', () => { 74 | qb.reset_query(); 75 | const sql = qb.insert_batch('galaxies', []); 76 | sql.should.eql("INSERT INTO [galaxies] () VALUES ()"); 77 | }); 78 | it('should utilize pre-existing tables set in from_array', () => { 79 | qb.reset_query(); 80 | qb.from('galaxies'); 81 | const sql = qb.insert_batch(null, []); 82 | sql.should.eql("INSERT INTO [galaxies] () VALUES ()"); 83 | }); 84 | it('should fail if any invalid values are passed into one of the data objects in the dataset', () => { 85 | qb.reset_query(); 86 | const func = () => console.log("foo"); 87 | const regex = /foobar/; 88 | const arr = [1,2,3]; 89 | const obj = {foo: 'bar'}; 90 | 91 | expect(() => qb.insert_batch('galaxies', [{id: func}]), 'function in data').to.throw(Error); 92 | expect(() => qb.insert_batch('galaxies', [{id: regex}]), 'regex in data').to.throw(Error); 93 | expect(() => qb.insert_batch('galaxies', [{id: Infinity}]), 'Infinity in data').to.throw(Error); 94 | expect(() => qb.insert_batch('galaxies', [{id: undefined}]), 'undefined in data').to.throw(Error); 95 | expect(() => qb.insert_batch('galaxies', [{id: NaN}]), 'NaN in data').to.throw(Error); 96 | expect(() => qb.insert_batch('galaxies', [{id: arr}]), 'array in data').to.throw(Error); 97 | expect(() => qb.insert_batch('galaxies', [{id: obj}]), 'object in data').to.throw(Error); 98 | }); 99 | it('should not support insert ignore statements', () => { 100 | qb.reset_query(); 101 | expect(() => qb.insert_batch('galaxies', test_data, true)).to.throw(Error); 102 | }); 103 | it('should not support insert statement suffixes', () => { 104 | qb.reset_query(); 105 | expect(() => qb.insert_batch('galaxies', test_data, false, 'ON DUPLICATE KEY UPDATE')).to.throw(Error); 106 | }); 107 | // it('should include the OUTPUT directive when the return() method is called in the chain', () => { 108 | // qb.reset_query(); 109 | // const sql = qb.returning('id').insert_batch('galaxies', test_data); 110 | // sql.should.eql("INSERT INTO [galaxies] ([id], [name], [type]) OUTPUT INSERTED.[id] VALUES (3, 'Milky Way', 'spiral')"); 111 | // }); 112 | // it('should include the OUTPUT directive with multiple outputs when the return() method is called in the chain with an array', () => { 113 | // qb.reset_query(); 114 | // const sql = qb.returning(['id', 'name']).insert_batch('galaxies', test_data); 115 | // sql.should.eql("INSERT INTO [galaxies] ([name], [type]) OUTPUT INSERTED.[id], INSERTED.[name] VALUES ('Milky Way', 'spiral')"); 116 | // }); 117 | }); 118 | -------------------------------------------------------------------------------- /test/mssql/03-tests-truncate.js: -------------------------------------------------------------------------------- 1 | const should = require('chai').should(); 2 | const expect = require('chai').expect; 3 | const QueryBuilder = require('../../drivers/mssql/query_builder.js'); 4 | const qb = new QueryBuilder(); 5 | 6 | describe('MSSQL: truncate()', () => { 7 | it('should exist', () => { 8 | should.exist(qb.truncate); 9 | }); 10 | it('should be a function', () => { 11 | qb.truncate.should.be.a('function'); 12 | }); 13 | it('should return a string', () => { 14 | qb.reset_query(); 15 | const sql = qb.truncate('galaxies'); 16 | expect(sql).to.be.a('string'); 17 | expect(sql).to.exist; 18 | expect(sql).to.not.eql(''); 19 | }); 20 | it('should build a proper truncate statement', () => { 21 | qb.reset_query(); 22 | const sql = qb.truncate('galaxies'); 23 | sql.should.eql('TRUNCATE [galaxies]'); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /test/mssql/03-tests-update.js: -------------------------------------------------------------------------------- 1 | const should = require('chai').should(); 2 | const expect = require('chai').expect; 3 | const QueryBuilder = require('../../drivers/mssql/query_builder.js'); 4 | const qb = new QueryBuilder(); 5 | 6 | const test_where = {id:3}; 7 | const test_data = {name:'Milky Way', type: 'spiral'}; 8 | const test_data_set = [{id:3, name:'Milky Way', type: 'spiral'}, {id:4, name: 'Andromeda', type: 'spiral'}]; 9 | 10 | // table, data, callback, ignore, suffix 11 | 12 | describe('MSSQL: update()', () => { 13 | it('should exist', () => { 14 | should.exist(qb.update); 15 | }); 16 | it('should be a function', () => { 17 | qb.update.should.be.a('function'); 18 | }); 19 | it('should add a table to from_array when a table is supplied', () => { 20 | qb.reset_query(); 21 | qb.update('galaxies', test_data, test_where); 22 | qb.from_array.should.eql(['[galaxies]']); 23 | }); 24 | it('should accept a string or falsy value for the table (first) parameter', () => { 25 | qb.reset_query(); 26 | 27 | // One could choose to pass a falsy value to the first param because they have or will 28 | // supply it with the from() method instead. 29 | 30 | qb.reset_query(); expect(() => qb.from('galaxies').update([], test_data), 'empty array provided').to.throw(Error); 31 | qb.reset_query(); expect(() => qb.from('galaxies').update({}, test_data), 'empty object provided').to.throw(Error); 32 | qb.reset_query(); expect(() => qb.from('galaxies').update(3, test_data), 'integer provided').to.throw(Error); 33 | qb.reset_query(); expect(() => qb.from('galaxies').update(3.5, test_data), 'float provided').to.throw(Error); 34 | qb.reset_query(); expect(() => qb.from('galaxies').update(true, test_data), 'true provided').to.throw(Error); 35 | qb.reset_query(); expect(() => qb.from('galaxies').update(Infinity, test_data), 'Infinity provided').to.throw(Error); 36 | qb.reset_query(); expect(() => qb.from('galaxies').update([1,2], test_data), 'array of numbers provided').to.throw(Error); 37 | qb.reset_query(); expect(() => qb.from('galaxies').update(/foobar/, test_data), 'regex provided').to.throw(Error); 38 | 39 | qb.reset_query(); expect(() => qb.from('galaxies').update(NaN, test_data), 'NaN provided').to.not.throw(Error); 40 | qb.reset_query(); expect(() => qb.from('galaxies').update(false, test_data), 'false provided').to.not.throw(Error); 41 | qb.reset_query(); expect(() => qb.from('galaxies').update('', test_data), 'empty string provided').to.not.throw(Error); 42 | qb.reset_query(); expect(() => qb.from('galaxies').update(' ', test_data), 'string full of spaces provided').to.not.throw(Error); 43 | qb.reset_query(); expect(() => qb.from('galaxies').update(null, test_data), 'null provided').to.not.throw(Error); 44 | qb.reset_query(); expect(() => qb.from('galaxies').update(undefined, test_data),'undefined provided').to.not.throw(Error); 45 | }); 46 | it('should fail if a number, non-standard object, regex, boolean, array of non-objects, or non-empty string is provided in data parameter', () => { 47 | // One could choose to pass a falsy value to the second param because they have or will 48 | // supply data with the set() method instead. 49 | 50 | qb.reset_query(); expect(() => qb.update('galaxies',test_data), 'non-empty array provided').to.not.throw(Error); 51 | qb.reset_query(); expect(() => qb.update('galaxies',test_data_set), 'array of non-empty standard objects provided').to.not.throw(Error); 52 | 53 | qb.reset_query(); expect(() => qb.set({id:2}).update('galaxies',NaN), 'NaN provided').to.not.throw(Error); 54 | qb.reset_query(); expect(() => qb.set({id:2}).update('galaxies',false), 'false provided').to.not.throw(Error); 55 | qb.reset_query(); expect(() => qb.set({id:2}).update('galaxies',''), 'empty string provided').to.not.throw(Error); 56 | qb.reset_query(); expect(() => qb.set({id:2}).update('galaxies',null), 'null provided').to.not.throw(Error); 57 | qb.reset_query(); expect(() => qb.set({id:2}).update('galaxies',undefined), 'undefined provided').to.not.throw(Error); 58 | qb.reset_query(); expect(() => qb.set({id:2}).update('galaxies'), 'nothing provided').to.not.throw(Error); 59 | 60 | qb.reset_query(); expect(() => qb.set({id:2}).update('galaxies',3), 'integer provided').to.throw(Error); 61 | qb.reset_query(); expect(() => qb.set({id:2}).update('galaxies',3.5), 'float provided').to.throw(Error); 62 | qb.reset_query(); expect(() => qb.set({id:2}).update('galaxies',true), 'true provided').to.throw(Error); 63 | qb.reset_query(); expect(() => qb.set({id:2}).update('galaxies',Infinity), 'Infinity provided').to.throw(Error); 64 | qb.reset_query(); expect(() => qb.set({id:2}).update('foobar',{}), 'empty object provided').to.throw(Error); 65 | qb.reset_query(); expect(() => qb.set({id:2}).update('galaxies',[{},{}]), 'array of empty objects provided').to.throw(Error); 66 | qb.reset_query(); expect(() => qb.set({id:2}).update('galaxies',[]), 'empty array provided').to.throw(Error); 67 | qb.reset_query(); expect(() => qb.set({id:2}).update('galaxies',[1,2]), 'array of numbers provided').to.throw(Error); 68 | qb.reset_query(); expect(() => qb.set({id:2}).update('galaxies',['abc',2,{foo:'bar'}]), 'array of mixed values provided').to.throw(Error); 69 | qb.reset_query(); expect(() => qb.set({id:2}).update('galaxies',/foobar/), 'regex provided').to.throw(Error); 70 | qb.reset_query(); expect(() => qb.set({id:2}).update('galaxies',' '), 'string full of spaces provided').to.throw(Error); 71 | }); 72 | it('should require that there is at least something being updated', () => { 73 | // TODO 74 | }); 75 | it('should utilize pre-existing tables set in from_array', () => { 76 | qb.reset_query(); 77 | qb.from('galaxies'); 78 | const sql = qb.update(null, test_data, test_where); 79 | sql.should.eql("UPDATE [galaxies] SET [name] = 'Milky Way', [type] = 'spiral' WHERE [id] = 3"); 80 | }); 81 | it('should utilize pre-existing value set in in set_array', () => { 82 | qb.reset_query(); 83 | qb.set(test_data); 84 | const sql = qb.update('galaxies'); 85 | sql.should.eql("UPDATE [galaxies] SET [name] = 'Milky Way', [type] = 'spiral'"); 86 | }); 87 | it('should utilize pre-existing tables and values from from_aray and set_array, respectively', () => { 88 | qb.reset_query(); 89 | qb.from('galaxies').set(test_data); 90 | const sql = qb.update(); 91 | sql.should.eql("UPDATE [galaxies] SET [name] = 'Milky Way', [type] = 'spiral'"); 92 | }); 93 | it('should accept a non-empty object for the data parameter', () => { 94 | qb.reset_query(); 95 | const sql = qb.update('galaxies', test_data); 96 | sql.should.eql("UPDATE [galaxies] SET [name] = 'Milky Way', [type] = 'spiral'"); 97 | }); 98 | it('should convert call to update_batch() if an array of non-emtpy objects is passed in the data parameter', () => { 99 | qb.reset_query(); 100 | const sql = qb.update('galaxies', test_data_set); 101 | qb.reset_query(); 102 | const sql_b = qb.update_batch('galaxies', test_data_set, 'id'); 103 | sql.should.eql(sql_b); 104 | }); 105 | it('should fail if any invalid values are passed in the data object.', () => { 106 | qb.reset_query(); 107 | const func = () => console.log("foo"); 108 | const regex = /foobar/; 109 | const arr = [1,2,3]; 110 | const obj = {foo: 'bar'}; 111 | 112 | qb.reset_query(); expect(() => qb.update('galaxies',{id: func}), 'function in data').to.throw(Error); 113 | qb.reset_query(); expect(() => qb.update('galaxies',{id: regex}), 'regex in data').to.throw(Error); 114 | qb.reset_query(); expect(() => qb.update('galaxies',{id: Infinity}), 'Infinity in data').to.throw(Error); 115 | qb.reset_query(); expect(() => qb.update('galaxies',{id: undefined}), 'undefined in data').to.throw(Error); 116 | qb.reset_query(); expect(() => qb.update('galaxies',{id: NaN}), 'NaN in data').to.throw(Error); 117 | qb.reset_query(); expect(() => qb.update('galaxies',{id: arr}), 'array in data').to.throw(Error); 118 | qb.reset_query(); expect(() => qb.update('galaxies',{id: obj}), 'object in data').to.throw(Error); 119 | 120 | }); 121 | it('should allow for a LIMITed update', () => { 122 | qb.reset_query(); 123 | const sql = qb.limit(10).update('galaxies', test_data, test_where); 124 | sql.should.eql("UPDATE [t] SET [name] = 'Milky Way', [type] = 'spiral' FROM (SELECT TOP (10) * FROM [galaxies] WHERE [id] = 3) [t]"); 125 | }); 126 | }); 127 | -------------------------------------------------------------------------------- /test/mssql/03-tests-update_batch.js: -------------------------------------------------------------------------------- 1 | const should = require('chai').should(); 2 | const expect = require('chai').expect; 3 | const QueryBuilder = require('../../drivers/mssql/query_builder.js'); 4 | const qb = new QueryBuilder(); 5 | 6 | const test_where = {quadrant: 'Alpha'}; 7 | const test_data = [{id:3, name:'Milky Way', type: 'spiral'}, {id:4, name: 'Andromeda', type: 'spiral'}]; 8 | 9 | describe('MSSQL: update_batch()', () => { 10 | it('should exist', () => { 11 | should.exist(qb.update_batch); 12 | }); 13 | it('should be a function', () => { 14 | qb.update_batch.should.be.a('function'); 15 | }); 16 | it('should build a proper batch UPDATE string', () => { 17 | qb.reset_query(); 18 | const sql = qb.update_batch('galaxies', test_data, 'id'); 19 | sql.should.eql(["UPDATE ([galaxies]) SET [name] = CASE WHEN [id] = 3 THEN 'Milky Way' WHEN [id] = 4 THEN 'Andromeda' ELSE [name] END, [type] = CASE WHEN [id] = 3 THEN 'spiral' WHEN [id] = 4 THEN 'spiral' ELSE [type] END WHERE [id] IN (3,4)"]); 20 | }); 21 | it('should build a proper batch UPDATE string when where clause is provided', () => { 22 | qb.reset_query(); 23 | const sql = qb.update_batch('galaxies', test_data, 'id', test_where); 24 | sql.should.eql(["UPDATE ([galaxies]) SET [name] = CASE WHEN [id] = 3 THEN 'Milky Way' WHEN [id] = 4 THEN 'Andromeda' ELSE [name] END, [type] = CASE WHEN [id] = 3 THEN 'spiral' WHEN [id] = 4 THEN 'spiral' ELSE [type] END WHERE [quadrant] = 'Alpha' AND [id] IN (3,4)"]); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /test/mssql/04-tests-query-promise.js: -------------------------------------------------------------------------------- 1 | const should = require('chai').should(); 2 | const expect = require('chai').expect; 3 | const QueryBuilder = require('../../index.js'); 4 | const settings = require('../configs').mssql; 5 | const driver = 'mssql'; 6 | 7 | const check = (done, f) => { 8 | try { 9 | f(); 10 | done(); 11 | } catch (e) { 12 | done(e); 13 | } 14 | }; 15 | 16 | describe('MSSQL: Query Promises', () => { 17 | it('should allow us to execute a simple SELECT query', async () => { 18 | const qb = new QueryBuilder(Object.assign({}, settings), driver); 19 | try { 20 | await qb.connect(); 21 | const res = await qb.like('city', 'Z', 'right').get_where('cities', { state_code: 'FL' }); 22 | 23 | expect(res, 'results should not be empty').to.not.be.empty; 24 | expect(res, 'should have 3 results').to.have.length(3); 25 | 26 | const expected_result = [{ city: 'Zellwood', state_code: 'FL' }, { city: 'Zephyrhills', state_code: 'FL' }, { city: 'Zolfo Springs', state_code: 'FL' }]; 27 | expect(res, 'should be just an array of objects representing the desired rows and columns').to.eql(expected_result); 28 | } catch (err) { 29 | throw err; 30 | } finally { 31 | 32 | } 33 | }); 34 | it('should have a javascript Standard Error object when running an invalid query', async () => { 35 | const qb = new QueryBuilder(Object.assign({}, settings), driver); 36 | try { 37 | await qb.connect(); 38 | await qb.query("select * = 'FL'"); 39 | } catch (err) { 40 | expect(err, 'there should be an error when the query is invalid').to.be.instanceof(Error); 41 | } finally { 42 | 43 | } 44 | }); 45 | it('should respond with an object explaining the results of an INSERT query', async () => { 46 | const qb = new QueryBuilder(Object.assign({}, settings), driver); 47 | try { 48 | await qb.connect(); 49 | const res = await qb.insert('cities', { city: 'Node QueryBuilder', state_code: 'NQ' }); 50 | 51 | expect(res, 'results should be an object with all the expected keys').to.be.an('object').that.includes.all.keys('insert_id', 'affected_rows', 'changed_rows'); 52 | expect(res.insert_id, 'insert id should be null').to.be.null; 53 | expect(res.affected_rows, 'affected_rows should be 1').to.eql(1); 54 | expect(res.changed_rows, 'changed_rows should be 0').to.eql(0); 55 | } catch (err) { 56 | throw err; 57 | } finally { 58 | 59 | } 60 | }); 61 | it('should respond with the requested IDs (from the `returning()` method) after insert', async () => { 62 | const qb = new QueryBuilder(Object.assign({}, settings), driver); 63 | 64 | try { 65 | await qb.connect(); 66 | const res = await qb.returning(['city', 'state_code']).insert('cities', { city: 'Node QB Returns', state_code: 'NQ' }); 67 | 68 | expect(res, 'results should be an object with all the expected keys').to.be.an('object').that.includes.all.keys('insert_id', 'affected_rows', 'changed_rows'); 69 | expect(res.insert_id, 'insert id should be the values of the ids requested').to.not.be.null; 70 | expect(res.affected_rows, 'affected_rows should be 1').to.eql(1); 71 | expect(res.changed_rows, 'changed_rows should be 0').to.eql(0); 72 | } catch (err) { 73 | throw err; 74 | } finally { 75 | 76 | } 77 | }); 78 | it('should respond with an object explaining the results of an UPDATE query', async () => { 79 | const qb = new QueryBuilder(Object.assign({}, settings), driver); 80 | try { 81 | await qb.connect(); 82 | const res = await qb.update('cities', { city: 'Node Query Builder' }, { state_code: 'NQ' }); 83 | 84 | expect(res, 'results should be an object with all the expected keys').to.be.an('object').that.includes.all.keys('insert_id', 'affected_rows', 'changed_rows'); 85 | expect(res.insert_id, 'insert id should be null').to.be.null; 86 | expect(res.affected_rows, 'affected_rows should be 1').to.gte(1); 87 | expect(res.changed_rows, 'changed_rows should be 1').to.gte(1); 88 | } catch (err) { 89 | throw err; 90 | } finally { 91 | 92 | } 93 | }); 94 | it('should respond with an object explaining the results of a DELETE query', async () => { 95 | const qb = new QueryBuilder(Object.assign({}, settings), driver); 96 | try { 97 | await qb.connect(); 98 | const res = await qb.delete('cities', { state_code: 'NQ' }); 99 | 100 | expect(res, 'results should be an object with all the expected keys').to.be.an('object').that.includes.all.keys('insert_id', 'affected_rows', 'changed_rows'); 101 | expect(res.insert_id, 'insert id should be null').to.be.null; 102 | expect(res.affected_rows, 'affected_rows should be 1').to.gte(1); 103 | expect(res.changed_rows, 'changed_rows should be 0').to.eql(0); 104 | } catch (err) { 105 | throw err; 106 | } finally { 107 | 108 | } 109 | }); 110 | }); 111 | -------------------------------------------------------------------------------- /test/mssql/04-tests-query-response.js: -------------------------------------------------------------------------------- 1 | const should = require('chai').should(); 2 | const expect = require('chai').expect; 3 | const QueryBuilder = require('../../index.js'); 4 | const settings = require('../configs').mssql; 5 | const driver = 'mssql'; 6 | 7 | const check = (done, f) => { 8 | try { 9 | f(); 10 | done(); 11 | } catch(e) { 12 | done(e); 13 | } 14 | }; 15 | 16 | describe('MSSQL: Query Responses', () => { 17 | it('should allow us to execute a simple SELECT query', done => { 18 | const qb = new QueryBuilder(Object.assign({}, settings), driver); 19 | qb.connect(err => { 20 | expect(err).to.not.be.instanceof(Error); 21 | 22 | qb.like('city', 'Z', 'right').get_where('cities', {state_code: 'FL'}, (err, res) => { 23 | check(done, () => { 24 | expect(err, 'there should not be an error when querying').to.not.be.instanceof(Error); 25 | expect(res, 'results should not be empty').to.not.be.empty; 26 | expect(res, 'should have 3 results').to.have.length(3); 27 | 28 | const expected_result = [{city: 'Zellwood', state_code: 'FL'},{city: 'Zephyrhills', state_code: 'FL'},{city: 'Zolfo Springs', state_code: 'FL'}]; 29 | expect(res, 'should be just an array of objects representing the desired rows and columns').to.eql(expected_result); 30 | }); 31 | }); 32 | }); 33 | }); 34 | it('should have a javascript Standard Error object when running an invalid query', done => { 35 | const qb = new QueryBuilder(Object.assign({}, settings), driver); 36 | qb.connect(err => { 37 | expect(err).to.not.be.instanceof(Error); 38 | 39 | qb.query("select * = 'FL'", (err, res) => { 40 | check(done, () => { 41 | expect(err, 'there should be an error when the query is invalid').to.be.instanceof(Error); 42 | }); 43 | }); 44 | }); 45 | }); 46 | it('should respond with an object explaining the results of an INSERT query', done => { 47 | const qb = new QueryBuilder(Object.assign({}, settings), driver); 48 | qb.connect(err => { 49 | expect(err).to.not.be.instanceof(Error); 50 | 51 | qb.insert('cities', {city: 'Node QueryBuilder', state_code: 'NQ'}, (err, res) => { 52 | check(done, () => { 53 | expect(err, 'there should not be an error when querying').to.not.be.instanceof(Error); 54 | expect(res, 'results should be an object with all the expected keys').to.be.an('object').that.includes.all.keys('insert_id', 'affected_rows', 'changed_rows'); 55 | expect(res.insert_id, 'insert id should be null').to.be.null; 56 | expect(res.affected_rows, 'affected_rows should be 1').to.eql(1); 57 | expect(res.changed_rows, 'changed_rows should be 0').to.eql(0); 58 | }); 59 | }); 60 | }); 61 | }); 62 | it('should respond with the requested IDs (from the `returning()` method) after insert', done => { 63 | const qb = new QueryBuilder(Object.assign({}, settings), driver); 64 | qb.connect(err => { 65 | expect(err, 'there should not be an error when querying').to.not.be.instanceof(Error); 66 | qb.returning(['city', 'state_code']).insert('cities', {city: 'Node QB Returns', state_code: 'NQ'}, (err, res) => { 67 | check(done, () => { 68 | expect(res, 'results should be an object with all the expected keys').to.be.an('object').that.includes.all.keys('insert_id', 'affected_rows', 'changed_rows'); 69 | expect(res.insert_id, 'insert id should be the values of the ids requested').to.not.be.null; 70 | expect(res.affected_rows, 'affected_rows should be 1').to.eql(1); 71 | expect(res.changed_rows, 'changed_rows should be 0').to.eql(0); 72 | }); 73 | }); 74 | }); 75 | }); 76 | it('should respond with an object explaining the results of an UPDATE query', done => { 77 | const qb = new QueryBuilder(Object.assign({}, settings), driver); 78 | qb.connect(err => { 79 | expect(err).to.not.be.instanceof(Error); 80 | 81 | qb.update('cities', {city: 'Node Query Builder'}, {state_code: 'NQ'}, (err, res) => { 82 | check(done, () => { 83 | expect(err, 'there should not be an error when querying').to.not.be.instanceof(Error); 84 | expect(res, 'results should be an object with all the expected keys').to.be.an('object').that.includes.all.keys('insert_id', 'affected_rows', 'changed_rows'); 85 | expect(res.insert_id, 'insert id should be null').to.be.null; 86 | expect(res.affected_rows, 'affected_rows should be 1').to.gte(1); 87 | expect(res.changed_rows, 'changed_rows should be 1').to.gte(1); 88 | }); 89 | }); 90 | }); 91 | }); 92 | it('should respond with an object explaining the results of a DELETE query', done => { 93 | const qb = new QueryBuilder(Object.assign({}, settings), driver); 94 | qb.connect(err => { 95 | expect(err).to.not.be.instanceof(Error); 96 | 97 | qb.delete('cities', {state_code: 'NQ'}, (err, res) => { 98 | check(done, () => { 99 | expect(err, 'there should not be an error when querying').to.not.be.instanceof(Error); 100 | expect(res, 'results should be an object with all the expected keys').to.be.an('object').that.includes.all.keys('insert_id', 'affected_rows', 'changed_rows'); 101 | expect(res.insert_id, 'insert id should be null').to.be.null; 102 | expect(res.affected_rows, 'affected_rows should be 1').to.gte(1); 103 | expect(res.changed_rows, 'changed_rows should be 0').to.eql(0); 104 | }); 105 | }); 106 | }); 107 | }); 108 | }); 109 | -------------------------------------------------------------------------------- /test/mssql/05-tests-multiple-pools.js: -------------------------------------------------------------------------------- 1 | const should = require('chai').should(); 2 | const expect = require('chai').expect; 3 | const settings = require('../configs').mssql; 4 | const QueryBuilder = require('../../index.js'); 5 | const pool1 = new QueryBuilder(settings, 'mssql', 'pool'); 6 | const pool2 = new QueryBuilder(Object.assign({}, settings, {database: 'mock_db2'}), 'mssql', 'pool'); 7 | let pool1_settings, pool2_settings; 8 | 9 | const compare_connections = (done) => { 10 | try { 11 | const db1 = pool1_settings.connection_settings.options.database; 12 | const db2 = pool2_settings.connection_settings.options.database; 13 | db1.should.not.be.eql(db2); 14 | done(); 15 | } catch(e) { 16 | done(e); 17 | } 18 | }; 19 | 20 | describe('MSSQL: Multiple Pools', () => { 21 | it('should not get confused by what pool settings to use', done => { 22 | let connections_established = 0; 23 | 24 | pool1.get_connection(qb1 => { 25 | pool1_settings = qb1.connection_settings(); 26 | connections_established++; 27 | if (connections_established >= 2) compare_connections(done); 28 | }); 29 | pool2.get_connection(qb2 => { 30 | pool2_settings = qb2.connection_settings(); 31 | connections_established++; 32 | if (connections_established >= 2) compare_connections(done); 33 | }); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /test/mssql/05-tests-multiple-queries.js: -------------------------------------------------------------------------------- 1 | const should = require('chai').should(); 2 | const expect = require('chai').expect; 3 | const settings = require('../configs').mssql; 4 | 5 | const check = (done, f) => { 6 | try { 7 | f(); 8 | done(); 9 | } catch(e) { 10 | done(e); 11 | } 12 | }; 13 | 14 | const QueryBuilder = require('../../index.js'); 15 | const pool = new QueryBuilder(settings, 'mssql', 'pool'); 16 | 17 | describe('MSSQL: Multiple Queries', () => { 18 | it('should not get confused about table after deleting records', done => { 19 | pool.get_connection(qb => { 20 | qb.limit(1).delete('cities', (err, result) => { 21 | qb.select(['city', 'state_code']).from('cities').limit(1).get((err2, result2) => { 22 | qb.release(); 23 | check(done, () => { 24 | expect(err, 'should not error on delete').to.not.be.instanceof(Error); 25 | expect(result.affected_rows, 'one record should be deleted').to.be.eql(1); 26 | expect(err2, 'should not error on select').to.not.be.instanceof(Error); 27 | expect(result2.length, 'should have one result').to.be.equal(1); 28 | }); 29 | }); 30 | }); 31 | }); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /test/mssql/create_mssql_mock.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | server=localhost 3 | admin_user=sa 4 | admin_pass=Password123 5 | db_user=travis 6 | password=Password123 7 | db_name=mock_db 8 | db_table=cities 9 | db2_name=mock_db2 10 | db2_table=cities2 11 | 12 | # Initial Admin Stuff 13 | sudo docker exec -it mssql-server-linux-latest /opt/mssql-tools/bin/sqlcmd -S $server -U $admin_user -P $admin_pass -Q "If not Exists (SELECT loginname FROM [master].[dbo].[syslogins] WHERE name = '$db_user') CREATE LOGIN $db_user WITH PASSWORD='$password';" 14 | sudo docker exec -it mssql-server-linux-latest /opt/mssql-tools/bin/sqlcmd -S $server -U $admin_user -P $admin_pass -Q "IF (db_id(N'$db_name') IS NULL) CREATE DATABASE $db_name;" 15 | sudo docker exec -it mssql-server-linux-latest /opt/mssql-tools/bin/sqlcmd -S $server -U $admin_user -P $admin_pass -Q "IF (db_id(N'$db2_name') IS NULL) CREATE DATABASE $db2_name;" 16 | sudo docker exec -it mssql-server-linux-latest /opt/mssql-tools/bin/sqlcmd -S $server -U $admin_user -P $admin_pass -d $db_name -Q "CREATE USER $db_user FOR LOGIN $db_user;" > /dev/null 2>&1 17 | sudo docker exec -it mssql-server-linux-latest /opt/mssql-tools/bin/sqlcmd -S $server -U $admin_user -P $admin_pass -d $db2_name -Q "CREATE USER $db_user FOR LOGIN $db_user;" > /dev/null 2>&1 18 | sudo docker exec -it mssql-server-linux-latest /opt/mssql-tools/bin/sqlcmd -S $server -U $admin_user -P $admin_pass -d $db_name -Q "GRANT CONTROL ON DATABASE:: $db_name TO $db_user;" 19 | sudo docker exec -it mssql-server-linux-latest /opt/mssql-tools/bin/sqlcmd -S $server -U $admin_user -P $admin_pass -d $db2_name -Q "GRANT CONTROL ON DATABASE:: $db2_name TO $db_user;" 20 | 21 | # User stuff 22 | sudo docker exec -it mssql-server-linux-latest /opt/mssql-tools/bin/sqlcmd -S $server -U $db_user -P $password -d $db_name -Q "IF OBJECT_ID('dbo.$db_table', 'U') IS NOT NULL DROP TABLE dbo.$db_table;" 23 | sudo docker exec -it mssql-server-linux-latest /opt/mssql-tools/bin/sqlcmd -S $server -U $db_user -P $password -d $db2_name -Q "IF OBJECT_ID('dbo.$db2_table', 'U') IS NOT NULL DROP TABLE dbo.$db2_table;" 24 | sudo docker exec -it mssql-server-linux-latest /opt/mssql-tools/bin/sqlcmd -S $server -U $db_user -P $password -d $db_name -Q "CREATE TABLE $db_table([city] varchar(50) NOT NULL, [state_code] char(2) NOT NULL);" 25 | sudo docker exec -it mssql-server-linux-latest /opt/mssql-tools/bin/sqlcmd -S $server -U $db_user -P $password -d $db2_name -Q "CREATE TABLE $db2_table([city] varchar(50) NOT NULL, [state_code] char(2) NOT NULL);" 26 | sudo docker exec -it mssql-server-linux-latest /opt/mssql-tools/bin/sqlcmd -S $server -U $db_user -P $password -d $db_name -i mock_data.sql 27 | sudo docker exec -it mssql-server-linux-latest /opt/mssql-tools/bin/sqlcmd -S $server -U $db_user -P $password -d $db2_name -i mock_data2.sql 28 | 29 | # Check to see if table exists now... 30 | #sudo docker exec -it mssql-server-linux-latest /opt/mssql-tools/bin/sqlcmd -U $db_user -P $password -d $db_name -Q "SELECT TABLE_NAME FROM $db_name.INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE'" 31 | 32 | # Check to see if data exists in $db_name 33 | sudo docker exec -it mssql-server-linux-latest /opt/mssql-tools/bin/sqlcmd -S $server -U $db_user -P $password -d $db_name -Q "SELECT TOP 5 * FROM [$db_table];" 34 | sudo docker exec -it mssql-server-linux-latest /opt/mssql-tools/bin/sqlcmd -S $server -U $db_user -P $password -d $db2_name -Q "SELECT TOP 5 * FROM [$db2_table];" 35 | 36 | echo "Done with MS SQL Import" -------------------------------------------------------------------------------- /test/mssql/mock_data2.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO [cities2] VALUES ('Aaronsburg','PA'),('Abbeville','AL'),('Abbeville','GA'); 2 | -------------------------------------------------------------------------------- /test/mysql/00-AA-tests-general.js: -------------------------------------------------------------------------------- 1 | const should = require('chai').should(); 2 | 3 | describe('MySQL: QueryBuilder', () => { 4 | it('actually exists and can be initialized', () => { 5 | const QueryBuilder = require('../../drivers/mysql/query_builder.js'); 6 | const qb = new QueryBuilder(); 7 | qb.should.be.instanceOf(QueryBuilder); 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /test/mysql/01-tests-distinct.js: -------------------------------------------------------------------------------- 1 | const should = require('chai').should(); 2 | const expect = require('chai').expect; 3 | const QueryBuilder = require('../../drivers/mysql/query_builder.js'); 4 | const qb = new QueryBuilder(); 5 | 6 | describe('MySQL: distinct()', () => { 7 | it('should exist', () => { 8 | should.exist(qb.distinct); 9 | }); 10 | it('should be a function', () => { 11 | qb.distinct.should.be.a('function'); 12 | }); 13 | it('should override the default distinct_clause with the "DISTINCT " keyword', () => { 14 | qb.reset_query(); 15 | qb.distinct(); 16 | qb.distinct_clause.should.eql(['DISTINCT ']); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /test/mysql/01-tests-from.js: -------------------------------------------------------------------------------- 1 | const should = require('chai').should(); 2 | const QueryBuilder = require('../../drivers/mysql/query_builder.js'); 3 | const qb = new QueryBuilder(); 4 | 5 | describe('MySQL: from()', () => { 6 | it('should exist', () => { 7 | should.exist(qb.from); 8 | }); 9 | it('should be a function', () => { 10 | qb.from.should.be.a('function'); 11 | }); 12 | it('should have an array to put fields into', () => { 13 | qb.should.have.property('from_array'); 14 | }); 15 | it('should have an empty array to put fields into at the beginning', () => { 16 | qb.from_array.should.be.empty; 17 | }); 18 | it('should add an item to an array and escape it properly', () => { 19 | qb.from('universe'); 20 | qb.from_array.should.eql(['`universe`']); 21 | }) 22 | it('should accept a comma-delimited string of items and trim and escape each properly', () => { 23 | qb.reset_query(); 24 | qb.from('universe,galaxy , star_system, planet'); 25 | qb.from_array.should.eql(['`universe`','`galaxy`','`star_system`','`planet`']); 26 | }); 27 | it('should have an empty array after resetting', () => { 28 | qb.reset_query(); 29 | qb.from_array.should.be.empty; 30 | }); 31 | it('should be allowed to be called multiple times to add multiple items to the from array', () => { 32 | qb.reset_query(); 33 | qb.from('universe').from('galaxy').from('star_system').from('planet'); 34 | qb.from_array.should.eql(['`universe`','`galaxy`','`star_system`','`planet`']); 35 | }); 36 | it('should accept an array of items and add them individually to the from array', () => { 37 | qb.reset_query(); 38 | qb.from(['universe','galaxy','star_system','planet']); 39 | qb.from_array.should.eql(['`universe`','`galaxy`','`star_system`','`planet`']); 40 | }); 41 | it('should not double-escape an item', () => { 42 | qb.reset_query(); 43 | qb.from('`do`'); 44 | qb.from_array.should.eql(['`do`']); 45 | }); 46 | it('should not double-escape items when provided with an array of pre-escaped items', () => { 47 | qb.reset_query(); 48 | qb.from(['`universe`','`galaxy`','`star_system`']); 49 | qb.from_array.should.eql(['`universe`','`galaxy`','`star_system`']); 50 | }); 51 | it('should not double-escape items when provided with an array of pre-escaped items but should escpae non-pre-escaped items', () => { 52 | qb.reset_query(); 53 | qb.from(['`universe`','galaxy','`star_system`']); 54 | qb.from_array.should.eql(['`universe`','`galaxy`','`star_system`']); 55 | }); 56 | it('should allow for aliases and it should escape them properly', () => { 57 | qb.reset_query(); 58 | qb.from('universe u'); 59 | qb.from_array.should.eql(['`universe` `u`']); 60 | }); 61 | it('should allow for the word AS to be used to alias an item', () => { 62 | qb.reset_query(); 63 | qb.from('universe as u'); 64 | qb.from_array.should.eql(['`universe` as `u`']); 65 | }); 66 | it('should allow for an array of item + aliases and it should escape them all properly', () => { 67 | qb.reset_query(); 68 | qb.from(['universe u', 'galaxy g']); 69 | qb.from_array.should.eql(['`universe` `u`','`galaxy` `g`']); 70 | }); 71 | it('should allow for an array of item + aliases that are pre-escaped and it should not double-escape them', () => { 72 | qb.reset_query(); 73 | qb.from(['`universe` `u`', '`galaxy` `g`']); 74 | qb.from_array.should.eql(['`universe` `u`','`galaxy` `g`']); 75 | }); 76 | it('should allow for an array of item + aliases where some are pre-escaped and it should not double-escape pre-escaped items', () => { 77 | qb.reset_query(); 78 | qb.from(['`universe` u', 'galaxy `g`']); 79 | qb.from_array.should.eql(['`universe` `u`','`galaxy` `g`']); 80 | }); 81 | it('should add aliases to alias-tracking array', () => { 82 | qb.reset_query(); 83 | qb.from(['`universe` `u`', '`galaxy` `g`']); 84 | qb.aliased_tables.should.eql(['u','g']); 85 | }); 86 | it('should allow for an comma-delimited list of item + aliases and it should escape them all properly', () => { 87 | qb.reset_query(); 88 | qb.from(['universe u, galaxy g']); 89 | qb.from_array.should.eql(['`universe` `u`','`galaxy` `g`']); 90 | }); 91 | it('should allow for namespacing in field name (host.db.table)', () => { 92 | qb.reset_query(); 93 | qb.from('star_system.planet'); 94 | qb.from_array.should.eql(['`star_system`.`planet`']); 95 | 96 | qb.reset_query(); 97 | qb.from('galaxy.star_system.planet'); 98 | qb.from_array.should.eql(['`galaxy`.`star_system`.`planet`']); 99 | }); 100 | it('should allow for namespacing in field name (host.db.table.column) + alias', () => { 101 | qb.reset_query(); 102 | qb.from('universe.galaxy.star_system planet'); 103 | qb.from_array.should.eql(['`universe`.`galaxy`.`star_system` `planet`']); 104 | }); 105 | it('should allow for namespacing in field name (host.db.table.column) + alias (declare with AS)', () => { 106 | qb.reset_query(); 107 | qb.from('universe.galaxy.star_system as planet'); 108 | qb.from_array.should.eql(['`universe`.`galaxy`.`star_system` as `planet`']); 109 | }); 110 | it('should accept but ignore empty strings and empty strings within arrays', () => { 111 | qb.reset_query(); 112 | qb.from(''); 113 | qb.from_array.should.be.empty; 114 | 115 | qb.reset_query(); 116 | qb.from(['','']); 117 | qb.from_array.should.be.empty; 118 | 119 | qb.reset_query(); 120 | qb.from(['','foobar']); 121 | qb.from_array.should.eql(['`foobar`']); 122 | }); 123 | }); 124 | -------------------------------------------------------------------------------- /test/mysql/01-tests-groupby.js: -------------------------------------------------------------------------------- 1 | const should = require('chai').should(); 2 | const expect = require('chai').expect; 3 | const QueryBuilder = require('../../drivers/mysql/query_builder.js'); 4 | const qb = new QueryBuilder(); 5 | 6 | describe('MySQL: group_by()', () => { 7 | it('should exist', () => { 8 | should.exist(qb.group_by); 9 | }); 10 | it('should be a function', () => { 11 | qb.group_by.should.be.a('function'); 12 | }); 13 | it('should have an array to put fields into', () => { 14 | qb.should.have.property('group_by_array'); 15 | }); 16 | it('should have an empty array to put fields into at the beginning', () => { 17 | qb.group_by_array.should.be.empty; 18 | }); 19 | it('should accept a single field in string form', () => { 20 | qb.reset_query(); 21 | qb.group_by('planet_type'); 22 | qb.group_by_array.should.eql(['`planet_type`']); 23 | }); 24 | it('should accept a multiple fields delimited by commas', () => { 25 | qb.reset_query(); 26 | qb.group_by('planet_type, planet_position'); 27 | qb.group_by_array.should.eql(['`planet_type`','`planet_position`']); 28 | }); 29 | it('should accept an array of fields', () => { 30 | qb.reset_query(); 31 | qb.group_by(['planet_type', 'planet_position']); 32 | qb.group_by_array.should.eql(['`planet_type`','`planet_position`']); 33 | }); 34 | it('should not accept anything but a string or an array of strings', () => { 35 | qb.reset_query(); 36 | expect(() => qb.group_by(), 'nothing provided').to.throw(Error); 37 | expect(() => qb.group_by(null), 'null provided').to.throw(Error); 38 | expect(() => qb.group_by(false), 'false provided').to.throw(Error); 39 | expect(() => qb.group_by(true), 'true provided').to.throw(Error); 40 | expect(() => qb.group_by({}), 'empty object provided').to.throw(Error); 41 | expect(() => qb.group_by(3), 'integer provided').to.throw(Error); 42 | expect(() => qb.group_by(3.5), 'float provided').to.throw(Error); 43 | expect(() => qb.group_by([]), 'empty array provided').to.throw(Error); 44 | expect(() => qb.group_by([1,2]), 'array of numbers provided').to.throw(Error); 45 | expect(() => qb.group_by(''), 'empty string provided').to.throw(Error); 46 | 47 | // valid string 48 | expect(() => qb.group_by('planet_type'), 'valid string provided').to.not.throw(Error); 49 | expect(() => qb.group_by(['planet_type']), 'array of string(s) provided').to.not.throw(Error); 50 | 51 | }); 52 | }); 53 | -------------------------------------------------------------------------------- /test/mysql/01-tests-having.js: -------------------------------------------------------------------------------- 1 | const should = require('chai').should(); 2 | const expect = require('chai').expect; 3 | const QueryBuilder = require('../../drivers/mysql/query_builder.js'); 4 | const qb = new QueryBuilder(); 5 | 6 | describe('MySQL: having()', () => { 7 | it('should exist', () => { 8 | should.exist(qb.having); 9 | }); 10 | it('should be a function', () => { 11 | qb.having.should.be.a('function'); 12 | }); 13 | it('should have an array to put fields into', () => { 14 | qb.should.have.property('having_array'); 15 | }); 16 | it('should accept a string only in this format: a [>|<|<>|>=|<=|=|!=] b for the first parameter', () => { 17 | qb.reset_query(); 18 | qb.having('planet_class > "M"'); 19 | qb.having_array.should.eql(["`planet_class` > 'M'"]); 20 | 21 | qb.reset_query(); 22 | qb.having('planet_class < "M"'); 23 | qb.having_array.should.eql(["`planet_class` < 'M'"]); 24 | 25 | qb.reset_query(); 26 | qb.having('planet_class <> "M"'); 27 | qb.having_array.should.eql(["`planet_class` <> 'M'"]); 28 | 29 | qb.reset_query(); 30 | qb.having('planet_class >= "M"'); 31 | qb.having_array.should.eql(["`planet_class` >= 'M'"]); 32 | 33 | qb.reset_query(); 34 | qb.having('planet_class <= "M"'); 35 | qb.having_array.should.eql(["`planet_class` <= 'M'"]); 36 | 37 | qb.reset_query(); 38 | qb.having('planet_class = "M"'); 39 | qb.having_array.should.eql(["`planet_class` = 'M'"]); 40 | 41 | qb.reset_query(); 42 | qb.having('planet_class != "M"'); 43 | qb.having_array.should.eql(["`planet_class` != 'M'"]); 44 | }); 45 | it('should not accept compound conditions in this format: a [>|<|<>|>=|<=|=|!=] b[, repeat[, etc...]]', () => { 46 | qb.reset_query(); 47 | expect(() => qb.having('planet_class = "M", sentient_life = 1'), 'two conditions provided').to.throw(Error); 48 | }); 49 | it('should accept an array of conditions and prepend AND to each condition following the first one', () => { 50 | qb.reset_query(); 51 | qb.having(["planet_class = 'M'", 'sentient_life = 1']); 52 | qb.having_array.should.eql(["`planet_class` = 'M'", 'AND `sentient_life` = 1']); 53 | }); 54 | it('should accept an object of conditions and prepend AND to each condition following the first one', () => { 55 | qb.reset_query(); 56 | const object = {planet_class: 'M', sentient_life: 1}; 57 | object['planet_order <='] = 3; 58 | qb.having(object); 59 | qb.having_array.should.eql(["`planet_class` = 'M'", 'AND `sentient_life` = 1','AND `planet_order` <= 3']); 60 | }); 61 | it('should not accept anything but a non-empty array, object, or string', () => { 62 | qb.reset_query(); 63 | expect(() => qb.group_by(), 'nothing provided').to.throw(Error); 64 | expect(() => qb.group_by(null), 'null provided').to.throw(Error); 65 | expect(() => qb.group_by(false), 'false provided').to.throw(Error); 66 | expect(() => qb.group_by(true), 'true provided').to.throw(Error); 67 | expect(() => qb.group_by({}), 'empty object provided').to.throw(Error); 68 | expect(() => qb.group_by(3), 'integer provided').to.throw(Error); 69 | expect(() => qb.group_by(3.5), 'float provided').to.throw(Error); 70 | expect(() => qb.group_by([]), 'empty array provided').to.throw(Error); 71 | expect(() => qb.group_by([1,2]), 'array of numbers provided').to.throw(Error); 72 | expect(() => qb.group_by(''), 'empty string provided').to.throw(Error); 73 | 74 | // valid string 75 | expect(() => qb.group_by('planet_type = "M"'), 'valid string provided').to.not.throw(Error); 76 | expect(() => qb.group_by(['planet_type = "M"']), 'array of string(s) provided').to.not.throw(Error); 77 | }); 78 | it('should accept 2 parameters where the first one is the field with optional condition and the second one is the value', () => { 79 | qb.reset_query(); 80 | qb.having('planet_class','M'); 81 | qb.having_array.should.eql(["`planet_class` = 'M'"]); 82 | }); 83 | it('should not escape conditions if asked not to', () => { 84 | qb.reset_query(); 85 | qb.having(["planet_class = 'M'", 'sentient_life = 1'], null, false); 86 | qb.having_array.should.eql(["planet_class = 'M'", 'AND sentient_life = 1']); 87 | }); 88 | it('should be chainable', () => { 89 | qb.reset_query(); 90 | qb.having('planet_class','M').having('sentient_life',true).having('planet_order <=',3); 91 | qb.having_array.should.eql(["`planet_class` = 'M'", 'AND `sentient_life` = 1','AND `planet_order` <= 3']); 92 | }); 93 | }); 94 | 95 | describe('MySQL: or_having()', () => { 96 | it('should exist', () => { 97 | should.exist(qb.or_having); 98 | }); 99 | it('should be a function', () => { 100 | qb.or_having.should.be.a('function'); 101 | }); 102 | it('should accept an array of conditions and prepend OR to each condition following the first one', () => { 103 | qb.reset_query(); 104 | qb.or_having(["planet_class = 'M'", 'sentient_life = 1']); 105 | qb.having_array.should.eql(["`planet_class` = 'M'", 'OR `sentient_life` = 1']); 106 | }); 107 | it('should be chainable with normal having', () => { 108 | qb.reset_query(); 109 | qb.having('planet_class','M').having('sentient_life',true).or_having('planet_order <=',3); 110 | qb.having_array.should.eql(["`planet_class` = 'M'", 'AND `sentient_life` = 1','OR `planet_order` <= 3']); 111 | }); 112 | }); 113 | -------------------------------------------------------------------------------- /test/mysql/01-tests-limit.js: -------------------------------------------------------------------------------- 1 | const should = require('chai').should(); 2 | const expect = require('chai').expect; 3 | const QueryBuilder = require('../../drivers/mysql/query_builder.js'); 4 | const qb = new QueryBuilder(); 5 | 6 | describe('MySQL: limit()', () => { 7 | it('should exist', () => { 8 | should.exist(qb.limit); 9 | }); 10 | it('should be a function', () => { 11 | qb.limit.should.be.a('function'); 12 | }); 13 | it('should have an array to put fields into', () => { 14 | qb.should.have.property('limit_to'); 15 | }); 16 | it('should have an empty array to put fields into at the beginning', () => { 17 | qb.limit_to.should.be.empty; 18 | }); 19 | it('should require an integer (or integer in string form) in first parameter', () => { 20 | qb.reset_query(); 21 | expect(() => qb.limit(5), 'integer provided').to.not.throw(Error); 22 | expect(() => qb.limit('5'), '5 in string form provided').to.not.throw(Error); 23 | expect(() => qb.limit(5.7), 'float provided').to.throw(Error); 24 | expect(() => qb.limit('5.7'), 'float provided').to.throw(Error); 25 | expect(() => qb.limit('abc'), 'alpha provided').to.throw(Error); 26 | expect(() => qb.limit('abc7'), 'alpha numerics provided').to.throw(Error); 27 | expect(() => qb.limit(), 'nothing provided').to.throw(Error); 28 | expect(() => qb.limit(null), 'null provided').to.throw(Error); 29 | expect(() => qb.limit(true), 'true provided').to.throw(Error); 30 | expect(() => qb.limit(false), 'false provided').to.throw(Error); 31 | expect(() => qb.limit(''), 'empty string provided').to.throw(Error); 32 | expect(() => qb.limit({}), 'empty object provided').to.throw(Error); 33 | expect(() => qb.limit([]), 'empty array provided').to.throw(Error); 34 | expect(() => qb.limit([5]), 'array with integer in it provided').to.throw(Error); 35 | }); 36 | it('should allow an integer (or integer in string form) in second parameter. Nothing else is allowed.', () => { 37 | qb.reset_query(); 38 | expect(() => qb.limit(10,5), 'integer provided').to.not.throw(Error); 39 | expect(() => qb.limit(10,'5'), '5 in string form provided').to.not.throw(Error); 40 | expect(() => qb.limit(10,5.7), 'float provided').to.throw(Error); 41 | expect(() => qb.limit(10,'5.7'), 'float provided').to.throw(Error); 42 | expect(() => qb.limit(10,'abc'), 'alpha provided').to.throw(Error); 43 | expect(() => qb.limit(10,'abc7'), 'alphanumerics provided').to.throw(Error); 44 | expect(() => qb.limit(10,null), 'null provided').to.throw(Error); 45 | expect(() => qb.limit(10,true), 'true provided').to.throw(Error); 46 | expect(() => qb.limit(10,false), 'false provided').to.throw(Error); 47 | expect(() => qb.limit(10,''), 'empty string provided').to.throw(Error); 48 | expect(() => qb.limit(10,{}), 'empty object provided').to.throw(Error); 49 | expect(() => qb.limit(10,[]), 'empty array provided').to.throw(Error); 50 | expect(() => qb.limit(10,[5]), 'array with integer in it provided').to.throw(Error); 51 | }); 52 | it('should override the default limit_to value when a limit is provided', () => { 53 | qb.reset_query(); 54 | qb.limit(10); 55 | qb.limit_to.should.eql([10]); 56 | }); 57 | it('should override the default limit_to and offset_val values when a limit and an offset are provided', () => { 58 | qb.reset_query(); 59 | qb.limit(10,20); 60 | qb.limit_to.should.eql([10]); 61 | qb.offset_val.should.eql([20]); 62 | }); 63 | it('should trim string values that are provided', () => { 64 | qb.reset_query(); 65 | qb.limit('10 '); 66 | qb.limit_to.should.eql([10]); 67 | }); 68 | it('should trim string values that are provided', () => { 69 | qb.reset_query(); 70 | qb.limit(' 10 ',' 12'); 71 | qb.limit_to.should.eql([10]); 72 | qb.offset_val.should.eql([12]); 73 | }); 74 | it('should override values set by any previous calls to itself', () => { 75 | qb.reset_query(); 76 | qb.limit(10); 77 | qb.limit_to.should.eql([10]); 78 | qb.limit(20); 79 | qb.limit_to.should.eql([20]); 80 | }); 81 | it('should be chainable whereby the last call to the method will contain the value(s) used', () => { 82 | qb.reset_query(); 83 | qb.limit(10,5).limit(20).limit(100,30); 84 | qb.limit_to.should.eql([100]); 85 | qb.offset_val.should.eql([30]); 86 | }); 87 | }); 88 | -------------------------------------------------------------------------------- /test/mysql/01-tests-offset.js: -------------------------------------------------------------------------------- 1 | const should = require('chai').should(); 2 | const expect = require('chai').expect; 3 | const QueryBuilder = require('../../drivers/mysql/query_builder.js'); 4 | const qb = new QueryBuilder(); 5 | 6 | describe('MySQL: offset()', () => { 7 | it('should exist', () => { 8 | should.exist(qb.offset); 9 | }); 10 | it('should be a function', () => { 11 | qb.offset.should.be.a('function'); 12 | }); 13 | it('should have an array to put fields into', () => { 14 | qb.should.have.property('offset_val'); 15 | }); 16 | it('should have an empty array to put fields into at the beginning', () => { 17 | qb.offset_val.should.be.empty; 18 | }); 19 | it('should require an integer (or integer in string form) in first parameter', () => { 20 | qb.reset_query(); 21 | expect(() => qb.offset(5), 'integer provided').to.not.throw(Error); 22 | expect(() => qb.offset('5'), '5 in string form provided').to.not.throw(Error); 23 | expect(() => qb.offset(5.7), 'float provided').to.throw(Error); 24 | expect(() => qb.offset('5.7'), 'float provided').to.throw(Error); 25 | expect(() => qb.offset('abc'), 'alpha provided').to.throw(Error); 26 | expect(() => qb.offset('abc7'), 'alpha numerics provided').to.throw(Error); 27 | expect(() => qb.offset(), 'nothing provided').to.throw(Error); 28 | expect(() => qb.offset(null), 'null provided').to.throw(Error); 29 | expect(() => qb.offset(true), 'true provided').to.throw(Error); 30 | expect(() => qb.offset(false), 'false provided').to.throw(Error); 31 | expect(() => qb.offset(''), 'empty string provided').to.throw(Error); 32 | expect(() => qb.offset({}), 'empty object provided').to.throw(Error); 33 | expect(() => qb.offset([]), 'empty array provided').to.throw(Error); 34 | expect(() => qb.offset([5]), 'array with integer in it provided').to.throw(Error); 35 | }); 36 | it('should override the default offset_val value when a offset is provided', () => { 37 | qb.reset_query(); 38 | qb.offset(10); 39 | qb.offset_val.should.eql([10]); 40 | }); 41 | it('should trim string values that are provided', () => { 42 | qb.reset_query(); 43 | qb.offset('10 '); 44 | qb.offset(' 10'); 45 | qb.offset(' 10 '); 46 | qb.offset_val.should.eql([10]); 47 | }); 48 | it('should override values set by any previous calls to itself', () => { 49 | qb.reset_query(); 50 | qb.offset(10); 51 | qb.offset_val.should.eql([10]); 52 | qb.offset(20); 53 | qb.offset_val.should.eql([20]); 54 | }); 55 | it('should be chainable whereby the last call to the method will contain the value used', () => { 56 | qb.reset_query(); 57 | qb.offset(10).offset(20).offset(100); 58 | qb.offset_val.should.eql([100]); 59 | }); 60 | }); 61 | -------------------------------------------------------------------------------- /test/mysql/01-tests-orderby.js: -------------------------------------------------------------------------------- 1 | const should = require('chai').should(); 2 | const expect = require('chai').expect; 3 | const QueryBuilder = require('../../drivers/mysql/query_builder.js'); 4 | const qb = new QueryBuilder(); 5 | 6 | describe('MySQL: order_by()', () => { 7 | it('should exist', () => { 8 | should.exist(qb.order_by); 9 | }); 10 | it('should be a function', () => { 11 | qb.order_by.should.be.a('function'); 12 | }); 13 | it('should have an array to put fields into', () => { 14 | qb.should.have.property('order_by_array'); 15 | }); 16 | it('should have an empty array to put fields into at the beginning', () => { 17 | qb.order_by_array.should.be.empty; 18 | }); 19 | it('should require non-empty string or array as first param unless random is provided as second parameter', () => { 20 | expect(() => qb.order_by(), 'nothing provided').to.throw(Error); 21 | expect(() => qb.order_by(null), 'null provided').to.throw(Error); 22 | expect(() => qb.order_by(false), 'false provided').to.throw(Error); 23 | expect(() => qb.order_by(true), 'true provided').to.throw(Error); 24 | expect(() => qb.order_by({}), 'empty object provided').to.throw(Error); 25 | expect(() => qb.order_by(3), 'integer provided').to.throw(Error); 26 | expect(() => qb.order_by(3.5), 'float provided').to.throw(Error); 27 | expect(() => qb.order_by([]), 'empty array provided').to.throw(Error); 28 | expect(() => qb.order_by(''), 'empty string provided').to.throw(Error); 29 | // If random 30 | expect(() => qb.order_by('','rand'), 'empty string and random direction provided').to.not.throw(Error); 31 | expect(() => qb.order_by(undefined,'rand'), 'undefined and random direction provided').to.not.throw(Error); 32 | expect(() => qb.order_by(null,'rand'), 'null and random direction provided').to.not.throw(Error); 33 | expect(() => qb.order_by(false,'rand'), 'false and random direction provided').to.not.throw(Error); 34 | expect(() => qb.order_by([],'rand'), 'empty array and random direction provided').to.not.throw(Error); 35 | }); 36 | it('should accept a field and direction separated by a space as first parameter and escape the field', () => { 37 | qb.reset_query(); 38 | qb.order_by('planet_position asc'); 39 | qb.order_by_array.should.eql(['`planet_position` ASC']); 40 | }); 41 | it('should accept a field and direction as seperate parameters and escape the field', () => { 42 | qb.reset_query(); 43 | qb.order_by('planet_position', 'asc'); 44 | qb.order_by_array.should.eql(['`planet_position` ASC']); 45 | }); 46 | it('should add additional order_by calls to teh order by array', () => { 47 | qb.reset_query(); 48 | qb.order_by('planet_position', 'asc'); 49 | qb.order_by('planet_size', 'desc'); 50 | qb.order_by_array.should.eql(['`planet_position` ASC', '`planet_size` DESC']); 51 | }); 52 | it('should be chainable', () => { 53 | qb.reset_query(); 54 | qb.order_by('planet_position', 'asc').order_by('planet_size', 'desc'); 55 | qb.order_by_array.should.eql(['`planet_position` ASC', '`planet_size` DESC']); 56 | }); 57 | it('should assume ASC when no direction is provided', () => { 58 | qb.reset_query(); 59 | qb.order_by('planet_position'); 60 | qb.order_by_array.should.eql(['`planet_position` ASC']); 61 | }); 62 | it('should only accept valid ordering directions (ASC, DESC, random)', () => { 63 | qb.reset_query(); 64 | expect(() => qb.order_by('planet_position')).to.not.throw(Error); 65 | expect(() => qb.order_by('planet_position','ASC')).to.not.throw(Error); 66 | expect(() => qb.order_by('planet_position','DESC')).to.not.throw(Error); 67 | expect(() => qb.order_by('planet_position','random')).to.not.throw(Error); 68 | expect(() => qb.order_by('planet_position',null)).to.not.throw(Error); 69 | expect(() => qb.order_by('planet_position',undefined)).to.not.throw(Error); 70 | expect(() => qb.order_by('planet_position',false)).to.not.throw(Error); 71 | expect(() => qb.order_by('planet_position',3)).to.not.throw(Error); 72 | expect(() => qb.order_by('planet_position',true)).to.not.throw(Error); 73 | expect(() => qb.order_by('planet_position',[])).to.not.throw(Error); 74 | expect(() => qb.order_by('planet_position',{})).to.not.throw(Error); 75 | expect(() => qb.order_by('planet_position','')).to.not.throw(Error); 76 | 77 | // Only an invalid string will throw an error 78 | expect(() => qb.order_by('planet_position','FAKE')).to.throw(Error); 79 | }); 80 | it('should accept a comma-separated list of fields to order by with a single direction at the end', () => { 81 | qb.reset_query(); 82 | qb.order_by('planet_position, planet_size asc'); 83 | qb.order_by_array.should.eql(['`planet_position` ASC', '`planet_size` ASC']); 84 | }); 85 | it('should accept a comma-separated list of field + direction pairs', () => { 86 | qb.reset_query(); 87 | qb.order_by('planet_position desc, planet_size asc'); 88 | qb.order_by_array.should.eql(['`planet_position` DESC', '`planet_size` ASC']); 89 | }); 90 | it('should accept a random direction in three forms: "random", "RAND", "RAND()" (case-insensitively) and remove field(s) from statement', () => { 91 | qb.reset_query(); 92 | qb.order_by('planet_position', 'random'); 93 | qb.order_by_array.should.eql(['RAND()']); 94 | 95 | qb.reset_query(); 96 | qb.order_by('planet_size', 'RAND'); 97 | qb.order_by_array.should.eql(['RAND()']); 98 | 99 | qb.reset_query(); 100 | qb.order_by('planet_position, planet_size', 'rand'); 101 | qb.order_by_array.should.eql(['RAND()']); 102 | 103 | qb.reset_query(); 104 | qb.order_by(null, 'RAND()'); 105 | qb.order_by_array.should.eql(['RAND()']); 106 | 107 | qb.reset_query(); 108 | qb.order_by('', 'rand'); 109 | qb.order_by_array.should.eql(['RAND()']); 110 | }); 111 | it('should accept an array of field + direction pairs', () => { 112 | qb.reset_query(); 113 | qb.order_by(['planet_position DESC', 'planet_size ASC']); 114 | qb.order_by_array.should.eql(['`planet_position` DESC', '`planet_size` ASC']); 115 | }); 116 | it('should use direction parameter as default when an array of field + direction pairs is provided (when a pair does not contain a direction)', () => { 117 | qb.reset_query(); 118 | qb.order_by(['planet_position', 'planet_size'], 'desc'); 119 | qb.order_by_array.should.eql(['`planet_position` DESC', '`planet_size` DESC']); 120 | 121 | qb.reset_query(); 122 | qb.order_by(['planet_position DESC', 'planet_size'], 'desc'); 123 | qb.order_by_array.should.eql(['`planet_position` DESC', '`planet_size` DESC']); 124 | }); 125 | it('should accept a simple array of fields as first param and default to ASC for the direction if none is provided', () => { 126 | qb.reset_query(); 127 | qb.order_by(['planet_position', 'planet_size']); 128 | qb.order_by_array.should.eql(['`planet_position` ASC', '`planet_size` ASC']); 129 | 130 | }); 131 | }); 132 | -------------------------------------------------------------------------------- /test/mysql/01-tests-set.js: -------------------------------------------------------------------------------- 1 | const should = require('chai').should(); 2 | const expect = require('chai').expect; 3 | const QueryBuilder = require('../../drivers/mysql/query_builder.js'); 4 | const qb = new QueryBuilder(); 5 | 6 | describe('MySQL: set()', () => { 7 | it('should exist', () => { 8 | should.exist(qb.set); 9 | }); 10 | it('should be a function', () => { 11 | qb.set.should.be.a('function'); 12 | }); 13 | it('should have an object to put fields into', () => { 14 | qb.should.have.property('set_array'); 15 | }); 16 | it('should have an empty array to put fields into at the beginning', () => { 17 | qb.set_array.should.be.empty; 18 | }); 19 | it('should not accept anything but a non-empty string or a non-empty object as first param', () => { 20 | qb.reset_query(); 21 | expect(() => qb.set(), 'nothing provided').to.throw(Error); 22 | expect(() => qb.set(null), 'null provided').to.throw(Error); 23 | expect(() => qb.set(false), 'false provided').to.throw(Error); 24 | expect(() => qb.set(true), 'true provided').to.throw(Error); 25 | expect(() => qb.set({}), 'empty object provided').to.throw(Error); 26 | expect(() => qb.set(3), 'integer provided').to.throw(Error); 27 | expect(() => qb.set(3.5), 'float provided').to.throw(Error); 28 | expect(() => qb.set(NaN), 'NaN provided').to.throw(Error); 29 | expect(() => qb.set(Infinity), 'Infinity provided').to.throw(Error); 30 | expect(() => qb.set([]), 'empty array provided').to.throw(Error); 31 | expect(() => qb.set([1,2]), 'array of numbers provided').to.throw(Error); 32 | expect(() => qb.set(''), 'empty string provided').to.throw(Error); 33 | expect(() => qb.set(' '), 'string full of spaces provided').to.throw(Error); 34 | expect(() => qb.set(/foobar/), 'regex provided').to.throw(Error); 35 | 36 | expect(() => qb.set('planet_position',3), 'valid string provided').to.not.throw(Error); 37 | expect(() => qb.set({planet_position: 3}), 'valid object provided').to.not.throw(Error); 38 | }); 39 | it('should not accept anything but a string, number, date, null, or boolean as second param if first param is a string.', () => { 40 | qb.reset_query(); 41 | expect(() => qb.set('planet_position'), 'nothing provided').to.throw(Error); 42 | expect(() => qb.set('planet_position',{}), 'empty object provided').to.throw(Error); 43 | expect(() => qb.set('planet_position',NaN), 'NaN provided').to.throw(Error); 44 | expect(() => qb.set('planet_position',Infinity), 'Infinity provided').to.throw(Error); 45 | expect(() => qb.set('planet_position',[]), 'empty array provided').to.throw(Error); 46 | expect(() => qb.set('planet_position',[1,2]), 'array of numbers provided').to.throw(Error); 47 | expect(() => qb.set('planet_position',/foobar/), 'regex provided').to.throw(Error); 48 | 49 | expect(() => qb.set('planet_position',new Date()), 'date provided').to.not.throw(Error); 50 | expect(() => qb.set('planet_position',null), 'null provided').to.not.throw(Error); 51 | expect(() => qb.set('planet_position',3), 'Integer provided').to.not.throw(Error); 52 | expect(() => qb.set('planet_position',3.5), 'float provided').to.not.throw(Error); 53 | expect(() => qb.set('planet_position',false), 'false provided').to.not.throw(Error); 54 | expect(() => qb.set('planet_position',true), 'true provided').to.not.throw(Error); 55 | expect(() => qb.set('planet_position',''), 'empty string provided').to.not.throw(Error); 56 | expect(() => qb.set('planet_position',' '), 'string full of spaces provided').to.not.throw(Error); 57 | expect(() => qb.set('planet_position','Three'), 'non-empty string provided').to.not.throw(Error); 58 | }); 59 | it('should add first param (key) and second param (value) to hash and escape them properly', () => { 60 | qb.reset_query(); 61 | qb.set('galaxy_name','Milky Way'); 62 | qb.set_array.should.eql([{"`galaxy_name`": "'Milky Way'"}]); 63 | }); 64 | it('should merge passed object into set_array and escape items properly', () => { 65 | qb.reset_query(); 66 | qb.set({galaxy_name: 'Milky Way'}); 67 | qb.set_array.should.eql([{"`galaxy_name`": "'Milky Way'"}]); 68 | 69 | qb.reset_query(); 70 | qb.set({galaxy_name: 'Milky Way', galaxy_class: 'C'}); 71 | qb.set_array.should.eql([{"`galaxy_name`": "'Milky Way'"}, {"`galaxy_class`": "'C'"}]); 72 | }); 73 | it('should not escape items if asked not to', () => { 74 | qb.reset_query(); 75 | qb.set({galaxy_name: 'Milky Way'}, null, false); 76 | qb.set_array.should.eql([{galaxy_name: 'Milky Way'}]); 77 | }); 78 | it('should append more items to set_array as set() is called', () => { 79 | qb.reset_query(); 80 | qb.set({galaxy_name: 'Milky Way'}, null, false); 81 | qb.set({galaxy_class: 'C'}, null, false); 82 | qb.set('galaxy_size','D'); 83 | qb.set_array.should.eql([{galaxy_name: 'Milky Way'}, {galaxy_class: 'C'}, {"`galaxy_size`": "'D'"}]); 84 | }); 85 | it('should be chainable', () => { 86 | qb.reset_query(); 87 | qb.set({galaxy_name: 'Milky Way', galaxy_class: 'C'}, null, false).set('galaxy_size','D'); 88 | qb.set_array.should.eql([{galaxy_name: 'Milky Way'}, {galaxy_class: 'C'}, {"`galaxy_size`": "'D'"}]); 89 | }); 90 | it('should overwrite values of keys that have been set already', () => { 91 | qb.reset_query(); 92 | qb.set({galaxy_name: 'Milky Way'}, null, false); 93 | qb.set({galaxy_class: 'C'}); 94 | qb.set('galaxy_class','D'); 95 | qb.set_array.should.eql([{galaxy_name: 'Milky Way'}, {"`galaxy_class`": "'D'"}]); 96 | }); 97 | it('should NOT overwrite values of keys that are the same but have different escape flags', () => { 98 | qb.reset_query(); 99 | qb.set({galaxy_name: 'Milky Way'}, null, false); 100 | qb.set({galaxy_class: 'C'}); 101 | qb.set('galaxy_class','D', false); 102 | qb.set_array.should.eql([{galaxy_name: 'Milky Way'}, {"`galaxy_class`": "'C'"}, {galaxy_class: 'D'}]); 103 | }); 104 | }); 105 | -------------------------------------------------------------------------------- /test/mysql/02-tests-compilation_methods.js: -------------------------------------------------------------------------------- 1 | const should = require('chai').should(); 2 | const expect = require('chai').expect; 3 | const QueryBuilder = require('../../drivers/mysql/query_builder.js'); 4 | const qb = new QueryBuilder(); 5 | 6 | describe('MySQL: get_compiled_select()', () => { 7 | it('should exist', () => { 8 | should.exist(qb.get_compiled_select); 9 | }); 10 | it('should be a function', () => { 11 | qb.get_compiled_select.should.be.a('function'); 12 | }); 13 | it('should add a table to from_array when a table is supplied', () => { 14 | qb.reset_query(); 15 | qb.get_compiled_select('galaxies'); 16 | qb.from_array.should.eql(['`galaxies`']); 17 | }); 18 | it('should add a set of tables to from_array when an array of tables is supplied', () => { 19 | qb.reset_query(); 20 | qb.get_compiled_select(['galaxies','star_systems','planets']); 21 | qb.from_array.should.eql(['`galaxies`','`star_systems`','`planets`']); 22 | }); 23 | it('should return a SQL string', () => { 24 | qb.reset_query(); 25 | const sql = qb.get_compiled_select('galaxies'); 26 | sql.should.eql('SELECT * FROM `galaxies`'); 27 | }); 28 | }); 29 | 30 | describe('MySQL: get_compiled_insert()', () => { 31 | it('should exist', () => { 32 | should.exist(qb.get_compiled_insert); 33 | }); 34 | it('should be a function', () => { 35 | qb.get_compiled_insert.should.be.a('function'); 36 | }); 37 | it('should return a SQL string', () => { 38 | qb.reset_query(); 39 | const sql = qb.set({foo:'bar'}).get_compiled_insert('galaxies'); 40 | sql.should.eql("INSERT INTO `galaxies` (`foo`) VALUES ('bar')"); 41 | }); 42 | }); 43 | 44 | describe('MySQL: get_compiled_update()', () => { 45 | it('should exist', () => { 46 | should.exist(qb.get_compiled_update); 47 | }); 48 | it('should be a function', () => { 49 | qb.get_compiled_update.should.be.a('function'); 50 | }); 51 | it('should return a SQL string', () => { 52 | qb.reset_query(); 53 | const sql = qb.set({foo:'bar'}).where('id',45).get_compiled_update('galaxies'); 54 | sql.should.eql("UPDATE (`galaxies`) SET `foo` = 'bar' WHERE `id` = 45"); 55 | }); 56 | }); 57 | 58 | describe('MySQL: get_compiled_delete()', () => { 59 | it('should exist', () => { 60 | should.exist(qb.get_compiled_delete); 61 | }); 62 | it('should be a function', () => { 63 | qb.get_compiled_delete.should.be.a('function'); 64 | }); 65 | it('should return a SQL string', () => { 66 | qb.reset_query(); 67 | const sql = qb.where('id',45).get_compiled_delete('galaxies'); 68 | sql.should.eql("DELETE FROM `galaxies` WHERE `id` = 45"); 69 | }); 70 | }); 71 | -------------------------------------------------------------------------------- /test/mysql/03-tests-count.js: -------------------------------------------------------------------------------- 1 | const should = require('chai').should(); 2 | const expect = require('chai').expect; 3 | const QueryBuilder = require('../../drivers/mysql/query_builder.js'); 4 | const qb = new QueryBuilder(); 5 | 6 | describe('MySQL: count()', () => { 7 | it('should exist', () => { 8 | should.exist(qb.count); 9 | }); 10 | it('should be a function', () => { 11 | qb.count.should.be.a('function'); 12 | }); 13 | it('should require that an item already exists in the from_array if one is not provided as the first parameter', () => { 14 | qb.reset_query(); 15 | expect(() => qb.count(), 'no tables supplied in chain').to.throw(Error); 16 | expect(() => qb.from('galaxies').count(), 'table supplied by from()').to.not.throw(Error); 17 | expect(() => qb.count('galaxies'), 'table supplied as first parameter').to.not.throw(Error); 18 | }); 19 | it('should add a table to from_array when a table is supplied', () => { 20 | qb.reset_query(); 21 | const sql = qb.count('galaxies'); 22 | qb.from_array.should.eql(['`galaxies`']); 23 | }); 24 | it('should return a string', () => { 25 | qb.reset_query(); 26 | const sql = qb.count('galaxies'); 27 | expect(sql).to.be.a('string'); 28 | expect(sql).to.exist; 29 | expect(sql).to.not.eql(''); 30 | }); 31 | it('should create a properly-escaped SELECT query', () => { 32 | qb.reset_query(); 33 | const sql = qb.count('galaxies'); 34 | sql.should.eql("SELECT COUNT(*) AS `numrows` FROM `galaxies`"); 35 | }); 36 | it('should include WHERE statements', () => { 37 | qb.reset_query(); 38 | const sql = qb.where({type:'spiral'}).count('galaxies'); 39 | sql.should.eql("SELECT COUNT(*) AS `numrows` FROM `galaxies` WHERE `type` = 'spiral'"); 40 | }); 41 | it('should work when table/view/procedure is provided earlier in chain but not in count() method', () => { 42 | qb.reset_query(); 43 | const sql = qb.from('galaxies').count(); 44 | sql.should.eql("SELECT COUNT(*) AS `numrows` FROM `galaxies`"); 45 | }); 46 | it('should work with multiple tables/views/stored procedures', () => { 47 | qb.reset_query(); 48 | const sql = qb.from(['planets','galaxies']).count(); 49 | sql.should.eql("SELECT COUNT(*) AS `numrows` FROM `planets`, `galaxies`"); 50 | }); 51 | it('should include any joins that were added in the chain', () => { 52 | qb.reset_query(); 53 | const sql = qb.join('galaxies g','g.id=s.galaxy_id','left').count('star_systems s'); 54 | sql.should.eql("SELECT COUNT(*) AS `numrows` FROM `star_systems` `s` LEFT JOIN `galaxies` `g` ON `g`.`id` = `s`.`galaxy_id`"); 55 | }); 56 | }); 57 | -------------------------------------------------------------------------------- /test/mysql/03-tests-delete.js: -------------------------------------------------------------------------------- 1 | const should = require('chai').should(); 2 | const expect = require('chai').expect; 3 | const QueryBuilder = require('../../drivers/mysql/query_builder.js'); 4 | const qb = new QueryBuilder(); 5 | 6 | describe('MySQL: delete()', () => { 7 | it('should exist', () => { 8 | should.exist(qb.delete); 9 | }); 10 | it('should be a function', () => { 11 | qb.delete.should.be.a('function'); 12 | }); 13 | it('should add a table to from_array when a table is supplied', () => { 14 | qb.reset_query(); 15 | qb.delete('galaxies'); 16 | qb.from_array.should.eql(['`galaxies`']); 17 | }); 18 | it('should only accept nothing or a non-empty-string for the table (first) parameter', () => { 19 | qb.reset_query(); 20 | 21 | expect(() => qb.delete([]), 'empty array provided').to.throw(Error); 22 | expect(() => qb.delete({}), 'empty object provided').to.throw(Error); 23 | expect(() => qb.delete(3), 'integer provided').to.throw(Error); 24 | expect(() => qb.delete(3.5), 'float provided').to.throw(Error); 25 | expect(() => qb.delete(true), 'true provided').to.throw(Error); 26 | expect(() => qb.delete(Infinity), 'Infinity provided').to.throw(Error); 27 | expect(() => qb.delete([1,2]), 'array of numbers provided').to.throw(Error); 28 | expect(() => qb.delete(/foobar/), 'regex provided').to.throw(Error); 29 | expect(() => qb.delete(NaN), 'NaN provided').to.throw(Error); 30 | expect(() => qb.delete(false), 'false provided').to.throw(Error); 31 | expect(() => qb.delete(''), 'empty string provided').to.throw(Error); 32 | expect(() => qb.delete(' '), 'string full of spaces provided').to.throw(Error); 33 | expect(() => qb.delete(null), 'null provided').to.throw(Error); 34 | 35 | // An undefined/nothing option will only work if a table has already been provided 36 | qb.from('galaxies'); expect(() => qb.delete(undefined),'undefined provided').to.not.throw(Error); 37 | qb.from('galaxies'); expect(() => qb.delete(),'nothing provided').to.not.throw(Error); 38 | }); 39 | it('should only use the first table supplied in a list if an array of table is supplied with the from() method.', () => { 40 | qb.reset_query(); 41 | const sql = qb.from(['galaxies','star_systems','planets']).delete(); 42 | sql.should.eql("DELETE FROM `galaxies`"); 43 | }); 44 | it('should add where conditions to where_array when conditions are supplied', () => { 45 | qb.reset_query(); 46 | qb.delete('planets', {continents: 7, star_system: 'Solar'}); 47 | qb.where_array.should.eql(["`continents` = 7", "AND `star_system` = 'Solar'"]); 48 | }); 49 | it('should return a string', () => { 50 | qb.reset_query(); 51 | const sql = qb.delete('galaxies', {continents: 7, star_system: 'Solar'}); 52 | expect(sql).to.be.a('string'); 53 | expect(sql).to.exist; 54 | expect(sql).to.not.eql(''); 55 | }); 56 | it('should build a properly-escaped delete statement that deletes all records in a table if only a table is given', () => { 57 | qb.reset_query(); 58 | const sql = qb.delete('galaxies'); 59 | sql.should.eql('DELETE FROM `galaxies`'); 60 | }); 61 | it('should build a properly-escaped delete statement that deletes all records in a table that matched passed conditions', () => { 62 | qb.reset_query(); 63 | const sql = qb.delete('galaxies', {class: 'M'}); 64 | sql.should.eql("DELETE FROM `galaxies` WHERE `class` = 'M'"); 65 | }); 66 | it('should use ONLY the FIRST table added previously via the from() method', () => { 67 | qb.reset_query(); 68 | qb.from('galaxies'); 69 | let sql = qb.delete(); 70 | sql.should.eql('DELETE FROM `galaxies`'); 71 | 72 | qb.reset_query(); 73 | sql = qb.from(['galaxies','star_systems','planets']).delete(); 74 | sql.should.eql('DELETE FROM `galaxies`'); 75 | }); 76 | it('should accept where conditions added previously via the where() method', () => { 77 | qb.reset_query(); 78 | const sql = qb.where('created >=',4.6E9).where({class: 'M'}).delete('galaxies'); 79 | sql.should.eql("DELETE FROM `galaxies` WHERE `created` >= 4600000000 AND `class` = 'M'"); 80 | }); 81 | it('should accept a limit on the number of rows deleted', () => { 82 | qb.reset_query(); 83 | const sql = qb.limit(20).delete('galaxies'); 84 | sql.should.eql("DELETE FROM `galaxies` LIMIT 20"); 85 | }); 86 | it('should accept a LIMIT on the number of rows to delete and an OFFSET at which to start deleting the rows', () => { 87 | qb.reset_query(); 88 | const sql = qb.limit(20,10).delete('galaxies'); 89 | sql.should.eql("DELETE FROM `galaxies` LIMIT 10, 20"); 90 | }); 91 | }); 92 | -------------------------------------------------------------------------------- /test/mysql/03-tests-empty_table.js: -------------------------------------------------------------------------------- 1 | const should = require('chai').should(); 2 | const expect = require('chai').expect; 3 | const QueryBuilder = require('../../drivers/mysql/query_builder.js'); 4 | const qb = new QueryBuilder(); 5 | 6 | describe('MySQL: empty_table()', () => { 7 | it('should exist', () => { 8 | should.exist(qb.empty_table); 9 | }); 10 | it('should be a function', () => { 11 | qb.empty_table.should.be.a('function'); 12 | }); 13 | it('should return a string', () => { 14 | qb.reset_query(); 15 | const sql = qb.empty_table('galaxies'); 16 | expect(sql).to.be.a('string'); 17 | expect(sql).to.exist; 18 | expect(sql).to.not.eql(''); 19 | }); 20 | it('should build a proper DELETE statement', () => { 21 | qb.reset_query(); 22 | const sql = qb.empty_table('galaxies'); 23 | sql.should.eql('DELETE FROM `galaxies`'); 24 | }); 25 | it('should only accept nothing or a non-empty-string for the table (first) parameter', () => { 26 | qb.reset_query(); 27 | 28 | expect(() => qb.empty_table([]), 'empty array provided').to.throw(Error); 29 | expect(() => qb.empty_table({}), 'empty object provided').to.throw(Error); 30 | expect(() => qb.empty_table(3), 'integer provided').to.throw(Error); 31 | expect(() => qb.empty_table(3.5), 'float provided').to.throw(Error); 32 | expect(() => qb.empty_table(true), 'true provided').to.throw(Error); 33 | expect(() => qb.empty_table(Infinity), 'Infinity provided').to.throw(Error); 34 | expect(() => qb.empty_table([1,2]), 'array of numbers provided').to.throw(Error); 35 | expect(() => qb.empty_table(/foobar/), 'regex provided').to.throw(Error); 36 | expect(() => qb.empty_table(NaN), 'NaN provided').to.throw(Error); 37 | expect(() => qb.empty_table(false), 'false provided').to.throw(Error); 38 | expect(() => qb.empty_table(''), 'empty string provided').to.throw(Error); 39 | expect(() => qb.empty_table(' '), 'string full of spaces provided').to.throw(Error); 40 | expect(() => qb.empty_table(null), 'null provided').to.throw(Error); 41 | 42 | // An undefined/nothing option will only work if a table has already been provided 43 | qb.from('galaxies'); expect(() => qb.empty_table(undefined),'undefined provided').to.not.throw(Error); 44 | qb.from('galaxies'); expect(() => qb.empty_table(),'nothing provided').to.not.throw(Error); 45 | }); 46 | it('should only use the first table supplied in a list if an array of table is supplied with the from() method.', () => { 47 | qb.reset_query(); 48 | const sql = qb.from(['galaxies','star_systems','planets']).empty_table(); 49 | sql.should.eql("DELETE FROM `galaxies`"); 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /test/mysql/03-tests-insert_batch.js: -------------------------------------------------------------------------------- 1 | const should = require('chai').should(); 2 | const expect = require('chai').expect; 3 | const QueryBuilder = require('../../drivers/mysql/query_builder.js'); 4 | const qb = new QueryBuilder(); 5 | 6 | const test_where = {id:3}; 7 | const test_data = [{id:3, name:'Milky Way', type: 'spiral'}, {id:4, name: 'Andromeda', type: 'spiral'}]; 8 | 9 | describe('MySQL: insert_batch()', () => { 10 | it('should exist', () => { 11 | should.exist(qb.insert_batch); 12 | }); 13 | it('should be a function', () => { 14 | qb.insert_batch.should.be.a('function'); 15 | }); 16 | it('should add a table to from_array when a table is supplied', () => { 17 | qb.reset_query(); 18 | qb.insert_batch('galaxies', test_data); 19 | qb.from_array.should.eql(['`galaxies`']); 20 | }); 21 | it('should only accept nothing or a string for the table (first) parameter', () => { 22 | qb.reset_query(); 23 | 24 | // Doing these to prevent other errors 25 | qb.from('galaxies'); 26 | 27 | expect(() => qb.insert_batch([], test_data), 'empty array provided').to.throw(Error); 28 | expect(() => qb.insert_batch({}, test_data), 'empty object provided').to.throw(Error); 29 | expect(() => qb.insert_batch(3, test_data), 'integer provided').to.throw(Error); 30 | expect(() => qb.insert_batch(3.5, test_data), 'float provided').to.throw(Error); 31 | expect(() => qb.insert_batch(true, test_data), 'true provided').to.throw(Error); 32 | expect(() => qb.insert_batch(Infinity, test_data), 'Infinity provided').to.throw(Error); 33 | expect(() => qb.insert_batch([1,2], test_data), 'array of numbers provided').to.throw(Error); 34 | expect(() => qb.insert_batch(/foobar/, test_data), 'regex provided').to.throw(Error); 35 | 36 | expect(() => qb.insert_batch(NaN, test_data), 'NaN provided').to.not.throw(Error); 37 | expect(() => qb.insert_batch(false, test_data), 'false provided').to.not.throw(Error); 38 | expect(() => qb.insert_batch('', test_data), 'empty string provided').to.not.throw(Error); 39 | expect(() => qb.insert_batch(' ', test_data), 'string full of spaces provided').to.not.throw(Error); 40 | expect(() => qb.insert_batch(null, test_data), 'null provided').to.not.throw(Error); 41 | expect(() => qb.insert_batch(undefined, test_data), 'undefined provided').to.not.throw(Error); 42 | }); 43 | it('should build a proper batch INSERT string', () => { 44 | qb.reset_query(); 45 | const sql = qb.insert_batch('galaxies', test_data); 46 | sql.should.eql("INSERT INTO `galaxies` (`id`, `name`, `type`) VALUES (3, 'Milky Way', 'spiral'), (4, 'Andromeda', 'spiral')"); 47 | }); 48 | it('should only accept an array as the second parameter', () => { 49 | qb.reset_query(); 50 | 51 | expect(() => qb.insert_batch('galaxies',test_data), 'array of objects provided').to.not.throw(Error); 52 | expect(() => qb.insert_batch('galaxies',[]), 'empty array provided').to.not.throw(Error); 53 | 54 | expect(() => qb.insert_batch('galaxies',[{},{}]), 'array of empty objects provided').to.throw(Error); 55 | expect(() => qb.insert_batch('galaxies',[test_data,test_data]), 'array of arrays provided').to.throw(Error); 56 | expect(() => qb.insert_batch('galaxies',{}), 'empty object provided').to.throw(Error); 57 | expect(() => qb.insert_batch('galaxies',''), 'empty string provided').to.throw(Error); 58 | expect(() => qb.insert_batch('galaxies',null), 'null provided').to.throw(Error); 59 | expect(() => qb.insert_batch('galaxies',undefined), 'undefined provided').to.throw(Error); 60 | expect(() => qb.insert_batch('galaxies'), 'nothing provided').to.throw(Error); 61 | expect(() => qb.insert_batch('galaxies',3), 'integer provided').to.throw(Error); 62 | expect(() => qb.insert_batch('galaxies',3.5), 'float provided').to.throw(Error); 63 | expect(() => qb.insert_batch('galaxies',true), 'true provided').to.throw(Error); 64 | expect(() => qb.insert_batch('galaxies',Infinity), 'Infinity provided').to.throw(Error); 65 | expect(() => qb.insert_batch('galaxies',[1,2]), 'array of numbers provided').to.throw(Error); 66 | expect(() => qb.insert_batch('galaxies',[Date, /foobar/, null]), 'array of non-standard objects provided').to.throw(Error); 67 | expect(() => qb.insert_batch('galaxies',['abc',2,{foo:'bar'}]), 'array of mixed values provided').to.throw(Error); 68 | expect(() => qb.insert_batch('galaxies',/foobar/), 'regex provided').to.throw(Error); 69 | expect(() => qb.insert_batch('galaxies',NaN), 'NaN provided').to.throw(Error); 70 | expect(() => qb.insert_batch('galaxies',false), 'false provided').to.throw(Error); 71 | expect(() => qb.insert_batch('galaxies',' '), 'string full of spaces provided').to.throw(Error); 72 | }); 73 | it('should allow for an empty data parameter', () => { 74 | qb.reset_query(); 75 | const sql = qb.insert_batch('galaxies',[]); 76 | sql.should.eql("INSERT INTO `galaxies` () VALUES ()"); 77 | }); 78 | it('should utilize pre-existing tables set in from_array', () => { 79 | qb.reset_query(); 80 | qb.from('galaxies'); 81 | const sql = qb.insert_batch(null,[]); 82 | sql.should.eql("INSERT INTO `galaxies` () VALUES ()"); 83 | }); 84 | it('should fail if any invalid values are passed into one of the data objects in the dataset', () => { 85 | qb.reset_query(); 86 | const func = () => console.log("foo"); 87 | const regex = /foobar/; 88 | const arr = [1,2,3]; 89 | const obj = {foo: 'bar'}; 90 | 91 | expect(() => qb.insert_batch('galaxies',[{id: func}]), 'function in data').to.throw(Error); 92 | expect(() => qb.insert_batch('galaxies',[{id: regex}]), 'regex in data').to.throw(Error); 93 | expect(() => qb.insert_batch('galaxies',[{id: Infinity}]), 'Infinity in data').to.throw(Error); 94 | expect(() => qb.insert_batch('galaxies',[{id: undefined}]), 'undefined in data').to.throw(Error); 95 | expect(() => qb.insert_batch('galaxies',[{id: NaN}]), 'NaN in data').to.throw(Error); 96 | expect(() => qb.insert_batch('galaxies',[{id: arr}]), 'array in data').to.throw(Error); 97 | expect(() => qb.insert_batch('galaxies',[{id: obj}]), 'object in data').to.throw(Error); 98 | }); 99 | it('should support insert ignore statements', () => { 100 | qb.reset_query(); 101 | const sql = qb.insert_batch('galaxies', test_data, true, 'ON DUPLICATE KEY UPDATE last_update = NOW()'); 102 | sql.should.eql("INSERT IGNORE INTO `galaxies` (`id`, `name`, `type`) VALUES (3, 'Milky Way', 'spiral'), (4, 'Andromeda', 'spiral') ON DUPLICATE KEY UPDATE last_update = NOW()"); 103 | }); 104 | }); 105 | -------------------------------------------------------------------------------- /test/mysql/03-tests-truncate.js: -------------------------------------------------------------------------------- 1 | const should = require('chai').should(); 2 | const expect = require('chai').expect; 3 | const QueryBuilder = require('../../drivers/mysql/query_builder.js'); 4 | const qb = new QueryBuilder(); 5 | 6 | describe('MySQL: truncate()', () => { 7 | it('should exist', () => { 8 | should.exist(qb.truncate); 9 | }); 10 | it('should be a function', () => { 11 | qb.truncate.should.be.a('function'); 12 | }); 13 | it('should return a string', () => { 14 | qb.reset_query(); 15 | const sql = qb.truncate('galaxies'); 16 | expect(sql).to.be.a('string'); 17 | expect(sql).to.exist; 18 | expect(sql).to.not.eql(''); 19 | }); 20 | it('should build a proper truncate statement', () => { 21 | qb.reset_query(); 22 | const sql = qb.truncate('galaxies'); 23 | sql.should.eql('TRUNCATE `galaxies`'); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /test/mysql/03-tests-update.js: -------------------------------------------------------------------------------- 1 | const should = require('chai').should(); 2 | const expect = require('chai').expect; 3 | const QueryBuilder = require('../../drivers/mysql/query_builder.js'); 4 | const qb = new QueryBuilder(); 5 | 6 | const test_where = {id:3}; 7 | const test_data = {name:'Milky Way', type: 'spiral'}; 8 | const test_data_set = [{id:3, name:'Milky Way', type: 'spiral'}, {id:4, name: 'Andromeda', type: 'spiral'}]; 9 | 10 | // table, data, callback, ignore, suffix 11 | 12 | describe('MySQL: update()', () => { 13 | it('should exist', () => { 14 | should.exist(qb.update); 15 | }); 16 | it('should be a function', () => { 17 | qb.update.should.be.a('function'); 18 | }); 19 | it('should add a table to from_array when a table is supplied', () => { 20 | qb.reset_query(); 21 | qb.update('galaxies', test_data, test_where); 22 | qb.from_array.should.eql(['`galaxies`']); 23 | }); 24 | it('should accept a string or falsy value for the table (first) parameter', () => { 25 | qb.reset_query(); 26 | 27 | // One could choose to pass a falsy value to the first param because they have or will 28 | // supply it with the from() method instead. 29 | 30 | qb.reset_query(); expect(() => qb.from('galaxies').update([], test_data), 'empty array provided').to.throw(Error); 31 | qb.reset_query(); expect(() => qb.from('galaxies').update({}, test_data), 'empty object provided').to.throw(Error); 32 | qb.reset_query(); expect(() => qb.from('galaxies').update(3, test_data), 'integer provided').to.throw(Error); 33 | qb.reset_query(); expect(() => qb.from('galaxies').update(3.5, test_data), 'float provided').to.throw(Error); 34 | qb.reset_query(); expect(() => qb.from('galaxies').update(true, test_data), 'true provided').to.throw(Error); 35 | qb.reset_query(); expect(() => qb.from('galaxies').update(Infinity, test_data), 'Infinity provided').to.throw(Error); 36 | qb.reset_query(); expect(() => qb.from('galaxies').update([1,2], test_data), 'array of numbers provided').to.throw(Error); 37 | qb.reset_query(); expect(() => qb.from('galaxies').update(/foobar/, test_data), 'regex provided').to.throw(Error); 38 | 39 | qb.reset_query(); expect(() => qb.from('galaxies').update(NaN, test_data), 'NaN provided').to.not.throw(Error); 40 | qb.reset_query(); expect(() => qb.from('galaxies').update(false, test_data), 'false provided').to.not.throw(Error); 41 | qb.reset_query(); expect(() => qb.from('galaxies').update('', test_data), 'empty string provided').to.not.throw(Error); 42 | qb.reset_query(); expect(() => qb.from('galaxies').update(' ', test_data), 'string full of spaces provided').to.not.throw(Error); 43 | qb.reset_query(); expect(() => qb.from('galaxies').update(null, test_data), 'null provided').to.not.throw(Error); 44 | qb.reset_query(); expect(() => qb.from('galaxies').update(undefined, test_data),'undefined provided').to.not.throw(Error); 45 | }); 46 | it('should fail if a number, non-standard object, regex, boolean, array of non-objects, or non-empty string is provided in data parameter', () => { 47 | // One could choose to pass a falsy value to the second param because they have or will 48 | // supply data with the set() method instead. 49 | 50 | qb.reset_query(); expect(() => qb.update('galaxies',test_data), 'non-empty array provided').to.not.throw(Error); 51 | qb.reset_query(); expect(() => qb.update('galaxies',test_data_set), 'array of non-empty standard objects provided').to.not.throw(Error); 52 | 53 | qb.reset_query(); expect(() => qb.set({id:2}).update('galaxies',NaN), 'NaN provided').to.not.throw(Error); 54 | qb.reset_query(); expect(() => qb.set({id:2}).update('galaxies',false), 'false provided').to.not.throw(Error); 55 | qb.reset_query(); expect(() => qb.set({id:2}).update('galaxies',''), 'empty string provided').to.not.throw(Error); 56 | qb.reset_query(); expect(() => qb.set({id:2}).update('galaxies',null), 'null provided').to.not.throw(Error); 57 | qb.reset_query(); expect(() => qb.set({id:2}).update('galaxies',undefined), 'undefined provided').to.not.throw(Error); 58 | qb.reset_query(); expect(() => qb.set({id:2}).update('galaxies'), 'nothing provided').to.not.throw(Error); 59 | 60 | qb.reset_query(); expect(() => qb.set({id:2}).update('galaxies',3), 'integer provided').to.throw(Error); 61 | qb.reset_query(); expect(() => qb.set({id:2}).update('galaxies',3.5), 'float provided').to.throw(Error); 62 | qb.reset_query(); expect(() => qb.set({id:2}).update('galaxies',true), 'true provided').to.throw(Error); 63 | qb.reset_query(); expect(() => qb.set({id:2}).update('galaxies',Infinity), 'Infinity provided').to.throw(Error); 64 | qb.reset_query(); expect(() => qb.set({id:2}).update('foobar',{}), 'empty object provided').to.throw(Error); 65 | qb.reset_query(); expect(() => qb.set({id:2}).update('galaxies',[{},{}]), 'array of empty objects provided').to.throw(Error); 66 | qb.reset_query(); expect(() => qb.set({id:2}).update('galaxies',[]), 'empty array provided').to.throw(Error); 67 | qb.reset_query(); expect(() => qb.set({id:2}).update('galaxies',[1,2]), 'array of numbers provided').to.throw(Error); 68 | qb.reset_query(); expect(() => qb.set({id:2}).update('galaxies',['abc',2,{foo:'bar'}]), 'array of mixed values provided').to.throw(Error); 69 | qb.reset_query(); expect(() => qb.set({id:2}).update('galaxies',/foobar/), 'regex provided').to.throw(Error); 70 | qb.reset_query(); expect(() => qb.set({id:2}).update('galaxies',' '), 'string full of spaces provided').to.throw(Error); 71 | }); 72 | it('should require that there is at least something being updated', () => { 73 | // @todo 74 | }); 75 | it('should utilize pre-existing tables set in from_array', () => { 76 | qb.reset_query(); 77 | qb.from('galaxies'); 78 | const sql = qb.update(null, test_data, test_where); 79 | sql.should.eql("UPDATE (`galaxies`) SET `name` = 'Milky Way', `type` = 'spiral' WHERE `id` = 3"); 80 | }); 81 | it('should utilize pre-existing value set in in set_array', () => { 82 | qb.reset_query(); 83 | qb.set(test_data); 84 | const sql = qb.update('galaxies'); 85 | sql.should.eql("UPDATE (`galaxies`) SET `name` = 'Milky Way', `type` = 'spiral'"); 86 | }); 87 | it('should utilize pre-existing tables and values from from_aray and set_array, respectively', () => { 88 | qb.reset_query(); 89 | qb.from('galaxies').set(test_data); 90 | const sql = qb.update(); 91 | sql.should.eql("UPDATE (`galaxies`) SET `name` = 'Milky Way', `type` = 'spiral'"); 92 | }); 93 | it('should accept a non-empty object for the data parameter', () => { 94 | qb.reset_query(); 95 | const sql = qb.update('galaxies', test_data); 96 | sql.should.eql("UPDATE (`galaxies`) SET `name` = 'Milky Way', `type` = 'spiral'"); 97 | }); 98 | it('should convert call to update_batch() if an array of non-emtpy objects is passed in the data parameter', () => { 99 | qb.reset_query(); 100 | const sql = qb.update('galaxies', test_data_set); 101 | qb.reset_query(); 102 | const sql_b = qb.update_batch('galaxies', test_data_set, 'id'); 103 | sql.should.eql(sql_b); 104 | }); 105 | it('should fail if any invalid values are passed in the data object.', () => { 106 | qb.reset_query(); 107 | const func = () => console.log("foo"); 108 | const regex = /foobar/; 109 | const arr = [1,2,3]; 110 | const obj = {foo: 'bar'}; 111 | 112 | qb.reset_query(); expect(() => qb.update('galaxies',{id: func}), 'function in data').to.throw(Error); 113 | qb.reset_query(); expect(() => qb.update('galaxies',{id: regex}), 'regex in data').to.throw(Error); 114 | qb.reset_query(); expect(() => qb.update('galaxies',{id: Infinity}), 'Infinity in data').to.throw(Error); 115 | qb.reset_query(); expect(() => qb.update('galaxies',{id: undefined}), 'undefined in data').to.throw(Error); 116 | qb.reset_query(); expect(() => qb.update('galaxies',{id: NaN}), 'NaN in data').to.throw(Error); 117 | qb.reset_query(); expect(() => qb.update('galaxies',{id: arr}), 'array in data').to.throw(Error); 118 | qb.reset_query(); expect(() => qb.update('galaxies',{id: obj}), 'object in data').to.throw(Error); 119 | 120 | }); 121 | }); 122 | -------------------------------------------------------------------------------- /test/mysql/03-tests-update_batch.js: -------------------------------------------------------------------------------- 1 | const should = require('chai').should(); 2 | const expect = require('chai').expect; 3 | const QueryBuilder = require('../../drivers/mysql/query_builder.js'); 4 | const qb = new QueryBuilder(); 5 | 6 | const test_where = {quadrant: 'Alpha'}; 7 | const test_data = [{id:3, name:'Milky Way', type: 'spiral'}, {id:4, name: 'Andromeda', type: 'spiral'}]; 8 | 9 | describe('MySQL: update_batch()', () => { 10 | it('should exist', () => { 11 | should.exist(qb.update_batch); 12 | }); 13 | it('should be a function', () => { 14 | qb.update_batch.should.be.a('function'); 15 | }); 16 | it('should build a proper batch UPDATE string', () => { 17 | qb.reset_query(); 18 | const sql = qb.update_batch('galaxies', test_data, 'id'); 19 | sql.should.eql(["UPDATE (`galaxies`) SET `name` = CASE WHEN `id` = 3 THEN 'Milky Way' WHEN `id` = 4 THEN 'Andromeda' ELSE `name` END, `type` = CASE WHEN `id` = 3 THEN 'spiral' WHEN `id` = 4 THEN 'spiral' ELSE `type` END WHERE `id` IN (3,4)"]); 20 | }); 21 | it('should build a proper batch UPDATE string when where clause is provided', () => { 22 | qb.reset_query(); 23 | const sql = qb.update_batch('galaxies', test_data, 'id', test_where); 24 | sql.should.eql(["UPDATE (`galaxies`) SET `name` = CASE WHEN `id` = 3 THEN 'Milky Way' WHEN `id` = 4 THEN 'Andromeda' ELSE `name` END, `type` = CASE WHEN `id` = 3 THEN 'spiral' WHEN `id` = 4 THEN 'spiral' ELSE `type` END WHERE `quadrant` = 'Alpha' AND `id` IN (3,4)"]); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /test/mysql/04-tests-query-promise.js: -------------------------------------------------------------------------------- 1 | const should = require('chai').should(); 2 | const expect = require('chai').expect; 3 | const QueryBuilder = require('../../index.js'); 4 | const settings = require('../configs').mysql; 5 | const driver = 'mysql'; 6 | 7 | const check = (done, f) => { 8 | try { 9 | f(); 10 | done(); 11 | } catch(e) { 12 | done(e); 13 | } 14 | }; 15 | 16 | describe('MySQL: Query Promises', () => { 17 | it('should allow us to execute a simple SELECT query', async () => { 18 | const qb = new QueryBuilder(Object.assign({}, settings), driver); 19 | try { 20 | await qb.connect(); 21 | const res = await qb.query("select * from `cities` where `city` like 'Z%' and `state_code` = 'FL'"); 22 | 23 | expect(res, 'results should not be empty').to.not.be.empty; 24 | expect(res, 'should have 3 results').to.have.length(3); 25 | } catch (err) { 26 | throw err; 27 | } finally { 28 | 29 | } 30 | }); 31 | it('should have a non-empty array of objects as a response value when there should be results from a SELECT query', async () => { 32 | const qb = new QueryBuilder(Object.assign({}, settings), driver); 33 | try { 34 | await qb.connect(); 35 | const res = await qb.like('city', 'Z', 'right').get_where('cities', { state_code: 'FL' }); 36 | 37 | expect(res, 'results should not be empty').to.not.be.empty; 38 | expect(res, 'should have 3 results').to.have.length(3); 39 | } catch (err) { 40 | throw err; 41 | } finally { 42 | 43 | } 44 | }); 45 | it('should have a javascript Standard Error object when running an invalid query', async () => { 46 | const qb = new QueryBuilder(Object.assign({}, settings), driver); 47 | 48 | await qb.connect(); 49 | 50 | try { 51 | await qb.query("select * = 'FL'"); 52 | } catch (err) { 53 | expect(err, 'there should be an error when the query is invalid').to.be.instanceof(Error); 54 | } finally { 55 | 56 | } 57 | }); 58 | it('should respond with an object explaining the results of an INSERT query', async () => { 59 | const qb = new QueryBuilder(Object.assign({}, settings), driver); 60 | try { 61 | await qb.connect(); 62 | const res = await qb.insert('cities', { city: 'Node QueryBuilder', state_code: 'NQ' }); 63 | 64 | expect(res, 'results should be an object with all the expected keys').to.be.an('object').that.includes.all.keys('insert_id', 'affected_rows', 'changed_rows'); 65 | expect(res.insert_id, 'insert id should be null').to.be.null; 66 | expect(res.affected_rows, 'affected_rows should be 1').to.eql(1); 67 | expect(res.changed_rows, 'changed_rows should be 0').to.eql(0); 68 | } catch (err) { 69 | throw err; 70 | } finally { 71 | 72 | } 73 | }); 74 | it('should respond with an object explaining the results of an UPDATE query', async () => { 75 | const qb = new QueryBuilder(Object.assign({}, settings), driver); 76 | try { 77 | await qb.connect(); 78 | const res = await qb.update('cities', { city: 'Node Query Builder' }, { state_code: 'NQ' }); 79 | 80 | expect(res, 'results should be an object with all the expected keys').to.be.an('object').that.includes.all.keys('insert_id', 'affected_rows', 'changed_rows'); 81 | expect(res.insert_id, 'insert id should be null').to.be.null; 82 | expect(res.affected_rows, 'affected_rows should be 1').to.gte(1); 83 | expect(res.changed_rows, 'changed_rows should be 1').to.gte(1); 84 | } catch (err) { 85 | throw err; 86 | } finally { 87 | 88 | } 89 | }); 90 | it('should respond with an object explaining the results of a DELETE query', async () => { 91 | const qb = new QueryBuilder(Object.assign({}, settings), driver); 92 | try { 93 | await qb.connect(); 94 | const res = await qb.delete('cities', { state_code: 'NQ' }); 95 | 96 | expect(res, 'results should be an object with all the expected keys').to.be.an('object').that.includes.all.keys('insert_id', 'affected_rows', 'changed_rows'); 97 | expect(res.insert_id, 'insert id should be null').to.be.null; 98 | expect(res.affected_rows, 'affected_rows should be 1').to.gte(1); 99 | expect(res.changed_rows, 'changed_rows should be 0').to.eql(0); 100 | } catch (err) { 101 | throw err; 102 | } finally { 103 | 104 | } 105 | }); 106 | }); 107 | -------------------------------------------------------------------------------- /test/mysql/04-tests-query-response.js: -------------------------------------------------------------------------------- 1 | const should = require('chai').should(); 2 | const expect = require('chai').expect; 3 | const QueryBuilder = require('../../index.js'); 4 | const settings = require('../configs').mysql; 5 | const driver = 'mysql'; 6 | 7 | const check = (done, f) => { 8 | try { 9 | f(); 10 | done(); 11 | } catch(e) { 12 | done(e); 13 | } 14 | }; 15 | 16 | describe('MySQL: Query Responses', () => { 17 | it('should allow us to execute a simple SELECT query', done => { 18 | const qb = new QueryBuilder(Object.assign({}, settings), driver); 19 | qb.connect(err => { 20 | expect(err).to.not.be.instanceof(Error); 21 | 22 | qb.query("select * from `cities` where `city` like 'Z%' and `state_code` = 'FL'", (err, res) => { 23 | check(done, () => { 24 | expect(err, 'there should not be an error when querying').to.not.be.instanceof(Error); 25 | expect(res, 'results should not be empty').to.not.be.empty; 26 | expect(res, 'should have 3 results').to.have.length(3); 27 | }); 28 | }); 29 | }); 30 | }); 31 | it('should have a non-empty array of objects as a response value when there should be results from a SELECT query', done => { 32 | const qb = new QueryBuilder(Object.assign({}, settings), driver); 33 | qb.connect(err => { 34 | expect(err).to.not.be.instanceof(Error); 35 | 36 | qb.like('city', 'Z', 'right').get_where('cities', {state_code: 'FL'}, (err, res) => { 37 | check(done, () => { 38 | expect(err, 'there should not be an error when querying').to.not.be.instanceof(Error); 39 | expect(res, 'results should not be empty').to.not.be.empty; 40 | expect(res, 'should have 3 results').to.have.length(3); 41 | }); 42 | }); 43 | }); 44 | }); 45 | it('should have a javascript Standard Error object when running an invalid query', done => { 46 | const qb = new QueryBuilder(Object.assign({}, settings), driver); 47 | qb.connect(err => { 48 | expect(err).to.not.be.instanceof(Error); 49 | 50 | qb.query("select * = 'FL'", (err, res) => { 51 | check(done, () => { 52 | expect(err, 'there should be an error when the query is invalid').to.be.instanceof(Error); 53 | }); 54 | }); 55 | }); 56 | }); 57 | it('should respond with an object explaining the results of an INSERT query', done => { 58 | const qb = new QueryBuilder(Object.assign({}, settings), driver); 59 | qb.connect(err => { 60 | expect(err).to.not.be.instanceof(Error); 61 | 62 | qb.insert('cities', {city: 'Node QueryBuilder', state_code: 'NQ'}, (err, res) => { 63 | check(done, () => { 64 | expect(err, 'there should not be an error when querying').to.not.be.instanceof(Error); 65 | expect(res, 'results should be an object with all the expected keys').to.be.an('object').that.includes.all.keys('insert_id', 'affected_rows', 'changed_rows'); 66 | expect(res.insert_id, 'insert id should be null').to.be.null; 67 | expect(res.affected_rows, 'affected_rows should be 1').to.eql(1); 68 | expect(res.changed_rows, 'changed_rows should be 0').to.eql(0); 69 | }); 70 | }); 71 | }); 72 | }); 73 | it('should respond with an object explaining the results of an UPDATE query', done => { 74 | const qb = new QueryBuilder(Object.assign({}, settings), driver); 75 | qb.connect(err => { 76 | expect(err).to.not.be.instanceof(Error); 77 | 78 | qb.update('cities', {city: 'Node Query Builder'}, {state_code: 'NQ'}, (err, res) => { 79 | check(done, () => { 80 | expect(err, 'there should not be an error when querying').to.not.be.instanceof(Error); 81 | expect(res, 'results should be an object with all the expected keys').to.be.an('object').that.includes.all.keys('insert_id', 'affected_rows', 'changed_rows'); 82 | expect(res.insert_id, 'insert id should be null').to.be.null; 83 | expect(res.affected_rows, 'affected_rows should be 1').to.gte(1); 84 | expect(res.changed_rows, 'changed_rows should be 1').to.gte(1); 85 | }); 86 | }); 87 | }); 88 | }); 89 | it('should respond with an object explaining the results of a DELETE query', done => { 90 | const qb = new QueryBuilder(Object.assign({}, settings), driver); 91 | qb.connect(err => { 92 | expect(err).to.not.be.instanceof(Error); 93 | 94 | qb.delete('cities', {state_code: 'NQ'}, (err, res) => { 95 | check(done, () => { 96 | expect(err, 'there should not be an error when querying').to.not.be.instanceof(Error); 97 | expect(res, 'results should be an object with all the expected keys').to.be.an('object').that.includes.all.keys('insert_id', 'affected_rows', 'changed_rows'); 98 | expect(res.insert_id, 'insert id should be null').to.be.null; 99 | expect(res.affected_rows, 'affected_rows should be 1').to.gte(1); 100 | expect(res.changed_rows, 'changed_rows should be 0').to.eql(0); 101 | }); 102 | }); 103 | }); 104 | }); 105 | }); 106 | -------------------------------------------------------------------------------- /test/mysql/05-tests-multiple-pools.js: -------------------------------------------------------------------------------- 1 | const should = require('chai').should(); 2 | const expect = require('chai').expect; 3 | const settings = require('../configs').mysql; 4 | const QueryBuilder = require('../../index.js'); 5 | const pool1 = new QueryBuilder(settings, 'mysql', 'pool'); 6 | const pool2 = new QueryBuilder(Object.assign({}, settings, {database: 'mock_db2'}), 'mysql', 'pool'); 7 | let pool1_settings, pool2_settings; 8 | 9 | const compare_connections = (done) => { 10 | try { 11 | const db1 = pool1_settings.database; 12 | const db2 = pool2_settings.database; 13 | db1.should.not.be.eql(db2); 14 | done(); 15 | } catch(e) { 16 | done(e); 17 | } 18 | }; 19 | 20 | describe('MySQL: Multiple Pools', () => { 21 | it('should not get confused by what pool settings to use', done => { 22 | let connections_established = 0; 23 | 24 | pool1.get_connection(qb1 => { 25 | pool1_settings = qb1.connection_settings(); 26 | connections_established++; 27 | if (connections_established >= 2) compare_connections(done); 28 | }); 29 | pool2.get_connection(qb2 => { 30 | pool2_settings = qb2.connection_settings(); 31 | connections_established++; 32 | if (connections_established >= 2) compare_connections(done); 33 | }); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /test/mysql/05-tests-multiple-queries.js: -------------------------------------------------------------------------------- 1 | const should = require('chai').should(); 2 | const expect = require('chai').expect; 3 | const settings = require('../configs').mysql; 4 | const QueryBuilder = require('../../index.js'); 5 | const pool = new QueryBuilder(settings, 'mysql', 'pool'); 6 | 7 | describe('MySQL: Multiple Queries', () => { 8 | it('should not get confused about table after delete records', done => { 9 | pool.get_connection(qb => { 10 | qb.limit(1).delete('cities', (err, result) => { 11 | qb.select(['city', 'state_code']).from('cities').limit(1).get((err2, result2) => { 12 | qb.release(); 13 | expect(err, 'should not error on delete').to.not.be.instanceof(Error); 14 | expect(result.affectedRows, 'one record should be deleted').to.be.eql(1); 15 | expect(err2, 'should not error on select').to.not.be.instanceof(Error); 16 | expect(result2.length, 'should have one result').to.be.equal(1); 17 | done(); 18 | }); 19 | }); 20 | }); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /test/mysql/create_mysql_mock.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | mysql -uroot -e "CREATE DATABASE IF NOT EXISTS mock_db;" 3 | mysql -uroot -e "CREATE DATABASE IF NOT EXISTS mock_db2;" 4 | mysql -uroot -e "CREATE USER IF NOT EXISTS 'travis'@'localhost';" 5 | mysql -uroot -e "GRANT ALL ON mock_db.* TO 'travis'@'localhost';" 6 | mysql -uroot -e "GRANT ALL ON mock_db2.* TO 'travis'@'localhost';" 7 | mysql -uroot mock_db -e "DROP TABLE IF EXISTS cities;" 8 | mysql -uroot mock_db2 -e "DROP TABLE IF EXISTS cities2;" 9 | mysql -utravis mock_db < ./test/mysql/mock_data.sql 10 | mysql -utravis mock_db2 < ./test/mysql/mock_data2.sql 11 | 12 | echo "Done with MySQL Import" -------------------------------------------------------------------------------- /test/mysql/mock_data2.sql: -------------------------------------------------------------------------------- 1 | -- MySQL dump 10.14 Distrib 5.5.40-MariaDB, for Linux (x86_64) 2 | -- 3 | -- Host: localhost Database: mock_db2 4 | -- ------------------------------------------------------ 5 | -- Server version 5.5.40-MariaDB 6 | 7 | /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; 8 | /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; 9 | /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; 10 | /*!40101 SET NAMES utf8 */; 11 | /*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; 12 | /*!40103 SET TIME_ZONE='+00:00' */; 13 | /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; 14 | /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; 15 | /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; 16 | /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; 17 | 18 | -- 19 | -- Table structure for table `cities` 20 | -- 21 | 22 | DROP TABLE IF EXISTS `cities2`; 23 | /*!40101 SET @saved_cs_client = @@character_set_client */; 24 | /*!40101 SET character_set_client = utf8 */; 25 | CREATE TABLE `cities2` ( 26 | `city` varchar(50) NOT NULL, 27 | `state_code` char(2) NOT NULL, 28 | KEY `idx_state_code` (`state_code`) 29 | ) ENGINE=InnoDB DEFAULT CHARSET=latin1; 30 | /*!40101 SET character_set_client = @saved_cs_client */; 31 | 32 | -- 33 | -- Dumping data for table `cities2` 34 | -- 35 | 36 | LOCK TABLES `cities2` WRITE; 37 | /*!40000 ALTER TABLE `cities2` DISABLE KEYS */; 38 | INSERT INTO `cities2` VALUES ('Aaronsburg','PA'),('Abbeville','AL'),('Abbeville','GA'); 39 | /*!40000 ALTER TABLE `cities2` ENABLE KEYS */; 40 | UNLOCK TABLES; 41 | /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; 42 | 43 | /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; 44 | /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; 45 | /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; 46 | /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; 47 | /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; 48 | /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; 49 | /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; 50 | --------------------------------------------------------------------------------