From c6e6ec9241605425df738a1a4c97f9396d864324 Mon Sep 17 00:00:00 2001 From: Lorefnon Date: Sat, 1 Feb 2020 11:13:11 +0530 Subject: [PATCH] Update QueryCompiler implementation to use classes --- lib/dialects/mssql/query/compiler.js | 41 +++--- lib/dialects/mysql/query/compiler.js | 42 +++--- lib/dialects/oracle/query/compiler.js | 42 +++--- lib/dialects/oracledb/query/compiler.js | 74 +++++----- lib/dialects/postgres/query/compiler.js | 43 +++--- lib/dialects/redshift/query/compiler.js | 29 ++-- lib/dialects/sqlite3/query/compiler.js | 51 ++++--- lib/query/compiler.js | 181 ++++++++++++------------ 8 files changed, 239 insertions(+), 264 deletions(-) diff --git a/lib/dialects/mssql/query/compiler.js b/lib/dialects/mssql/query/compiler.js index cb436973f5..326b822520 100644 --- a/lib/dialects/mssql/query/compiler.js +++ b/lib/dialects/mssql/query/compiler.js @@ -1,15 +1,9 @@ // MSSQL Query Compiler // ------ -const inherits = require('inherits'); const QueryCompiler = require('../../../query/compiler'); const { isEmpty, compact, identity } = require('lodash'); -function QueryCompiler_MSSQL(client, builder) { - QueryCompiler.call(this, client, builder); -} -inherits(QueryCompiler_MSSQL, QueryCompiler); - const components = [ 'columns', 'join', @@ -23,14 +17,17 @@ const components = [ 'offset', ]; -Object.assign(QueryCompiler_MSSQL.prototype, { - _emptyInsertValue: 'default values', +class QueryCompiler_MSSQL extends QueryCompiler { + constructor(client, builder) { + super(client, builder); + this._emptyInsertValue = 'default values'; + } select() { const sql = this.with(); const statements = components.map((component) => this[component](this)); return sql + compact(statements).join(' '); - }, + } // Compiles an "insert" query, allowing for multiple // inserts using a single query statement. @@ -79,7 +76,7 @@ Object.assign(QueryCompiler_MSSQL.prototype, { sql, returning, }; - }, + } // Compiles an `update` query, allowing for a return value. update() { @@ -103,7 +100,7 @@ Object.assign(QueryCompiler_MSSQL.prototype, { (!returning ? this._returning('rowcount', '@@rowcount') : ''), returning: returning || '@@rowcount', }; - }, + } // Compiles a `delete` query. del() { @@ -121,7 +118,7 @@ Object.assign(QueryCompiler_MSSQL.prototype, { (!returning ? this._returning('rowcount', '@@rowcount') : ''), returning: returning || '@@rowcount', }; - }, + } // Compiles the columns in the query, specifying if an item was distinct. columns() { @@ -156,7 +153,7 @@ Object.assign(QueryCompiler_MSSQL.prototype, { sql.join(', ') + (this.tableName ? ` from ${this.tableName}` : '') ); - }, + } _returning(method, value) { switch (method) { @@ -172,23 +169,23 @@ Object.assign(QueryCompiler_MSSQL.prototype, { case 'rowcount': return value ? ';select @@rowcount' : ''; } - }, + } // Compiles a `truncate` query. truncate() { return `truncate table ${this.tableName}`; - }, + } forUpdate() { // this doesn't work exacltly as it should, one should also mention index while locking // https://stackoverflow.com/a/9818448/360060 return 'with (UPDLOCK)'; - }, + } forShare() { // http://www.sqlteam.com/article/introduction-to-locking-in-sql-server return 'with (HOLDLOCK)'; - }, + } // Compiles a `columnInfo` query. columnInfo() { @@ -230,18 +227,18 @@ Object.assign(QueryCompiler_MSSQL.prototype, { return (column && out[column]) || out; }, }; - }, + } top() { const noLimit = !this.single.limit && this.single.limit !== 0; const noOffset = !this.single.offset; if (noLimit || !noOffset) return ''; return `top (${this.formatter.parameter(this.single.limit)})`; - }, + } limit() { return ''; - }, + } offset() { const noLimit = !this.single.limit && this.single.limit !== 0; @@ -256,8 +253,8 @@ Object.assign(QueryCompiler_MSSQL.prototype, { )} rows only`; } return offset; - }, -}); + } +} // Set the QueryBuilder & QueryCompiler on the client object, // in case anyone wants to modify things to suit their own purposes. diff --git a/lib/dialects/mysql/query/compiler.js b/lib/dialects/mysql/query/compiler.js index 7105f35dc2..85fc21b909 100644 --- a/lib/dialects/mysql/query/compiler.js +++ b/lib/dialects/mysql/query/compiler.js @@ -1,26 +1,22 @@ // MySQL Query Compiler // ------ -const inherits = require('inherits'); const QueryCompiler = require('../../../query/compiler'); const { identity } = require('lodash'); -function QueryCompiler_MySQL(client, builder) { - QueryCompiler.call(this, client, builder); +class QueryCompiler_MySQL extends QueryCompiler { + constructor(client, builder) { + super(client, builder); - const { returning } = this.single; + const { returning } = this.single; - if (returning) { - this.client.logger.warn( - '.returning() is not supported by mysql and will not have any effect.' - ); + if (returning) { + this.client.logger.warn( + '.returning() is not supported by mysql and will not have any effect.' + ); + } + this._emptyInsertValue = '() values ()'; } -} - -inherits(QueryCompiler_MySQL, QueryCompiler); - -Object.assign(QueryCompiler_MySQL.prototype, { - _emptyInsertValue: '() values ()', // Update method, including joins, wheres, order & limits. update() { @@ -38,25 +34,25 @@ Object.assign(QueryCompiler_MySQL.prototype, { (order ? ` ${order}` : '') + (limit ? ` ${limit}` : '') ); - }, + } forUpdate() { return 'for update'; - }, + } forShare() { return 'lock in share mode'; - }, + } // Only supported on MySQL 8.0+ skipLocked() { return 'skip locked'; - }, + } // Supported on MySQL 8.0+ and MariaDB 10.3.0+ noWait() { return 'nowait'; - }, + } // Compiles a `columnInfo` query. columnInfo() { @@ -72,7 +68,7 @@ Object.assign(QueryCompiler_MySQL.prototype, { 'select * from information_schema.columns where table_name = ? and table_schema = ?', bindings: [table, this.client.database()], output(resp) { - const out = resp.reduce(function(columns, val) { + const out = resp.reduce(function (columns, val) { columns[val.COLUMN_NAME] = { defaultValue: val.COLUMN_DEFAULT, type: val.DATA_TYPE, @@ -84,7 +80,7 @@ Object.assign(QueryCompiler_MySQL.prototype, { return (column && out[column]) || out; }, }; - }, + } limit() { const noLimit = !this.single.limit && this.single.limit !== 0; @@ -97,8 +93,8 @@ Object.assign(QueryCompiler_MySQL.prototype, { ? '18446744073709551615' : this.formatter.parameter(this.single.limit); return `limit ${limit}`; - }, -}); + } +} // Set the QueryBuilder & QueryCompiler on the client object, // in case anyone wants to modify things to suit their own purposes. diff --git a/lib/dialects/oracle/query/compiler.js b/lib/dialects/oracle/query/compiler.js index 7e88b810d8..672b457766 100644 --- a/lib/dialects/oracle/query/compiler.js +++ b/lib/dialects/oracle/query/compiler.js @@ -3,7 +3,6 @@ // Oracle Query Builder & Compiler // ------ const { - assign, isPlainObject, isEmpty, isString, @@ -12,7 +11,6 @@ const { compact, identity, } = require('lodash'); -const inherits = require('inherits'); const QueryCompiler = require('../../../query/compiler'); const { ReturningHelper } = require('../utils'); @@ -33,13 +31,16 @@ const components = [ // Set the "Formatter" to use for the queries, // ensuring that all parameterized values (even across sub-queries) // are properly built into the same query. -function QueryCompiler_Oracle(client, builder) { - QueryCompiler.call(this, client, builder); -} +class QueryCompiler_Oracle extends QueryCompiler { + constructor(client, builder) { + super(client, builder); -inherits(QueryCompiler_Oracle, QueryCompiler); + // Compiles the `select` statement, or nested sub-selects + // by calling each of the component compilers, trimming out + // the empties, and returning a generated query string. + this.first = this.select; + } -assign(QueryCompiler_Oracle.prototype, { // Compiles an "insert" query, allowing for multiple // inserts using a single query statement. insert() { @@ -167,7 +168,7 @@ assign(QueryCompiler_Oracle.prototype, { } return sql; - }, + } // Update method, including joins, wheres, order & limits. update() { @@ -190,16 +191,16 @@ assign(QueryCompiler_Oracle.prototype, { } return this._addReturningToSqlAndConvert(sql, returning, this.tableName); - }, + } // Compiles a `truncate` query. truncate() { return `truncate table ${this.tableName}`; - }, + } forUpdate() { return 'for update'; - }, + } forShare() { // lock for share is not directly supported by oracle @@ -208,7 +209,7 @@ assign(QueryCompiler_Oracle.prototype, { 'lock for share is not supported by oracle dialect' ); return ''; - }, + } // Compiles a `columnInfo` query. columnInfo() { @@ -247,7 +248,7 @@ assign(QueryCompiler_Oracle.prototype, { return (column && out[column]) || out; }, }; - }, + } select() { let query = this.with(); @@ -256,11 +257,11 @@ assign(QueryCompiler_Oracle.prototype, { }); query += compact(statements).join(' '); return this._surroundQueryWithLimitAndOffset(query); - }, + } aggregate(stmt) { return this._aggregate(stmt, { aliasSeparator: ' ' }); - }, + } // for single commands only _addReturningToSqlAndConvert(sql, returning, tableName) { @@ -284,7 +285,7 @@ assign(QueryCompiler_Oracle.prototype, { res.outParams = [returningHelper]; res.returning = returning; return res; - }, + } _surroundQueryWithLimitAndOffset(query) { let { limit } = this.single; @@ -314,12 +315,7 @@ assign(QueryCompiler_Oracle.prototype, { 'where rownum_ > ' + this.formatter.parameter(offset) ); - }, -}); - -// Compiles the `select` statement, or nested sub-selects -// by calling each of the component compilers, trimming out -// the empties, and returning a generated query string. -QueryCompiler_Oracle.prototype.first = QueryCompiler_Oracle.prototype.select; + } +} module.exports = QueryCompiler_Oracle; diff --git a/lib/dialects/oracledb/query/compiler.js b/lib/dialects/oracledb/query/compiler.js index d1c790b6f5..dbf1c67659 100644 --- a/lib/dialects/oracledb/query/compiler.js +++ b/lib/dialects/oracledb/query/compiler.js @@ -1,18 +1,16 @@ const _ = require('lodash'); -const inherits = require('inherits'); const Oracle_Compiler = require('../../oracle/query/compiler'); const ReturningHelper = require('../utils').ReturningHelper; const BlobHelper = require('../utils').BlobHelper; -function Oracledb_Compiler(client, builder) { - Oracle_Compiler.call(this, client, builder); -} -inherits(Oracledb_Compiler, Oracle_Compiler); +class Oracledb_Compiler extends Oracle_Compiler { + constructor(client, builder) { + super(client, builder); + } -_.assign(Oracledb_Compiler.prototype, { // Compiles an "insert" query, allowing for multiple // inserts using a single query statement. - insert: function() { + insert() { const self = this; const outBindPrep = this._prepOutbindings( this.single.insert, @@ -29,10 +27,10 @@ _.assign(Oracledb_Compiler.prototype, { ) { return this._addReturningToSqlAndConvert( 'insert into ' + - this.tableName + - ' (' + - this.formatter.wrap(this.single.returning) + - ') values (default)', + this.tableName + + ' (' + + this.formatter.wrap(this.single.returning) + + ') values (default)', outBinding[0], this.tableName, returning @@ -62,12 +60,12 @@ _.assign(Oracledb_Compiler.prototype, { if (insertData.values.length === 1) { return this._addReturningToSqlAndConvert( 'insert into ' + - this.tableName + - ' (' + - this.formatter.columnize(insertData.columns) + - ') values (' + - this.formatter.parameterize(insertData.values[0]) + - ')', + this.tableName + + ' (' + + this.formatter.columnize(insertData.columns) + + ') values (' + + this.formatter.parameterize(insertData.values[0]) + + ')', outBinding[0], this.tableName, returning @@ -78,7 +76,7 @@ _.assign(Oracledb_Compiler.prototype, { sql.returning = returning; sql.sql = 'begin ' + - _.map(insertData.values, function(value, index) { + _.map(insertData.values, function (value, index) { const parameterizedValues = !insertDefaultsOnly ? self.formatter.parameterize(value, self.client.valueForUndefined) : ''; @@ -106,7 +104,7 @@ _.assign(Oracledb_Compiler.prototype, { let usingClause = ''; let outClause = ''; - _.each(value, function(val) { + _.each(value, function (val) { if (!(val instanceof BlobHelper)) { usingClause += ' ?,'; } @@ -114,7 +112,7 @@ _.assign(Oracledb_Compiler.prototype, { usingClause = usingClause.slice(0, -1); // Build returning and into clauses - _.each(outBinding[index], function(ret) { + _.each(outBinding[index], function (ret) { const columnName = ret.columnName || ret; returningClause += self.formatter.wrap(columnName) + ','; intoClause += ' ?,'; @@ -162,20 +160,20 @@ _.assign(Oracledb_Compiler.prototype, { if (returning[0] === '*') { // Generate select statement with special order by // to keep the order because 'in (..)' may change the order - sql.returningSql = function() { + sql.returningSql = function () { return ( 'select * from ' + self.tableName + ' where ROWID in (' + this.outBinding - .map(function(v, i) { + .map(function (v, i) { return ':' + (i + 1); }) .join(', ') + ')' + ' order by case ROWID ' + this.outBinding - .map(function(v, i) { + .map(function (v, i) { return 'when CHARTOROWID(:' + (i + 1) + ') then ' + i; }) .join(' ') + @@ -185,9 +183,9 @@ _.assign(Oracledb_Compiler.prototype, { } return sql; - }, + } - _addReturningToSqlAndConvert: function( + _addReturningToSqlAndConvert( sql, outBinding, tableName, @@ -207,7 +205,7 @@ _.assign(Oracledb_Compiler.prototype, { let returningClause = ''; let intoClause = ''; // Build returning and into clauses - _.each(returningValues, function(ret) { + _.each(returningValues, function (ret) { const columnName = ret.columnName || ret; returningClause += self.formatter.wrap(columnName) + ','; intoClause += '?,'; @@ -228,16 +226,16 @@ _.assign(Oracledb_Compiler.prototype, { } res.outBinding = [outBinding]; if (returning[0] === '*') { - res.returningSql = function() { + res.returningSql = function () { return 'select * from ' + self.tableName + ' where ROWID = :1'; }; } res.returning = returning; return res; - }, + } - _prepOutbindings: function(paramValues, paramReturning) { + _prepOutbindings(paramValues, paramReturning) { const result = {}; let params = paramValues || []; let returning = paramReturning || []; @@ -251,13 +249,13 @@ _.assign(Oracledb_Compiler.prototype, { const outBinding = []; // Handle Buffer value as Blob - _.each(params, function(values, index) { + _.each(params, function (values, index) { if (returning[0] === '*') { outBinding[index] = ['ROWID']; } else { outBinding[index] = _.clone(returning); } - _.each(values, function(value, key) { + _.each(values, function (value, key) { if (value instanceof Buffer) { values[key] = new BlobHelper(key, value); @@ -278,9 +276,9 @@ _.assign(Oracledb_Compiler.prototype, { result.outBinding = outBinding; result.values = params; return result; - }, + } - update: function() { + update() { const self = this; const sql = {}; const outBindPrep = this._prepOutbindings( @@ -301,8 +299,8 @@ _.assign(Oracledb_Compiler.prototype, { } // Build returning and into clauses - _.each(outBinding, function(out) { - _.each(out, function(ret) { + _.each(outBinding, function (out) { + _.each(out, function (ret) { const columnName = ret.columnName || ret; returningClause += self.formatter.wrap(columnName) + ','; intoClause += ' ?,'; @@ -330,7 +328,7 @@ _.assign(Oracledb_Compiler.prototype, { sql.sql += ' returning ' + returningClause + ' into' + intoClause; } if (returning[0] === '*') { - sql.returningSql = function() { + sql.returningSql = function () { let sql = 'select * from ' + self.tableName; const modifiedRowsCount = this.rowsAffected.length || this.rowsAffected; let returningSqlIn = ' where ROWID in ('; @@ -354,7 +352,7 @@ _.assign(Oracledb_Compiler.prototype, { } return sql; - }, -}); + } +} module.exports = Oracledb_Compiler; diff --git a/lib/dialects/postgres/query/compiler.js b/lib/dialects/postgres/query/compiler.js index 6fee2f88dc..cc00f29830 100644 --- a/lib/dialects/postgres/query/compiler.js +++ b/lib/dialects/postgres/query/compiler.js @@ -1,25 +1,22 @@ // PostgreSQL Query Builder & Compiler // ------ -const inherits = require('inherits'); - const QueryCompiler = require('../../../query/compiler'); const { reduce, identity } = require('lodash'); -function QueryCompiler_PG(client, builder) { - QueryCompiler.call(this, client, builder); -} - -inherits(QueryCompiler_PG, QueryCompiler); +class QueryCompiler_PG extends QueryCompiler { + constructor(client, builder) { + super(client, builder); + this._defaultInsertValue = 'default'; + } -Object.assign(QueryCompiler_PG.prototype, { // Compiles a truncate query. truncate() { return `truncate ${this.tableName} restart identity`; - }, + } // is used if the an array with multiple empty values supplied - _defaultInsertValue: 'default', + // Compiles an `insert` query, allowing for multiple // inserts using a single query statement. @@ -31,7 +28,7 @@ Object.assign(QueryCompiler_PG.prototype, { sql: sql + this._returning(returning), returning, }; - }, + } // Compiles an `update` query, allowing for a return value. update() { @@ -48,7 +45,7 @@ Object.assign(QueryCompiler_PG.prototype, { this._returning(returning), returning, }; - }, + } // Compiles an `update` query, allowing for a return value. del() { @@ -58,15 +55,15 @@ Object.assign(QueryCompiler_PG.prototype, { sql: sql + this._returning(returning), returning, }; - }, + } aggregate(stmt) { return this._aggregate(stmt, { distinctParentheses: true }); - }, + } _returning(value) { return value ? ` returning ${this.formatter.columnize(value)}` : ''; - }, + } // Join array of table names and apply default schema. _tableNames(tables) { @@ -85,7 +82,7 @@ Object.assign(QueryCompiler_PG.prototype, { } return sql.join(', '); - }, + } forUpdate() { const tables = this.single.lockTables || []; @@ -93,7 +90,7 @@ Object.assign(QueryCompiler_PG.prototype, { return ( 'for update' + (tables.length ? ' of ' + this._tableNames(tables) : '') ); - }, + } forShare() { const tables = this.single.lockTables || []; @@ -101,15 +98,15 @@ Object.assign(QueryCompiler_PG.prototype, { return ( 'for share' + (tables.length ? ' of ' + this._tableNames(tables) : '') ); - }, + } skipLocked() { return 'skip locked'; - }, + } noWait() { return 'nowait'; - }, + } // Compiles a columnInfo query columnInfo() { @@ -156,11 +153,11 @@ Object.assign(QueryCompiler_PG.prototype, { return (column && out[column]) || out; }, }; - }, + } distinctOn(value) { return 'distinct on (' + this.formatter.columnize(value) + ') '; - }, -}); + } +} module.exports = QueryCompiler_PG; diff --git a/lib/dialects/redshift/query/compiler.js b/lib/dialects/redshift/query/compiler.js index 581b6ec83e..3c377d4a82 100644 --- a/lib/dialects/redshift/query/compiler.js +++ b/lib/dialects/redshift/query/compiler.js @@ -1,22 +1,19 @@ // Redshift Query Builder & Compiler // ------ -const inherits = require('inherits'); - const QueryCompiler = require('../../../query/compiler'); const QueryCompiler_PG = require('../../postgres/query/compiler'); const { reduce, identity } = require('lodash'); -function QueryCompiler_Redshift(client, builder) { - QueryCompiler_PG.call(this, client, builder); -} +class QueryCompiler_Redshift extends QueryCompiler_PG { -inherits(QueryCompiler_Redshift, QueryCompiler_PG); + constructor(client, builder) { + super(client, builder); + } -Object.assign(QueryCompiler_Redshift.prototype, { truncate() { return `truncate ${this.tableName.toLowerCase()}`; - }, + } // Compiles an `insert` query, allowing for multiple // inserts using a single query statement. @@ -27,7 +24,7 @@ Object.assign(QueryCompiler_Redshift.prototype, { return { sql, }; - }, + } // Compiles an `update` query, warning on unsupported returning update() { @@ -36,7 +33,7 @@ Object.assign(QueryCompiler_Redshift.prototype, { return { sql, }; - }, + } // Compiles an `delete` query, warning on unsupported returning del() { @@ -45,7 +42,7 @@ Object.assign(QueryCompiler_Redshift.prototype, { return { sql, }; - }, + } // simple: if trying to return, warn _slightReturn() { @@ -54,19 +51,19 @@ Object.assign(QueryCompiler_Redshift.prototype, { 'insert/update/delete returning is not supported by redshift dialect' ); } - }, + } forUpdate() { this.client.logger.warn('table lock is not supported by redshift dialect'); return ''; - }, + } forShare() { this.client.logger.warn( 'lock for share is not supported by redshift dialect' ); return ''; - }, + } // Compiles a columnInfo query columnInfo() { @@ -116,7 +113,7 @@ Object.assign(QueryCompiler_Redshift.prototype, { return (column && out[column]) || out; }, }; - }, -}); + } +} module.exports = QueryCompiler_Redshift; diff --git a/lib/dialects/sqlite3/query/compiler.js b/lib/dialects/sqlite3/query/compiler.js index 1f28d7768e..2765f92c57 100644 --- a/lib/dialects/sqlite3/query/compiler.js +++ b/lib/dialects/sqlite3/query/compiler.js @@ -1,9 +1,9 @@ // SQLite3 Query Builder & Compiler -const inherits = require('inherits'); const QueryCompiler = require('../../../query/compiler'); + const { - assign, + constant, each, isEmpty, isString, @@ -12,25 +12,25 @@ const { identity, } = require('lodash'); -function QueryCompiler_SQLite3(client, builder) { - QueryCompiler.call(this, client, builder); +const emptyStr = constant(''); - const { returning } = this.single; +class QueryCompiler_SQLite3 extends QueryCompiler { - if (returning) { - this.client.logger.warn( - '.returning() is not supported by sqlite3 and will not have any effect.' - ); - } -} + constructor(client, builder) { + super(client, builder); -inherits(QueryCompiler_SQLite3, QueryCompiler); + const { returning } = this.single; -assign(QueryCompiler_SQLite3.prototype, { - // The locks are not applicable in SQLite3 - forShare: emptyStr, + if (returning) { + this.client.logger.warn( + '.returning() is not supported by sqlite3 and will not have any effect.' + ); + } - forUpdate: emptyStr, + // The locks are not applicable in SQLite3 + this.forShare = emptyStr; + this.forUpdate = emptyStr; + } // SQLite requires us to build the multi-row insert as a listing of select with // unions joining them together. So we'll build out this list of columns and @@ -72,8 +72,8 @@ assign(QueryCompiler_SQLite3.prototype, { if (binding === undefined) throw new TypeError( '`sqlite` does not support inserting default values. Specify ' + - 'values explicitly or use the `useNullAsDefault` config flag. ' + - '(see docs http://knexjs.org/#Builder-insert).' + 'values explicitly or use the `useNullAsDefault` config flag. ' + + '(see docs http://knexjs.org/#Builder-insert).' ); }); }); @@ -105,7 +105,7 @@ assign(QueryCompiler_SQLite3.prototype, { blocks[i] = block.join(', '); } return sql + ' select ' + blocks.join(' union all select '); - }, + } // Compile a truncate table statement into SQL. truncate() { @@ -118,7 +118,7 @@ assign(QueryCompiler_SQLite3.prototype, { }).catch(noop); }, }; - }, + } // Compiles a `columnInfo` query columnInfo() { @@ -135,7 +135,7 @@ assign(QueryCompiler_SQLite3.prototype, { const maxLengthRegex = /.*\((\d+)\)/; const out = reduce( resp, - function(columns, val) { + function (columns, val) { let { type } = val; let maxLength = type.match(maxLengthRegex); if (maxLength) { @@ -155,7 +155,7 @@ assign(QueryCompiler_SQLite3.prototype, { return (column && out[column]) || out; }, }; - }, + } limit() { const noLimit = !this.single.limit && this.single.limit !== 0; @@ -166,11 +166,8 @@ assign(QueryCompiler_SQLite3.prototype, { return `limit ${this.formatter.parameter( noLimit ? -1 : this.single.limit )}`; - }, -}); - -function emptyStr() { - return ''; + } } + module.exports = QueryCompiler_SQLite3; diff --git a/lib/query/compiler.js b/lib/query/compiler.js index 4fbe851beb..7e90221985 100644 --- a/lib/query/compiler.js +++ b/lib/query/compiler.js @@ -23,20 +23,6 @@ const uuid = require('uuid'); const debugBindings = debug('knex:bindings'); -// The "QueryCompiler" takes all of the query statements which -// have been gathered in the "QueryBuilder" and turns them into a -// properly formatted / bound query string. -function QueryCompiler(client, builder) { - this.client = client; - this.method = builder._method || 'select'; - this.options = builder._options; - this.single = builder._single; - this.timeout = builder._timeout || false; - this.cancelOnTimeout = builder._cancelOnTimeout || false; - this.grouped = groupBy(builder._statements, 'grouping'); - this.formatter = client.formatter(builder); -} - const components = [ 'columns', 'join', @@ -51,9 +37,23 @@ const components = [ 'waitMode', ]; -assign(QueryCompiler.prototype, { - // Used when the insert call is empty. - _emptyInsertValue: 'default values', +// The "QueryCompiler" takes all of the query statements which +// have been gathered in the "QueryBuilder" and turns them into a +// properly formatted / bound query string. +class QueryCompiler { + constructor(client, builder) { + this.client = client; + this.method = builder._method || 'select'; + this.options = builder._options; + this.single = builder._single; + this.timeout = builder._timeout || false; + this.cancelOnTimeout = builder._cancelOnTimeout || false; + this.grouped = groupBy(builder._statements, 'grouping'); + this.formatter = client.formatter(builder); + // Used when the insert call is empty. + this._emptyInsertValue = 'default values'; + this.first = this.select; + } // Collapse the builder into a single object toSQL(method, tz) { @@ -70,7 +70,7 @@ assign(QueryCompiler.prototype, { cancelOnTimeout: this.cancelOnTimeout, bindings: this.formatter.bindings || [], __knexQueryUid: uuid.v1(), - }; + } Object.defineProperties(query, { toNative: { @@ -100,14 +100,14 @@ assign(QueryCompiler.prototype, { debugBindings(query.bindings); throw new Error( `Undefined binding(s) detected when compiling ` + - `${method.toUpperCase()}. Undefined column(s): [${this.undefinedBindingsInfo.join( - ', ' - )}] query: ${query.sql}` + `${method.toUpperCase()}. Undefined column(s): [${this.undefinedBindingsInfo.join( + ', ' + )}] query: ${query.sql}` ); } return query; - }, + } // Compiles the `select` statement, or nested sub-selects by calling each of // the component compilers, trimming out the empties, and returning a @@ -118,7 +118,7 @@ assign(QueryCompiler.prototype, { const statements = components.map((component) => this[component](this)); sql += compact(statements).join(' '); return sql; - }, + } pluck() { let toPluck = this.single.pluck; @@ -129,7 +129,8 @@ assign(QueryCompiler.prototype, { sql: this.select(), pluck: toPluck, }; - }, + } + // Compiles an "insert" query, allowing for multiple // inserts using a single query statement. @@ -167,7 +168,7 @@ assign(QueryCompiler.prototype, { } } return sql; - }, + } // Compiles the "update" query. update() { @@ -183,7 +184,7 @@ assign(QueryCompiler.prototype, { updateData.join(', ') + (wheres ? ` ${wheres}` : '') ); - }, + } // Compiles the columns in the query, specifying if an item was distinct. columns() { @@ -217,7 +218,7 @@ assign(QueryCompiler.prototype, { ? ` from ${this.single.only ? 'only ' : ''}${this.tableName}` : '') ); - }, + } _aggregate(stmt, { aliasSeparator = ' as ', distinctParentheses } = {}) { const value = stmt.value; @@ -273,16 +274,16 @@ assign(QueryCompiler.prototype, { alias = value.slice(splitOn + 4); } return [aggregateString(column, alias)]; - }, + } aggregate(stmt) { return this._aggregate(stmt); - }, + } aggregateRaw(stmt) { const distinct = stmt.aggregateDistinct ? 'distinct ' : ''; return `${stmt.method}(${distinct + this.formatter.unwrapRaw(stmt.value)})`; - }, + } // Compiles all each of the `join` clauses on the query, // including any nested join queries. @@ -315,7 +316,7 @@ assign(QueryCompiler.prototype, { } } return sql; - }, + } onBetween(statement) { return ( @@ -327,7 +328,7 @@ assign(QueryCompiler.prototype, { ' and ' ) ); - }, + } onNull(statement) { return ( @@ -335,7 +336,7 @@ assign(QueryCompiler.prototype, { ' is ' + this._not(statement, 'null') ); - }, + } onExists(statement) { return ( @@ -344,7 +345,7 @@ assign(QueryCompiler.prototype, { this.formatter.rawOrFn(statement.value) + ')' ); - }, + } onIn(statement) { if (Array.isArray(statement.column)) return this.multiOnIn(statement); @@ -354,7 +355,7 @@ assign(QueryCompiler.prototype, { this._not(statement, 'in ') + this.wrap(this.formatter.parameterize(statement.value)) ); - }, + } multiOnIn(statement) { let i = -1, @@ -365,7 +366,7 @@ assign(QueryCompiler.prototype, { sql += this.formatter.parameterize(statement.value[i]); } return sql + '))'; - }, + } // Compiles all `where` statements on the query. where() { @@ -393,15 +394,15 @@ assign(QueryCompiler.prototype, { } } return sql.length > 1 ? sql.join(' ') : ''; - }, + } group() { return this._groupsOrders('group'); - }, + } order() { return this._groupsOrders('order'); - }, + } // Compiles the `having` statements. having() { @@ -422,16 +423,16 @@ assign(QueryCompiler.prototype, { } } return sql.length > 1 ? sql.join(' ') : ''; - }, + } havingRaw(statement) { return this._not(statement, '') + this.formatter.unwrapRaw(statement.value); - }, + } havingWrapped(statement) { const val = this.formatter.rawOrFn(statement.value, 'where'); return (val && this._not(statement, '') + '(' + val.slice(6) + ')') || ''; - }, + } havingBasic(statement) { return ( @@ -442,7 +443,7 @@ assign(QueryCompiler.prototype, { ' ' + this.formatter.parameter(statement.value) ); - }, + } havingNull(statement) { return ( @@ -450,7 +451,7 @@ assign(QueryCompiler.prototype, { ' is ' + this._not(statement, 'null') ); - }, + } havingExists(statement) { return ( @@ -459,7 +460,7 @@ assign(QueryCompiler.prototype, { this.formatter.rawOrFn(statement.value) + ')' ); - }, + } havingBetween(statement) { return ( @@ -471,7 +472,7 @@ assign(QueryCompiler.prototype, { ' and ' ) ); - }, + } havingIn(statement) { if (Array.isArray(statement.column)) return this.multiHavingIn(statement); @@ -481,7 +482,7 @@ assign(QueryCompiler.prototype, { this._not(statement, 'in ') + this.wrap(this.formatter.parameterize(statement.value)) ); - }, + } multiHavingIn(statement) { let i = -1, @@ -492,7 +493,7 @@ assign(QueryCompiler.prototype, { sql += this.formatter.parameterize(statement.value[i]); } return sql + '))'; - }, + } // Compile the "union" queries attached to the main query. union() { @@ -512,24 +513,24 @@ assign(QueryCompiler.prototype, { } } return sql; - }, + } // If we haven't specified any columns or a `tableName`, we're assuming this // is only being used for unions. onlyUnions() { return !this.grouped.columns && this.grouped.union && !this.tableName; - }, + } limit() { const noLimit = !this.single.limit && this.single.limit !== 0; if (noLimit) return ''; return `limit ${this.formatter.parameter(this.single.limit)}`; - }, + } offset() { if (!this.single.offset) return ''; return `offset ${this.formatter.parameter(this.single.offset)}`; - }, + } // Compiles a `delete` query. del() { @@ -542,44 +543,44 @@ assign(QueryCompiler.prototype, { `delete from ${this.single.only ? 'only ' : ''}${tableName}` + (wheres ? ` ${wheres}` : '') ); - }, + } // Compiles a `truncate` query. truncate() { return `truncate ${this.tableName}`; - }, + } // Compiles the "locks". lock() { if (this.single.lock) { return this[this.single.lock](); } - }, + } // Compiles the wait mode on the locks. waitMode() { if (this.single.waitMode) { return this[this.single.waitMode](); } - }, + } // Fail on unsupported databases skipLocked() { throw new Error( '.skipLocked() is currently only supported on MySQL 8.0+ and PostgreSQL 9.5+' ); - }, + } // Fail on unsupported databases noWait() { throw new Error( '.noWait() is currently only supported on MySQL 8.0+, MariaDB 10.3.0+ and PostgreSQL 9.5+' ); - }, + } distinctOn(value) { throw new Error('.distinctOn() is currently only supported on PostgreSQL'); - }, + } // On Clause // ------ @@ -591,7 +592,7 @@ assign(QueryCompiler.prototype, { clause.value.call(wrapJoin, wrapJoin); let sql = ''; - wrapJoin.clauses.forEach(function(wrapClause, ii) { + wrapJoin.clauses.forEach(function (wrapClause, ii) { if (ii > 0) { sql += ` ${wrapClause.bool} `; } @@ -605,7 +606,7 @@ assign(QueryCompiler.prototype, { return `(${sql})`; } return ''; - }, + } onBasic(clause) { return ( @@ -615,7 +616,7 @@ assign(QueryCompiler.prototype, { ' ' + this.formatter.wrap(clause.value) ); - }, + } onVal(clause) { return ( @@ -625,15 +626,15 @@ assign(QueryCompiler.prototype, { ' ' + this.formatter.parameter(clause.value) ); - }, + } onRaw(clause) { return this.formatter.unwrapRaw(clause.value); - }, + } onUsing(clause) { return '(' + this.formatter.columnize(clause.column) + ')'; - }, + } // Where Clause // ------ @@ -648,7 +649,7 @@ assign(QueryCompiler.prototype, { const values = this.formatter.values(statement.value); return `${columns} ${this._not(statement, 'in ')}${values}`; - }, + } whereNull(statement) { return ( @@ -656,7 +657,7 @@ assign(QueryCompiler.prototype, { ' is ' + this._not(statement, 'null') ); - }, + } // Compiles a basic "where" clause. whereBasic(statement) { @@ -670,7 +671,7 @@ assign(QueryCompiler.prototype, { ? this.formatter.wrap(statement.value) : this.formatter.parameter(statement.value)) ); - }, + } whereExists(statement) { return ( @@ -679,12 +680,12 @@ assign(QueryCompiler.prototype, { this.formatter.rawOrFn(statement.value) + ')' ); - }, + } whereWrapped(statement) { const val = this.formatter.rawOrFn(statement.value, 'where'); return (val && this._not(statement, '') + '(' + val.slice(6) + ')') || ''; - }, + } whereBetween(statement) { return ( @@ -696,17 +697,17 @@ assign(QueryCompiler.prototype, { ' and ' ) ); - }, + } // Compiles a "whereRaw" query. whereRaw(statement) { return this._not(statement, '') + this.formatter.unwrapRaw(statement.value); - }, + } wrap(str) { if (str.charAt(0) !== '(') return `(${str})`; return str; - }, + } // Compiles all `with` statements on the query. with() { @@ -727,7 +728,7 @@ assign(QueryCompiler.prototype, { sql.push(val); } return `with ${isRecursive ? 'recursive ' : ''}${sql.join(', ')} `; - }, + } withWrapped(statement) { const val = this.formatter.rawOrFn(statement.value); @@ -736,13 +737,13 @@ assign(QueryCompiler.prototype, { this.formatter.columnize(statement.alias) + ' as (' + val + ')') || '' ); - }, + } // Determines whether to add a "not" prefix to the where clause. _not(statement, str) { if (statement.not) return `not ${str}`; return str; - }, + } _prepInsert(data) { const isRaw = this.formatter.rawOrFn(data); @@ -777,7 +778,7 @@ assign(QueryCompiler.prototype, { columns, values, }; - }, + } // "Preps" the update. _prepUpdate(data = {}) { @@ -813,8 +814,8 @@ assign(QueryCompiler.prototype, { while (++i < columns.length) { vals.push( this.formatter.wrap(columns[i]) + - ' = ' + - this.formatter.parameter(data[columns[i]]) + ' = ' + + this.formatter.parameter(data[columns[i]]) ); } @@ -833,7 +834,7 @@ assign(QueryCompiler.prototype, { } return vals; - }, + } _formatGroupsItemValue(value) { const { formatter } = this; @@ -844,7 +845,7 @@ assign(QueryCompiler.prototype, { } else { return formatter.columnize(value); } - }, + } // Compiles the `order by` statements. _groupsOrders(type) { @@ -860,15 +861,11 @@ assign(QueryCompiler.prototype, { return column + direction; }); return sql.length ? type + ' by ' + sql.join(', ') : ''; - }, -}); - -QueryCompiler.prototype.first = QueryCompiler.prototype.select; + } -// Get the table name, wrapping it if necessary. -// Implemented as a property to prevent ordering issues as described in #704. -Object.defineProperty(QueryCompiler.prototype, 'tableName', { - get() { + // Get the table name, wrapping it if necessary. + // Implemented as a property to prevent ordering issues as described in #704. + get tableName() { if (!this._tableName) { // Only call this.formatter.wrap() the first time this property is accessed. let tableName = this.single.table; @@ -878,11 +875,11 @@ Object.defineProperty(QueryCompiler.prototype, 'tableName', { this._tableName = tableName ? // Wrap subQuery with parenthesis, #3485 - this.formatter.wrap(tableName, tableName instanceof QueryBuilder) + this.formatter.wrap(tableName, tableName instanceof QueryBuilder) : ''; } return this._tableName; - }, -}); + } +} module.exports = QueryCompiler;