2015-05-09 13:58:18 -04:00
// Query Compiler
// -------
2020-12-28 16:55:08 +02:00
const helpers = require ( '../util/helpers' ) ;
2019-06-04 00:37:17 +02:00
const Raw = require ( '../raw' ) ;
2021-01-09 17:59:53 +02:00
const QueryBuilder = require ( './querybuilder' ) ;
2019-06-04 00:37:17 +02:00
const JoinClause = require ( './joinclause' ) ;
const debug = require ( 'debug' ) ;
2016-05-11 15:26:53 +02:00
2020-04-18 20:41:23 +03:00
const assign = require ( 'lodash/assign' ) ;
const compact = require ( 'lodash/compact' ) ;
const groupBy = require ( 'lodash/groupBy' ) ;
const has = require ( 'lodash/has' ) ;
const isEmpty = require ( 'lodash/isEmpty' ) ;
const map = require ( 'lodash/map' ) ;
const omitBy = require ( 'lodash/omitBy' ) ;
const reduce = require ( 'lodash/reduce' ) ;
2020-10-30 14:21:17 +02:00
const { nanoid } = require ( '../util/nanoid' ) ;
2020-10-05 21:29:39 +03:00
const { isString , isUndefined } = require ( '../util/is' ) ;
2021-01-07 23:34:46 +02:00
const {
columnize : columnize _ ,
2021-01-09 17:40:30 +02:00
direction : direction _ ,
operator : operator _ ,
wrap : wrap _ ,
2021-01-11 00:38:15 +02:00
unwrapRaw : unwrapRaw _ ,
rawOrFn : rawOrFn _ ,
2021-01-07 23:34:46 +02:00
} = require ( '../formatter/wrappingFormatter' ) ;
2016-06-17 09:19:20 -07:00
2018-07-09 08:10:34 -04:00
const debugBindings = debug ( 'knex:bindings' ) ;
2016-09-16 16:04:11 -04:00
2016-05-17 01:01:34 +10:00
const components = [
2018-07-09 08:10:34 -04:00
'columns' ,
'join' ,
'where' ,
'union' ,
'group' ,
'having' ,
'order' ,
'limit' ,
'offset' ,
'lock' ,
2019-07-06 09:05:53 -03:00
'waitMode' ,
2015-05-09 13:58:18 -04:00
] ;
2020-02-01 21:52:00 +05:30
// 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 {
2021-01-09 17:40:30 +02:00
constructor ( client , builder , bindings ) {
2020-02-01 21:52:00 +05:30
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' ) ;
2021-01-09 17:40:30 +02:00
this . formatter = client . formatter ( builder ) ;
2020-02-01 21:52:00 +05:30
// Used when the insert call is empty.
this . _emptyInsertValue = 'default values' ;
this . first = this . select ;
2021-01-07 23:34:46 +02:00
2021-01-09 17:40:30 +02:00
this . bindings = bindings || [ ] ;
this . formatter . bindings = this . bindings ;
this . bindingsHolder = this ;
2021-01-07 23:34:46 +02:00
this . builder = this . formatter . builder ;
2020-02-01 21:52:00 +05:30
}
2015-05-09 13:58:18 -04:00
// Collapse the builder into a single object
2016-05-06 13:28:50 +10:00
toSQL ( method , tz ) {
2016-05-30 20:09:16 +02:00
this . _undefinedInWhereClause = false ;
2019-09-04 23:59:04 +03:00
this . undefinedBindingsInfo = [ ] ;
2016-05-30 20:09:16 +02:00
2018-07-09 08:10:34 -04:00
method = method || this . method ;
2017-09-27 13:12:40 +03:00
const val = this [ method ] ( ) || '' ;
const query = {
2016-05-17 01:01:34 +10:00
method ,
2015-05-09 13:58:18 -04:00
options : reduce ( this . options , assign , { } ) ,
2016-02-15 17:06:08 +01:00
timeout : this . timeout ,
2016-05-26 11:06:33 -07:00
cancelOnTimeout : this . cancelOnTimeout ,
2021-01-09 17:40:30 +02:00
bindings : this . bindingsHolder . bindings || [ ] ,
2020-10-30 14:21:17 +02:00
_ _knexQueryUid : nanoid ( ) ,
2020-03-05 21:43:57 +01:00
} ;
2017-09-27 13:12:40 +03:00
2018-01-03 23:56:34 +08:00
Object . defineProperties ( query , {
toNative : {
value : ( ) => {
return {
sql : this . client . positionBindings ( query . sql ) ,
2018-07-09 08:10:34 -04:00
bindings : this . client . prepBindings ( query . bindings ) ,
2018-01-03 23:56:34 +08:00
} ;
} ,
2018-07-09 08:10:34 -04:00
enumerable : false ,
} ,
2018-01-03 23:56:34 +08:00
} ) ;
2016-03-02 16:52:32 +01:00
if ( isString ( val ) ) {
2017-09-27 13:12:40 +03:00
query . sql = val ;
} else {
assign ( query , val ) ;
2015-05-09 13:58:18 -04:00
}
2016-05-28 19:45:00 +02:00
2016-11-18 13:43:39 +02:00
if ( method === 'select' || method === 'first' ) {
2018-07-09 08:10:34 -04:00
if ( this . single . as ) {
2017-09-27 13:12:40 +03:00
query . as = this . single . as ;
2016-05-28 19:45:00 +02:00
}
2016-05-30 20:09:16 +02:00
}
2016-05-28 19:45:00 +02:00
2018-07-09 08:10:34 -04:00
if ( this . _undefinedInWhereClause ) {
debugBindings ( query . bindings ) ;
2016-06-14 19:50:51 +10:00
throw new Error (
` Undefined binding(s) detected when compiling ` +
2020-03-05 21:43:57 +01:00
` ${ method . toUpperCase ( ) } . Undefined column(s): [ ${ this . undefinedBindingsInfo . join (
', '
) } ] query : $ { query . sql } `
2016-06-14 19:50:51 +10:00
) ;
2015-05-09 13:58:18 -04:00
}
2016-03-11 22:54:37 +01:00
2017-09-27 13:12:40 +03:00
return query ;
2020-02-01 21:52:00 +05:30
}
2015-05-09 13:58:18 -04:00
2016-05-17 01:01:34 +10:00
// 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.
2016-05-06 13:28:50 +10:00
select ( ) {
2016-09-13 12:14:04 +02:00
let sql = this . with ( ) ;
2018-07-09 08:10:34 -04:00
const statements = components . map ( ( component ) => this [ component ] ( this ) ) ;
2016-09-13 12:14:04 +02:00
sql += compact ( statements ) . join ( ' ' ) ;
return sql ;
2020-02-01 21:52:00 +05:30
}
2016-03-08 08:41:13 +01:00
2016-05-06 13:28:50 +10:00
pluck ( ) {
2018-07-09 08:10:34 -04:00
let toPluck = this . single . pluck ;
2016-09-13 09:56:53 -04:00
if ( toPluck . indexOf ( '.' ) !== - 1 ) {
2018-07-09 08:10:34 -04:00
toPluck = toPluck . split ( '.' ) . slice ( - 1 ) [ 0 ] ;
2016-09-13 09:56:53 -04:00
}
2015-05-09 13:58:18 -04:00
return {
sql : this . select ( ) ,
2018-07-09 08:10:34 -04:00
pluck : toPluck ,
2015-05-09 13:58:18 -04:00
} ;
2020-02-01 21:52:00 +05:30
}
2015-05-09 13:58:18 -04:00
// Compiles an "insert" query, allowing for multiple
// inserts using a single query statement.
2016-05-06 13:28:50 +10:00
insert ( ) {
2016-05-17 01:01:34 +10:00
const insertValues = this . single . insert || [ ] ;
2021-11-09 09:34:19 +01:00
const sql = this . with ( ) + ` insert into ${ this . tableName } ` ;
const body = this . _insertBody ( insertValues ) ;
return body === '' ? '' : sql + body ;
}
_buildInsertValues ( insertData ) {
let sql = '' ;
let i = - 1 ;
while ( ++ i < insertData . values . length ) {
if ( i !== 0 ) sql += '), (' ;
sql += this . client . parameterize (
insertData . values [ i ] ,
this . client . valueForUndefined ,
this . builder ,
this . bindingsHolder
) ;
}
return sql ;
}
_insertBody ( insertValues ) {
let sql = '' ;
2015-05-09 13:58:18 -04:00
if ( Array . isArray ( insertValues ) ) {
if ( insertValues . length === 0 ) {
2018-07-09 08:10:34 -04:00
return '' ;
2015-05-09 13:58:18 -04:00
}
2016-03-02 16:52:32 +01:00
} else if ( typeof insertValues === 'object' && isEmpty ( insertValues ) ) {
2018-07-09 08:10:34 -04:00
return sql + this . _emptyInsertValue ;
2015-05-09 13:58:18 -04:00
}
2016-05-17 01:01:34 +10:00
const insertData = this . _prepInsert ( insertValues ) ;
2015-05-09 13:58:18 -04:00
if ( typeof insertData === 'string' ) {
sql += insertData ;
2018-07-09 08:10:34 -04:00
} else {
2015-05-09 13:58:18 -04:00
if ( insertData . columns . length ) {
2021-01-07 23:34:46 +02:00
sql += ` ( ${ columnize _ (
insertData . columns ,
this . builder ,
this . client ,
2021-01-09 17:40:30 +02:00
this . bindingsHolder
2021-01-07 23:34:46 +02:00
) } ` ;
2021-11-09 09:34:19 +01:00
sql += ') values (' + this . _buildInsertValues ( insertData ) + ')' ;
2015-05-09 13:58:18 -04:00
} else if ( insertValues . length === 1 && insertValues [ 0 ] ) {
2018-07-09 08:10:34 -04:00
sql += this . _emptyInsertValue ;
2015-05-09 13:58:18 -04:00
} else {
2018-07-09 08:10:34 -04:00
sql = '' ;
2015-05-09 13:58:18 -04:00
}
}
return sql ;
2020-02-01 21:52:00 +05:30
}
2015-05-09 13:58:18 -04:00
// Compiles the "update" query.
2016-05-06 13:28:50 +10:00
update ( ) {
2015-05-09 13:58:18 -04:00
// Make sure tableName is processed by the formatter first.
2018-08-04 17:33:10 +02:00
const withSQL = this . with ( ) ;
2016-05-18 19:59:24 +10:00
const { tableName } = this ;
2016-05-17 01:01:34 +10:00
const updateData = this . _prepUpdate ( this . single . update ) ;
2016-05-18 19:59:24 +10:00
const wheres = this . where ( ) ;
2018-07-09 08:10:34 -04:00
return (
2018-08-04 17:33:10 +02:00
withSQL +
2017-01-26 16:22:09 +00:00
` update ${ this . single . only ? 'only ' : '' } ${ tableName } ` +
2018-07-09 08:10:34 -04:00
' set ' +
updateData . join ( ', ' ) +
( wheres ? ` ${ wheres } ` : '' )
) ;
2020-02-01 21:52:00 +05:30
}
2015-05-09 13:58:18 -04:00
2021-01-28 00:57:25 +01:00
_hintComments ( ) {
let hints = this . grouped . hintComments || [ ] ;
hints = hints . map ( ( hint ) => compact ( hint . value ) . join ( ' ' ) ) ;
hints = compact ( hints ) . join ( ' ' ) ;
2021-02-03 01:13:16 +02:00
return hints ? ` /*+ ${ hints } */ ` : '' ;
2021-01-28 00:57:25 +01:00
}
2015-05-09 13:58:18 -04:00
// Compiles the columns in the query, specifying if an item was distinct.
2016-05-06 13:28:50 +10:00
columns ( ) {
2019-11-11 05:21:47 +05:30
let distinctClause = '' ;
2018-07-09 08:10:34 -04:00
if ( this . onlyUnions ( ) ) return '' ;
2021-02-03 01:13:16 +02:00
const hints = this . _hintComments ( ) ;
2018-07-09 08:10:34 -04:00
const columns = this . grouped . columns || [ ] ;
let i = - 1 ,
sql = [ ] ;
2015-05-09 13:58:18 -04:00
if ( columns ) {
while ( ++ i < columns . length ) {
2016-05-17 01:01:34 +10:00
const stmt = columns [ i ] ;
2019-11-11 05:21:47 +05:30
if ( stmt . distinct ) distinctClause = 'distinct ' ;
if ( stmt . distinctOn ) {
distinctClause = this . distinctOn ( stmt . value ) ;
continue ;
}
2015-05-09 13:58:18 -04:00
if ( stmt . type === 'aggregate' ) {
2019-07-16 22:18:28 +05:30
sql . push ( ... this . aggregate ( stmt ) ) ;
2018-07-09 08:10:34 -04:00
} else if ( stmt . type === 'aggregateRaw' ) {
sql . push ( this . aggregateRaw ( stmt ) ) ;
2020-12-31 14:38:50 +02:00
} else if ( stmt . type === 'analytic' ) {
sql . push ( this . analytic ( stmt ) ) ;
2021-12-22 10:47:16 +01:00
} else if ( stmt . type === 'json' ) {
sql . push ( this . json ( stmt ) ) ;
2018-07-09 08:10:34 -04:00
} else if ( stmt . value && stmt . value . length > 0 ) {
2021-01-11 00:38:15 +02:00
sql . push (
columnize _ (
stmt . value ,
this . builder ,
this . client ,
this . bindingsHolder
)
) ;
2015-05-09 13:58:18 -04:00
}
}
}
if ( sql . length === 0 ) sql = [ '*' ] ;
2021-12-22 10:47:16 +01:00
const select = this . onlyJson ( ) ? '' : 'select ' ;
2018-07-09 08:10:34 -04:00
return (
2021-12-22 10:47:16 +01:00
` ${ select } ${ hints } ${ distinctClause } ` +
2018-07-09 08:10:34 -04:00
sql . join ( ', ' ) +
( this . tableName
? ` from ${ this . single . only ? 'only ' : '' } ${ this . tableName } `
: '' )
) ;
2020-02-01 21:52:00 +05:30
}
2015-05-09 13:58:18 -04:00
2018-03-12 22:42:43 +01:00
_aggregate ( stmt , { aliasSeparator = ' as ' , distinctParentheses } = { } ) {
const value = stmt . value ;
const method = stmt . method ;
2016-05-17 01:01:34 +10:00
const distinct = stmt . aggregateDistinct ? 'distinct ' : '' ;
2021-01-09 17:40:30 +02:00
const wrap = ( identifier ) =>
wrap _ (
identifier ,
undefined ,
this . builder ,
this . client ,
this . bindingsHolder
) ;
2018-03-12 22:42:43 +01:00
const addAlias = ( value , alias ) => {
if ( alias ) {
return value + aliasSeparator + wrap ( alias ) ;
}
return value ;
} ;
const aggregateArray = ( value , alias ) => {
let columns = value . map ( wrap ) . join ( ', ' ) ;
if ( distinct ) {
const openParen = distinctParentheses ? '(' : ' ' ;
const closeParen = distinctParentheses ? ')' : '' ;
columns = distinct . trim ( ) + openParen + columns + closeParen ;
}
const aggregated = ` ${ method } ( ${ columns } ) ` ;
return addAlias ( aggregated , alias ) ;
} ;
const aggregateString = ( value , alias ) => {
const aggregated = ` ${ method } ( ${ distinct + wrap ( value ) } ) ` ;
return addAlias ( aggregated , alias ) ;
} ;
if ( Array . isArray ( value ) ) {
2019-07-16 22:18:28 +05:30
return [ aggregateArray ( value ) ] ;
2018-03-12 22:42:43 +01:00
}
if ( typeof value === 'object' ) {
2019-07-16 22:18:28 +05:30
if ( stmt . alias ) {
2019-07-23 18:00:13 +02:00
throw new Error ( 'When using an object explicit alias can not be used' ) ;
2018-03-12 22:42:43 +01:00
}
2019-07-16 22:18:28 +05:30
return Object . entries ( value ) . map ( ( [ alias , column ] ) => {
2019-07-23 18:00:13 +02:00
if ( Array . isArray ( column ) ) {
return aggregateArray ( column , alias ) ;
}
return aggregateString ( column , alias ) ;
2019-07-16 22:18:28 +05:30
} ) ;
2018-03-12 22:42:43 +01:00
}
2015-05-09 13:58:18 -04:00
// Allows us to speciy an alias for the aggregate types.
2018-03-12 22:42:43 +01:00
const splitOn = value . toLowerCase ( ) . indexOf ( ' as ' ) ;
2019-07-16 22:18:28 +05:30
let column = value ;
2019-07-23 18:00:13 +02:00
let { alias } = stmt ;
2015-05-09 13:58:18 -04:00
if ( splitOn !== - 1 ) {
2019-07-16 22:18:28 +05:30
column = value . slice ( 0 , splitOn ) ;
if ( alias ) {
2019-07-23 18:00:13 +02:00
throw new Error ( ` Found multiple aliases for same column: ${ column } ` ) ;
2019-07-16 22:18:28 +05:30
}
alias = value . slice ( splitOn + 4 ) ;
2015-05-09 13:58:18 -04:00
}
2019-07-16 22:18:28 +05:30
return [ aggregateString ( column , alias ) ] ;
2020-02-01 21:52:00 +05:30
}
2018-03-12 22:42:43 +01:00
aggregate ( stmt ) {
return this . _aggregate ( stmt ) ;
2020-02-01 21:52:00 +05:30
}
2015-05-09 13:58:18 -04:00
Allow raw expressions in query builder aggregate methods (#2257)
The aggregate methods include `count`, `min`, `max`, `sum`, `avg`,
`countDistinct`, `sumDistinct`, and `avgDistinct`, all of which
can now receive raw expressions, e.g.
```js
knex('users').count(knex.raw("data->'active'"));
```
There seems to be some demand for this, and I think it's cleaner than the alternative of
```js
knex('users').select(knex.raw("count(data->'active')"));
```
2017-10-14 08:19:06 -07:00
aggregateRaw ( stmt ) {
const distinct = stmt . aggregateDistinct ? 'distinct ' : '' ;
2021-01-11 00:38:15 +02:00
return ` ${ stmt . method } ( ${
distinct +
unwrapRaw _ (
stmt . value ,
undefined ,
this . builder ,
this . client ,
this . bindingsHolder
)
} ) ` ;
2020-02-01 21:52:00 +05:30
}
Allow raw expressions in query builder aggregate methods (#2257)
The aggregate methods include `count`, `min`, `max`, `sum`, `avg`,
`countDistinct`, `sumDistinct`, and `avgDistinct`, all of which
can now receive raw expressions, e.g.
```js
knex('users').count(knex.raw("data->'active'"));
```
There seems to be some demand for this, and I think it's cleaner than the alternative of
```js
knex('users').select(knex.raw("count(data->'active')"));
```
2017-10-14 08:19:06 -07:00
2021-11-07 14:22:49 +01:00
_joinTable ( join ) {
return join . schema && ! ( join . table instanceof Raw )
? ` ${ join . schema } . ${ join . table } `
: join . table ;
}
2015-05-09 13:58:18 -04:00
// Compiles all each of the `join` clauses on the query,
// including any nested join queries.
2016-05-06 13:28:50 +10:00
join ( ) {
2016-05-17 01:01:34 +10:00
let sql = '' ;
let i = - 1 ;
const joins = this . grouped . join ;
2015-05-09 13:58:18 -04:00
if ( ! joins ) return '' ;
while ( ++ i < joins . length ) {
2016-05-17 01:01:34 +10:00
const join = joins [ i ] ;
2021-11-07 14:22:49 +01:00
const table = this . _joinTable ( join ) ;
2018-07-09 08:10:34 -04:00
if ( i > 0 ) sql += ' ' ;
2015-05-09 13:58:18 -04:00
if ( join . joinType === 'raw' ) {
2021-02-03 21:17:20 +02:00
sql += unwrapRaw _ (
join . table ,
undefined ,
this . builder ,
this . client ,
this . bindingsHolder
) ;
2015-05-09 13:58:18 -04:00
} else {
2021-01-09 17:40:30 +02:00
sql +=
join . joinType +
' join ' +
wrap _ (
table ,
undefined ,
this . builder ,
this . client ,
this . bindingsHolder
) ;
2018-07-09 08:10:34 -04:00
let ii = - 1 ;
2015-05-09 13:58:18 -04:00
while ( ++ ii < join . clauses . length ) {
2018-07-09 08:10:34 -04:00
const clause = join . clauses [ ii ] ;
2016-05-11 16:22:15 +02:00
if ( ii > 0 ) {
2016-05-17 01:01:34 +10:00
sql += ` ${ clause . bool } ` ;
2016-05-11 16:22:15 +02:00
} else {
2016-05-17 01:01:34 +10:00
sql += ` ${ clause . type === 'onUsing' ? 'using' : 'on' } ` ;
2016-05-11 16:22:15 +02:00
}
2021-01-11 00:38:15 +02:00
const val = this [ clause . type ] ( clause ) ;
2016-05-11 15:26:53 +02:00
if ( val ) {
sql += val ;
}
2015-05-09 13:58:18 -04:00
}
}
}
return sql ;
2020-02-01 21:52:00 +05:30
}
2015-05-09 13:58:18 -04:00
2016-09-19 18:20:12 +02:00
onBetween ( statement ) {
2018-07-09 08:10:34 -04:00
return (
2021-01-09 17:40:30 +02:00
wrap _ (
statement . column ,
undefined ,
this . builder ,
this . client ,
2021-01-11 00:38:15 +02:00
this . bindingsHolder
2021-01-09 17:40:30 +02:00
) +
2018-07-09 08:10:34 -04:00
' ' +
this . _not ( statement , 'between' ) +
' ' +
2021-01-11 00:38:15 +02:00
statement . value
. map ( ( value ) =>
this . client . parameter ( value , this . builder , this . bindingsHolder )
)
. join ( ' and ' )
2018-07-09 08:10:34 -04:00
) ;
2020-02-01 21:52:00 +05:30
}
2016-09-19 18:20:12 +02:00
onNull ( statement ) {
2018-07-09 08:10:34 -04:00
return (
2021-01-09 17:40:30 +02:00
wrap _ (
statement . column ,
undefined ,
this . builder ,
this . client ,
this . bindingsHolder
) +
2018-07-09 08:10:34 -04:00
' is ' +
this . _not ( statement , 'null' )
) ;
2020-02-01 21:52:00 +05:30
}
2016-09-19 18:20:12 +02:00
onExists ( statement ) {
2018-07-09 08:10:34 -04:00
return (
this . _not ( statement , 'exists' ) +
' (' +
2021-01-11 00:38:15 +02:00
rawOrFn _ (
statement . value ,
undefined ,
this . builder ,
this . client ,
this . bindingsHolder
) +
2018-07-09 08:10:34 -04:00
')'
) ;
2020-02-01 21:52:00 +05:30
}
2016-09-19 18:20:12 +02:00
onIn ( statement ) {
if ( Array . isArray ( statement . column ) ) return this . multiOnIn ( statement ) ;
2021-11-16 09:25:29 +01:00
let values ;
if ( statement . value instanceof Raw ) {
values = this . client . parameter (
statement . value ,
this . builder ,
this . formatter
) ;
} else {
values = this . client . parameterize (
statement . value ,
undefined ,
this . builder ,
this . bindingsHolder
) ;
}
2018-07-09 08:10:34 -04:00
return (
2021-01-09 17:40:30 +02:00
wrap _ (
statement . column ,
undefined ,
this . builder ,
this . client ,
this . bindingsHolder
) +
2018-07-09 08:10:34 -04:00
' ' +
this . _not ( statement , 'in ' ) +
2021-11-16 09:25:29 +01:00
this . wrap ( values )
2018-07-09 08:10:34 -04:00
) ;
2020-02-01 21:52:00 +05:30
}
2016-09-19 18:20:12 +02:00
multiOnIn ( statement ) {
2018-07-09 08:10:34 -04:00
let i = - 1 ,
2021-01-11 00:38:15 +02:00
sql = ` ( ${ columnize _ (
statement . column ,
this . builder ,
this . client ,
this . bindingsHolder
) } ) ` ;
2018-07-09 08:10:34 -04:00
sql += this . _not ( statement , 'in ' ) + '((' ;
2016-09-19 18:20:12 +02:00
while ( ++ i < statement . value . length ) {
2018-07-09 08:10:34 -04:00
if ( i !== 0 ) sql += '),(' ;
2021-02-03 21:17:20 +02:00
sql += this . client . parameterize (
statement . value [ i ] ,
undefined ,
this . builder ,
this . bindingsHolder
) ;
2016-09-19 18:20:12 +02:00
}
2018-07-09 08:10:34 -04:00
return sql + '))' ;
2020-02-01 21:52:00 +05:30
}
2016-09-19 18:20:12 +02:00
2015-05-09 13:58:18 -04:00
// Compiles all `where` statements on the query.
2016-05-06 13:28:50 +10:00
where ( ) {
2016-05-17 01:01:34 +10:00
const wheres = this . grouped . where ;
2015-05-09 13:58:18 -04:00
if ( ! wheres ) return ;
2016-05-17 01:01:34 +10:00
const sql = [ ] ;
let i = - 1 ;
2015-05-09 13:58:18 -04:00
while ( ++ i < wheres . length ) {
2018-07-09 08:10:34 -04:00
const stmt = wheres [ i ] ;
if (
2019-07-10 20:47:56 +02:00
Object . prototype . hasOwnProperty . call ( stmt , 'value' ) &&
2018-07-09 08:10:34 -04:00
helpers . containsUndefined ( stmt . value )
) {
2019-10-06 20:21:32 +02:00
this . undefinedBindingsInfo . push ( stmt . column ) ;
2016-05-30 20:09:16 +02:00
this . _undefinedInWhereClause = true ;
}
2018-07-09 08:10:34 -04:00
const val = this [ stmt . type ] ( stmt ) ;
2015-05-09 13:58:18 -04:00
if ( val ) {
if ( sql . length === 0 ) {
2018-07-09 08:10:34 -04:00
sql [ 0 ] = 'where' ;
2015-05-09 13:58:18 -04:00
} else {
2018-07-09 08:10:34 -04:00
sql . push ( stmt . bool ) ;
2015-05-09 13:58:18 -04:00
}
2018-07-09 08:10:34 -04:00
sql . push ( val ) ;
2015-05-09 13:58:18 -04:00
}
}
return sql . length > 1 ? sql . join ( ' ' ) : '' ;
2020-02-01 21:52:00 +05:30
}
2015-05-09 13:58:18 -04:00
2016-05-06 13:28:50 +10:00
group ( ) {
2015-05-09 13:58:18 -04:00
return this . _groupsOrders ( 'group' ) ;
2020-02-01 21:52:00 +05:30
}
2015-05-09 13:58:18 -04:00
2016-05-06 13:28:50 +10:00
order ( ) {
2015-05-09 13:58:18 -04:00
return this . _groupsOrders ( 'order' ) ;
2020-02-01 21:52:00 +05:30
}
2015-05-09 13:58:18 -04:00
// Compiles the `having` statements.
2016-05-06 13:28:50 +10:00
having ( ) {
2016-05-17 01:01:34 +10:00
const havings = this . grouped . having ;
2015-05-09 13:58:18 -04:00
if ( ! havings ) return '' ;
2016-05-17 01:01:34 +10:00
const sql = [ 'having' ] ;
for ( let i = 0 , l = havings . length ; i < l ; i ++ ) {
const s = havings [ i ] ;
2016-09-19 18:20:12 +02:00
const val = this [ s . type ] ( s ) ;
if ( val ) {
2018-07-09 08:10:34 -04:00
if ( sql . length === 0 ) {
2016-09-19 18:20:12 +02:00
sql [ 0 ] = 'where' ;
}
if ( sql . length > 1 || ( sql . length === 1 && sql [ 0 ] !== 'having' ) ) {
2018-07-09 08:10:34 -04:00
sql . push ( s . bool ) ;
2015-05-09 13:58:18 -04:00
}
2018-07-09 08:10:34 -04:00
sql . push ( val ) ;
2015-05-09 13:58:18 -04:00
}
}
return sql . length > 1 ? sql . join ( ' ' ) : '' ;
2020-02-01 21:52:00 +05:30
}
2015-05-09 13:58:18 -04:00
2016-09-19 18:20:12 +02:00
havingRaw ( statement ) {
2021-01-11 00:38:15 +02:00
return (
this . _not ( statement , '' ) +
unwrapRaw _ (
statement . value ,
undefined ,
this . builder ,
this . client ,
this . bindingsHolder
)
) ;
2020-02-01 21:52:00 +05:30
}
2016-09-19 18:20:12 +02:00
havingWrapped ( statement ) {
2021-01-11 00:38:15 +02:00
const val = rawOrFn _ (
statement . value ,
'where' ,
this . builder ,
this . client ,
this . bindingsHolder
) ;
2018-07-09 08:10:34 -04:00
return ( val && this . _not ( statement , '' ) + '(' + val . slice ( 6 ) + ')' ) || '' ;
2020-02-01 21:52:00 +05:30
}
2016-09-19 18:20:12 +02:00
havingBasic ( statement ) {
2018-07-09 08:10:34 -04:00
return (
this . _not ( statement , '' ) +
2021-01-09 17:40:30 +02:00
wrap _ (
statement . column ,
undefined ,
this . builder ,
this . client ,
this . bindingsHolder
) +
2018-07-09 08:10:34 -04:00
' ' +
2021-01-09 17:40:30 +02:00
operator _ (
statement . operator ,
this . builder ,
this . client ,
this . bindingsHolder
) +
2018-07-09 08:10:34 -04:00
' ' +
2021-01-09 17:40:30 +02:00
this . client . parameter ( statement . value , this . builder , this . bindingsHolder )
2018-07-09 08:10:34 -04:00
) ;
2020-02-01 21:52:00 +05:30
}
2016-09-19 18:20:12 +02:00
havingNull ( statement ) {
2018-07-09 08:10:34 -04:00
return (
2021-01-09 17:40:30 +02:00
wrap _ (
statement . column ,
undefined ,
this . builder ,
this . client ,
this . bindingsHolder
) +
2018-07-09 08:10:34 -04:00
' is ' +
this . _not ( statement , 'null' )
) ;
2020-02-01 21:52:00 +05:30
}
2016-09-19 18:20:12 +02:00
havingExists ( statement ) {
2018-07-09 08:10:34 -04:00
return (
this . _not ( statement , 'exists' ) +
' (' +
2021-01-11 00:38:15 +02:00
rawOrFn _ (
statement . value ,
undefined ,
this . builder ,
this . client ,
this . bindingsHolder
) +
2018-07-09 08:10:34 -04:00
')'
) ;
2020-02-01 21:52:00 +05:30
}
2016-09-19 18:20:12 +02:00
havingBetween ( statement ) {
2018-07-09 08:10:34 -04:00
return (
2021-01-09 17:40:30 +02:00
wrap _ (
statement . column ,
undefined ,
this . builder ,
this . client ,
2021-01-11 00:38:15 +02:00
this . bindingsHolder
2021-01-09 17:40:30 +02:00
) +
2018-07-09 08:10:34 -04:00
' ' +
this . _not ( statement , 'between' ) +
' ' +
2021-01-11 00:38:15 +02:00
statement . value
. map ( ( value ) =>
this . client . parameter ( value , this . builder , this . bindingsHolder )
)
. join ( ' and ' )
2018-07-09 08:10:34 -04:00
) ;
2020-02-01 21:52:00 +05:30
}
2016-09-19 18:20:12 +02:00
havingIn ( statement ) {
if ( Array . isArray ( statement . column ) ) return this . multiHavingIn ( statement ) ;
2018-07-09 08:10:34 -04:00
return (
2021-01-09 17:40:30 +02:00
wrap _ (
statement . column ,
undefined ,
this . builder ,
this . client ,
this . bindingsHolder
) +
2018-07-09 08:10:34 -04:00
' ' +
this . _not ( statement , 'in ' ) +
2021-02-03 21:17:20 +02:00
this . wrap (
this . client . parameterize (
statement . value ,
undefined ,
this . builder ,
this . bindingsHolder
)
)
2018-07-09 08:10:34 -04:00
) ;
2020-02-01 21:52:00 +05:30
}
2016-09-19 18:20:12 +02:00
multiHavingIn ( statement ) {
2021-11-09 09:34:19 +01:00
return this . multiOnIn ( statement ) ;
2020-02-01 21:52:00 +05:30
}
2016-09-19 18:20:12 +02:00
2015-05-09 13:58:18 -04:00
// Compile the "union" queries attached to the main query.
2016-05-06 13:28:50 +10:00
union ( ) {
2016-05-17 01:01:34 +10:00
const onlyUnions = this . onlyUnions ( ) ;
const unions = this . grouped . union ;
2015-05-09 13:58:18 -04:00
if ( ! unions ) return '' ;
2016-05-17 01:01:34 +10:00
let sql = '' ;
for ( let i = 0 , l = unions . length ; i < l ; i ++ ) {
const union = unions [ i ] ;
2015-05-09 13:58:18 -04:00
if ( i > 0 ) sql += ' ' ;
if ( i > 0 || ! onlyUnions ) sql += union . clause + ' ' ;
2021-01-11 00:38:15 +02:00
const statement = rawOrFn _ (
union . value ,
undefined ,
this . builder ,
this . client ,
this . bindingsHolder
) ;
2015-05-09 13:58:18 -04:00
if ( statement ) {
if ( union . wrap ) sql += '(' ;
sql += statement ;
if ( union . wrap ) sql += ')' ;
}
}
return sql ;
2020-02-01 21:52:00 +05:30
}
2015-05-09 13:58:18 -04:00
// If we haven't specified any columns or a `tableName`, we're assuming this
// is only being used for unions.
2016-05-06 13:28:50 +10:00
onlyUnions ( ) {
2021-11-03 22:18:27 +01:00
return (
( ! this . grouped . columns || ! ! this . grouped . columns [ 0 ] . value ) &&
this . grouped . union &&
! this . tableName
) ;
2020-02-01 21:52:00 +05:30
}
2015-05-09 13:58:18 -04:00
2021-11-08 10:49:11 +01:00
_getValueOrParameterFromAttribute ( attribute , rawValue ) {
if ( this . single . skipBinding [ attribute ] === true ) {
return rawValue !== undefined && rawValue !== null
? rawValue
: this . single [ attribute ] ;
}
return this . client . parameter (
this . single [ attribute ] ,
this . builder ,
this . bindingsHolder
) ;
}
2021-12-22 10:47:16 +01:00
onlyJson ( ) {
return (
! this . tableName &&
this . grouped . columns &&
this . grouped . columns . length === 1 &&
this . grouped . columns [ 0 ] . type === 'json'
) ;
}
2016-05-06 13:28:50 +10:00
limit ( ) {
2016-05-17 01:01:34 +10:00
const noLimit = ! this . single . limit && this . single . limit !== 0 ;
2015-05-09 13:58:18 -04:00
if ( noLimit ) return '' ;
2021-11-08 10:49:11 +01:00
return ` limit ${ this . _getValueOrParameterFromAttribute ( 'limit' ) } ` ;
2020-02-01 21:52:00 +05:30
}
2015-05-09 13:58:18 -04:00
2016-05-06 13:28:50 +10:00
offset ( ) {
2015-05-09 13:58:18 -04:00
if ( ! this . single . offset ) return '' ;
2021-11-08 10:49:11 +01:00
return ` offset ${ this . _getValueOrParameterFromAttribute ( 'offset' ) } ` ;
2020-02-01 21:52:00 +05:30
}
2015-05-09 13:58:18 -04:00
// Compiles a `delete` query.
2016-05-06 13:28:50 +10:00
del ( ) {
2015-05-09 13:58:18 -04:00
// Make sure tableName is processed by the formatter first.
2016-05-18 19:59:24 +10:00
const { tableName } = this ;
2018-08-04 17:33:10 +02:00
const withSQL = this . with ( ) ;
2016-05-17 01:01:34 +10:00
const wheres = this . where ( ) ;
2021-07-13 09:41:16 +03:00
const joins = this . join ( ) ;
// When using joins, delete the "from" table values as a default
const deleteSelector = joins ? tableName + ' ' : '' ;
2018-07-09 08:10:34 -04:00
return (
2018-08-04 17:33:10 +02:00
withSQL +
2021-07-13 09:41:16 +03:00
` delete ${ deleteSelector } from ${
this . single . only ? 'only ' : ''
} $ { tableName } ` +
( joins ? ` ${ joins } ` : '' ) +
2018-07-09 08:10:34 -04:00
( wheres ? ` ${ wheres } ` : '' )
) ;
2020-02-01 21:52:00 +05:30
}
2015-05-09 13:58:18 -04:00
// Compiles a `truncate` query.
2016-05-06 13:28:50 +10:00
truncate ( ) {
2016-05-17 01:01:34 +10:00
return ` truncate ${ this . tableName } ` ;
2020-02-01 21:52:00 +05:30
}
2015-05-09 13:58:18 -04:00
// Compiles the "locks".
2016-05-06 13:28:50 +10:00
lock ( ) {
2015-05-09 13:58:18 -04:00
if ( this . single . lock ) {
2018-07-09 08:10:34 -04:00
return this [ this . single . lock ] ( ) ;
2015-05-09 13:58:18 -04:00
}
2020-02-01 21:52:00 +05:30
}
2015-05-09 13:58:18 -04:00
2019-07-06 09:05:53 -03:00
// Compiles the wait mode on the locks.
waitMode ( ) {
if ( this . single . waitMode ) {
return this [ this . single . waitMode ] ( ) ;
}
2020-02-01 21:52:00 +05:30
}
2019-07-06 09:05:53 -03:00
// Fail on unsupported databases
skipLocked ( ) {
throw new Error (
'.skipLocked() is currently only supported on MySQL 8.0+ and PostgreSQL 9.5+'
) ;
2020-02-01 21:52:00 +05:30
}
2019-07-06 09:05:53 -03:00
// 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+'
) ;
2020-02-01 21:52:00 +05:30
}
2019-07-06 09:05:53 -03:00
2019-11-11 05:21:47 +05:30
distinctOn ( value ) {
2019-12-28 22:36:14 +01:00
throw new Error ( '.distinctOn() is currently only supported on PostgreSQL' ) ;
2020-02-01 21:52:00 +05:30
}
2019-11-11 05:21:47 +05:30
2016-05-11 15:26:53 +02:00
// On Clause
// ------
onWrapped ( clause ) {
2016-05-17 01:01:34 +10:00
const self = this ;
2016-05-11 15:26:53 +02:00
2016-05-17 01:01:34 +10:00
const wrapJoin = new JoinClause ( ) ;
2016-05-12 09:09:42 +02:00
clause . value . call ( wrapJoin , wrapJoin ) ;
2016-05-11 15:26:53 +02:00
2016-05-17 01:01:34 +10:00
let sql = '' ;
2021-01-11 00:38:15 +02:00
for ( let ii = 0 ; ii < wrapJoin . clauses . length ; ii ++ ) {
const wrapClause = wrapJoin . clauses [ ii ] ;
2016-05-11 15:26:53 +02:00
if ( ii > 0 ) {
2016-05-17 01:01:34 +10:00
sql += ` ${ wrapClause . bool } ` ;
2016-05-11 15:26:53 +02:00
}
2016-05-17 01:01:34 +10:00
const val = self [ wrapClause . type ] ( wrapClause ) ;
2016-05-11 15:26:53 +02:00
if ( val ) {
sql += val ;
}
2021-01-11 00:38:15 +02:00
}
2016-05-11 15:26:53 +02:00
if ( sql . length ) {
2016-05-17 01:01:34 +10:00
return ` ( ${ sql } ) ` ;
2016-05-11 15:26:53 +02:00
}
return '' ;
2020-02-01 21:52:00 +05:30
}
2016-05-11 15:26:53 +02:00
onBasic ( clause ) {
2016-05-17 01:01:34 +10:00
return (
2021-01-09 17:40:30 +02:00
wrap _ (
clause . column ,
undefined ,
this . builder ,
this . client ,
this . bindingsHolder
) +
2018-07-09 08:10:34 -04:00
' ' +
2021-01-11 00:38:15 +02:00
operator _ (
clause . operator ,
this . builder ,
this . client ,
this . bindingsHolder
) +
2018-07-09 08:10:34 -04:00
' ' +
2021-01-09 17:40:30 +02:00
wrap _ (
clause . value ,
undefined ,
this . builder ,
this . client ,
this . bindingsHolder
)
2016-05-17 01:01:34 +10:00
) ;
2020-02-01 21:52:00 +05:30
}
2016-05-11 15:26:53 +02:00
2018-10-03 09:32:29 -04:00
onVal ( clause ) {
2018-10-03 04:44:46 +02:00
return (
2021-01-09 17:40:30 +02:00
wrap _ (
clause . column ,
undefined ,
this . builder ,
this . client ,
this . bindingsHolder
) +
2018-10-03 04:44:46 +02:00
' ' +
2021-01-11 00:38:15 +02:00
operator _ (
clause . operator ,
this . builder ,
this . client ,
this . bindingsHolder
) +
2018-10-03 04:44:46 +02:00
' ' +
2021-01-09 17:40:30 +02:00
this . client . parameter ( clause . value , this . builder , this . bindingsHolder )
2018-10-03 04:44:46 +02:00
) ;
2020-02-01 21:52:00 +05:30
}
2018-10-03 04:44:46 +02:00
2016-05-11 15:26:53 +02:00
onRaw ( clause ) {
2021-02-03 21:17:20 +02:00
return unwrapRaw _ (
clause . value ,
undefined ,
this . builder ,
this . client ,
this . bindingsHolder
) ;
2020-02-01 21:52:00 +05:30
}
2016-05-11 15:26:53 +02:00
2016-05-11 16:22:15 +02:00
onUsing ( clause ) {
2021-01-11 00:38:15 +02:00
return (
'(' +
columnize _ (
clause . column ,
this . builder ,
this . client ,
this . bindingsHolder
) +
')'
) ;
2020-02-01 21:52:00 +05:30
}
2016-05-11 16:22:15 +02:00
2015-05-09 13:58:18 -04:00
// Where Clause
// ------
2021-10-31 19:52:22 +01:00
_valueClause ( statement ) {
return statement . asColumn
? wrap _ (
statement . value ,
undefined ,
this . builder ,
this . client ,
this . bindingsHolder
)
: this . client . parameter (
statement . value ,
this . builder ,
this . bindingsHolder
) ;
}
_columnClause ( statement ) {
2021-01-11 00:38:15 +02:00
let columns ;
2018-02-22 07:51:09 +11:00
if ( Array . isArray ( statement . column ) ) {
2021-01-11 00:38:15 +02:00
columns = ` ( ${ columnize _ (
statement . column ,
this . builder ,
this . client ,
this . bindingsHolder
) } ) ` ;
2018-02-22 07:51:09 +11:00
} else {
2021-01-09 17:40:30 +02:00
columns = wrap _ (
statement . column ,
undefined ,
this . builder ,
this . client ,
this . bindingsHolder
) ;
2015-05-09 13:58:18 -04:00
}
2021-10-31 19:52:22 +01:00
return columns ;
}
2018-02-22 07:51:09 +11:00
2021-10-31 19:52:22 +01:00
whereIn ( statement ) {
2021-02-03 01:13:16 +02:00
const values = this . client . values (
statement . value ,
this . builder ,
2021-02-03 21:17:20 +02:00
this . bindingsHolder
2021-02-03 01:13:16 +02:00
) ;
2021-10-31 19:52:22 +01:00
return ` ${ this . _columnClause ( statement ) } ${ this . _not (
statement ,
'in '
) } $ { values } ` ;
}
whereLike ( statement ) {
return ` ${ this . _columnClause ( statement ) } ${ this . _not (
statement ,
'like '
) } $ { this . _valueClause ( statement ) } ` ;
}
whereILike ( statement ) {
return ` ${ this . _columnClause ( statement ) } ${ this . _not (
statement ,
'ilike '
) } $ { this . _valueClause ( statement ) } ` ;
2020-02-01 21:52:00 +05:30
}
2015-05-09 13:58:18 -04:00
2016-05-06 13:28:50 +10:00
whereNull ( statement ) {
2018-07-09 08:10:34 -04:00
return (
2021-01-09 17:40:30 +02:00
wrap _ (
statement . column ,
undefined ,
this . builder ,
this . client ,
this . bindingsHolder
) +
2018-07-09 08:10:34 -04:00
' is ' +
this . _not ( statement , 'null' )
) ;
2020-02-01 21:52:00 +05:30
}
2015-05-09 13:58:18 -04:00
// Compiles a basic "where" clause.
2016-05-06 13:28:50 +10:00
whereBasic ( statement ) {
2018-07-09 08:10:34 -04:00
return (
this . _not ( statement , '' ) +
2021-01-09 17:40:30 +02:00
wrap _ (
statement . column ,
undefined ,
this . builder ,
this . client ,
this . bindingsHolder
) +
2018-07-09 08:10:34 -04:00
' ' +
2021-01-11 00:38:15 +02:00
operator _ (
statement . operator ,
this . builder ,
this . client ,
this . bindingsHolder
) +
2018-07-09 08:10:34 -04:00
' ' +
2021-10-31 19:52:22 +01:00
this . _valueClause ( statement )
2018-07-09 08:10:34 -04:00
) ;
2020-02-01 21:52:00 +05:30
}
2015-05-09 13:58:18 -04:00
2016-05-06 13:28:50 +10:00
whereExists ( statement ) {
2018-07-09 08:10:34 -04:00
return (
this . _not ( statement , 'exists' ) +
' (' +
2021-01-11 00:38:15 +02:00
rawOrFn _ (
statement . value ,
undefined ,
this . builder ,
this . client ,
this . bindingsHolder
) +
2018-07-09 08:10:34 -04:00
')'
) ;
2020-02-01 21:52:00 +05:30
}
2015-05-09 13:58:18 -04:00
2016-05-06 13:28:50 +10:00
whereWrapped ( statement ) {
2021-01-11 00:38:15 +02:00
const val = rawOrFn _ (
statement . value ,
'where' ,
this . builder ,
this . client ,
this . bindingsHolder
) ;
2018-07-09 08:10:34 -04:00
return ( val && this . _not ( statement , '' ) + '(' + val . slice ( 6 ) + ')' ) || '' ;
2020-02-01 21:52:00 +05:30
}
2015-05-09 13:58:18 -04:00
2016-05-06 13:28:50 +10:00
whereBetween ( statement ) {
2018-07-09 08:10:34 -04:00
return (
2021-01-09 17:40:30 +02:00
wrap _ (
statement . column ,
undefined ,
this . builder ,
this . client ,
this . bindingsHolder
) +
2018-07-09 08:10:34 -04:00
' ' +
this . _not ( statement , 'between' ) +
' ' +
2021-01-11 00:38:15 +02:00
statement . value
. map ( ( value ) =>
this . client . parameter ( value , this . builder , this . bindingsHolder )
)
. join ( ' and ' )
2018-07-09 08:10:34 -04:00
) ;
2020-02-01 21:52:00 +05:30
}
2015-05-09 13:58:18 -04:00
// Compiles a "whereRaw" query.
2016-05-06 13:28:50 +10:00
whereRaw ( statement ) {
2021-01-11 00:38:15 +02:00
return (
this . _not ( statement , '' ) +
unwrapRaw _ (
statement . value ,
undefined ,
this . builder ,
this . client ,
this . bindingsHolder
)
) ;
2020-02-01 21:52:00 +05:30
}
2015-05-09 13:58:18 -04:00
2021-12-22 10:47:16 +01:00
_jsonWrapValue ( jsonValue ) {
if ( this . builder . _isJsonObject ( jsonValue ) ) {
return JSON . stringify ( jsonValue ) ;
}
return jsonValue ;
}
_jsonValueClause ( statement ) {
statement . value = this . _jsonWrapValue ( statement . value ) ;
return this . _valueClause ( statement ) ;
}
whereJsonObject ( statement ) {
return ` ${ this . _columnClause ( statement ) } ${
statement . not ? '!=' : '='
} $ { this . _jsonValueClause ( statement ) } ` ;
}
2016-05-06 13:28:50 +10:00
wrap ( str ) {
2016-05-17 01:01:34 +10:00
if ( str . charAt ( 0 ) !== '(' ) return ` ( ${ str } ) ` ;
2015-05-09 13:58:18 -04:00
return str ;
2020-02-01 21:52:00 +05:30
}
2015-05-09 13:58:18 -04:00
2021-12-22 10:47:16 +01:00
json ( stmt ) {
return this [ stmt . method ] ( stmt . params ) ;
}
2020-12-31 14:38:50 +02:00
analytic ( stmt ) {
let sql = '' ;
const self = this ;
sql += stmt . method + '() over (' ;
if ( stmt . raw ) {
sql += stmt . raw ;
} else {
if ( stmt . partitions . length ) {
sql += 'partition by ' ;
sql +=
map ( stmt . partitions , function ( partition ) {
2021-08-30 19:17:16 +02:00
if ( isString ( partition ) ) {
return self . formatter . columnize ( partition ) ;
} else return self . formatter . columnize ( partition . column ) + ( partition . order ? ' ' + partition . order : '' ) ;
2020-12-31 14:38:50 +02:00
} ) . join ( ', ' ) + ' ' ;
}
sql += 'order by ' ;
sql += map ( stmt . order , function ( order ) {
2021-08-30 19:17:16 +02:00
if ( isString ( order ) ) {
return self . formatter . columnize ( order ) ;
} else return self . formatter . columnize ( order . column ) + ( order . order ? ' ' + order . order : '' ) ;
2020-12-31 14:38:50 +02:00
} ) . join ( ', ' ) ;
}
sql += ')' ;
if ( stmt . alias ) {
sql += ' as ' + stmt . alias ;
}
return sql ;
}
2016-09-13 12:14:04 +02:00
// Compiles all `with` statements on the query.
with ( ) {
2018-07-09 08:10:34 -04:00
if ( ! this . grouped . with || ! this . grouped . with . length ) {
2016-09-13 12:14:04 +02:00
return '' ;
}
const withs = this . grouped . with ;
if ( ! withs ) return ;
const sql = [ ] ;
let i = - 1 ;
2018-11-04 19:30:04 -06:00
let isRecursive = false ;
2016-09-13 12:14:04 +02:00
while ( ++ i < withs . length ) {
2018-07-09 08:10:34 -04:00
const stmt = withs [ i ] ;
2018-11-04 19:30:04 -06:00
if ( stmt . recursive ) {
isRecursive = true ;
}
2018-07-09 08:10:34 -04:00
const val = this [ stmt . type ] ( stmt ) ;
2016-09-13 12:14:04 +02:00
sql . push ( val ) ;
}
2018-11-04 19:30:04 -06:00
return ` with ${ isRecursive ? 'recursive ' : '' } ${ sql . join ( ', ' ) } ` ;
2020-02-01 21:52:00 +05:30
}
2016-09-13 12:14:04 +02:00
withWrapped ( statement ) {
2021-01-11 00:38:15 +02:00
const val = rawOrFn _ (
statement . value ,
undefined ,
this . builder ,
this . client ,
this . bindingsHolder
) ;
2021-09-03 15:27:32 -04:00
const columnList = statement . columnList
? '(' +
columnize _ (
statement . columnList ,
this . builder ,
this . client ,
this . bindingsHolder
) +
')'
: '' ;
2022-01-20 22:54:03 +01:00
const materialized =
statement . materialized === undefined
? ''
: statement . materialized
? 'materialized '
: 'not materialized ' ;
2018-07-09 08:10:34 -04:00
return (
( val &&
2021-01-11 00:38:15 +02:00
columnize _ (
statement . alias ,
this . builder ,
this . client ,
this . bindingsHolder
) +
2021-09-03 15:27:32 -04:00
columnList +
2022-01-20 22:54:03 +01:00
' as ' +
materialized +
'(' +
2021-01-11 00:38:15 +02:00
val +
')' ) ||
2018-07-09 08:10:34 -04:00
''
) ;
2020-02-01 21:52:00 +05:30
}
2016-09-13 12:14:04 +02:00
2015-05-09 13:58:18 -04:00
// Determines whether to add a "not" prefix to the where clause.
2016-05-06 13:28:50 +10:00
_not ( statement , str ) {
2016-05-17 01:01:34 +10:00
if ( statement . not ) return ` not ${ str } ` ;
2015-05-09 13:58:18 -04:00
return str ;
2020-02-01 21:52:00 +05:30
}
2016-03-08 08:41:13 +01:00
2016-05-06 13:28:50 +10:00
_prepInsert ( data ) {
2021-01-11 00:38:15 +02:00
const isRaw = rawOrFn _ (
data ,
undefined ,
this . builder ,
this . client ,
this . bindingsHolder
) ;
2015-05-09 13:58:18 -04:00
if ( isRaw ) return isRaw ;
2016-05-17 01:01:34 +10:00
let columns = [ ] ;
2016-05-18 19:59:24 +10:00
const values = [ ] ;
2015-05-09 13:58:18 -04:00
if ( ! Array . isArray ( data ) ) data = data ? [ data ] : [ ] ;
2018-07-09 08:10:34 -04:00
let i = - 1 ;
2015-05-09 13:58:18 -04:00
while ( ++ i < data . length ) {
if ( data [ i ] == null ) break ;
2018-07-09 08:10:34 -04:00
if ( i === 0 ) columns = Object . keys ( data [ i ] ) . sort ( ) ;
const row = new Array ( columns . length ) ;
const keys = Object . keys ( data [ i ] ) ;
let j = - 1 ;
2015-05-09 13:58:18 -04:00
while ( ++ j < keys . length ) {
2016-05-17 01:01:34 +10:00
const key = keys [ j ] ;
let idx = columns . indexOf ( key ) ;
2015-05-09 13:58:18 -04:00
if ( idx === - 1 ) {
2018-07-09 08:10:34 -04:00
columns = columns . concat ( key ) . sort ( ) ;
idx = columns . indexOf ( key ) ;
let k = - 1 ;
2015-05-09 13:58:18 -04:00
while ( ++ k < values . length ) {
2018-07-09 08:10:34 -04:00
values [ k ] . splice ( idx , 0 , undefined ) ;
2015-05-09 13:58:18 -04:00
}
2018-07-09 08:10:34 -04:00
row . splice ( idx , 0 , undefined ) ;
2015-05-09 13:58:18 -04:00
}
2018-07-09 08:10:34 -04:00
row [ idx ] = data [ i ] [ key ] ;
2015-05-09 13:58:18 -04:00
}
2018-07-09 08:10:34 -04:00
values . push ( row ) ;
2015-05-09 13:58:18 -04:00
}
return {
2016-05-17 01:01:34 +10:00
columns ,
2018-07-09 08:10:34 -04:00
values ,
2015-05-09 13:58:18 -04:00
} ;
2020-02-01 21:52:00 +05:30
}
2015-05-09 13:58:18 -04:00
// "Preps" the update.
2018-10-06 16:10:56 +02:00
_prepUpdate ( data = { } ) {
const { counter = { } } = this . single ;
2019-12-28 22:36:14 +01:00
for ( const column of Object . keys ( counter ) ) {
2018-10-06 16:10:56 +02:00
//Skip?
if ( has ( data , column ) ) {
//Needed?
this . client . logger . warn (
` increment/decrement called for a column that has already been specified in main .update() call. Ignoring increment/decrement and using value from .update() call. `
) ;
continue ;
}
let value = counter [ column ] ;
const symbol = value < 0 ? '-' : '+' ;
if ( symbol === '-' ) {
value = - value ;
}
data [ column ] = this . client . raw ( ` ?? ${ symbol } ? ` , [ column , value ] ) ;
}
2018-07-09 08:10:34 -04:00
data = omitBy ( data , isUndefined ) ;
2018-10-06 16:10:56 +02:00
2018-07-09 08:10:34 -04:00
const vals = [ ] ;
const columns = Object . keys ( data ) ;
let i = - 1 ;
2018-10-06 16:10:56 +02:00
2017-07-26 11:36:50 +03:00
while ( ++ i < columns . length ) {
2016-05-17 01:01:34 +10:00
vals . push (
2021-01-09 17:40:30 +02:00
wrap _ (
columns [ i ] ,
undefined ,
this . builder ,
this . client ,
this . bindingsHolder
) +
2020-03-05 21:43:57 +01:00
' = ' +
2021-01-09 17:40:30 +02:00
this . client . parameter (
data [ columns [ i ] ] ,
this . builder ,
this . bindingsHolder
)
2016-05-17 01:01:34 +10:00
) ;
2015-05-09 13:58:18 -04:00
}
2018-02-15 13:28:35 +01:00
2018-07-09 08:10:34 -04:00
if ( isEmpty ( vals ) ) {
throw new Error (
[
'Empty .update() call detected!' ,
'Update data does not contain any values to update.' ,
'This will result in a faulty query.' ,
2019-12-28 22:36:14 +01:00
this . single . table ? ` Table: ${ this . single . table } . ` : '' ,
this . single . update
? ` Columns: ${ Object . keys ( this . single . update ) } . `
: '' ,
2018-07-09 08:10:34 -04:00
] . join ( ' ' )
) ;
2018-02-15 13:28:35 +01:00
}
2015-05-09 13:58:18 -04:00
return vals ;
2020-02-01 21:52:00 +05:30
}
2015-05-09 13:58:18 -04:00
2021-10-10 21:45:48 +02:00
_formatGroupsItemValue ( value , nulls ) {
2019-10-24 23:08:12 +09:00
const { formatter } = this ;
2021-10-10 21:45:48 +02:00
let nullOrder = '' ;
if ( nulls === 'last' ) {
nullOrder = ' is null' ;
} else if ( nulls === 'first' ) {
nullOrder = ' is not null' ;
}
2021-12-15 07:57:23 -05:00
let groupOrder ;
2019-10-24 23:08:12 +09:00
if ( value instanceof Raw ) {
2021-12-15 07:57:23 -05:00
groupOrder = unwrapRaw _ (
2021-02-03 21:17:20 +02:00
value ,
undefined ,
this . builder ,
this . client ,
this . bindingsHolder
) ;
2021-10-10 21:45:48 +02:00
} else if ( value instanceof QueryBuilder || nulls ) {
2021-12-15 07:57:23 -05:00
groupOrder = '(' + formatter . columnize ( value ) + nullOrder + ')' ;
2019-10-24 23:08:12 +09:00
} else {
2021-12-15 07:57:23 -05:00
groupOrder = formatter . columnize ( value ) ;
2019-10-24 23:08:12 +09:00
}
2021-12-15 07:57:23 -05:00
return groupOrder ;
}
_groupOrder ( item , type ) {
const column = this . _formatGroupsItemValue ( item . value , item . nulls ) ;
const direction =
type === 'order' && item . type !== 'orderByRaw'
? ` ${ direction _ (
item . direction ,
this . builder ,
this . client ,
this . bindingsHolder
) } `
: '' ;
return column + direction ;
2020-02-01 21:52:00 +05:30
}
2019-10-24 23:08:12 +09:00
2015-05-09 13:58:18 -04:00
// Compiles the `order by` statements.
2016-05-06 13:28:50 +10:00
_groupsOrders ( type ) {
2016-05-17 01:01:34 +10:00
const items = this . grouped [ type ] ;
2015-05-09 13:58:18 -04:00
if ( ! items ) return '' ;
2018-07-09 08:10:34 -04:00
const sql = items . map ( ( item ) => {
2021-12-15 07:57:23 -05:00
return this . _groupOrder ( item , type ) ;
2015-05-09 13:58:18 -04:00
} ) ;
return sql . length ? type + ' by ' + sql . join ( ', ' ) : '' ;
2020-02-01 21:52:00 +05:30
}
2015-05-09 13:58:18 -04:00
2020-02-01 21:52:00 +05:30
// Get the table name, wrapping it if necessary.
// Implemented as a property to prevent ordering issues as described in #704.
get tableName ( ) {
2018-07-09 08:10:34 -04:00
if ( ! this . _tableName ) {
2015-05-09 13:58:18 -04:00
// Only call this.formatter.wrap() the first time this property is accessed.
2016-05-17 01:01:34 +10:00
let tableName = this . single . table ;
const schemaName = this . single . schema ;
2015-08-09 20:22:39 -03:00
2021-02-03 19:16:00 +02:00
if ( tableName && schemaName ) {
const isQueryBuilder = tableName instanceof QueryBuilder ;
const isRawQuery = tableName instanceof Raw ;
const isFunction = typeof tableName === 'function' ;
if ( ! isQueryBuilder && ! isRawQuery && ! isFunction ) {
tableName = ` ${ schemaName } . ${ tableName } ` ;
}
}
2015-08-09 20:22:39 -03:00
2019-10-29 04:38:01 +08:00
this . _tableName = tableName
? // Wrap subQuery with parenthesis, #3485
2021-01-09 17:40:30 +02:00
wrap _ (
tableName ,
tableName instanceof QueryBuilder ,
this . builder ,
this . client ,
this . bindingsHolder
)
2019-10-29 04:38:01 +08:00
: '' ;
2015-05-09 13:58:18 -04:00
}
return this . _tableName ;
2020-02-01 21:52:00 +05:30
}
2021-12-22 10:47:16 +01:00
_jsonPathWrap ( extraction ) {
return this . client . parameter (
extraction . path || extraction [ 1 ] ,
this . builder ,
this . bindingsHolder
) ;
}
// Json common functions
_jsonExtract ( nameFunction , params ) {
let extractions ;
if ( Array . isArray ( params . column ) ) {
extractions = params . column ;
} else {
extractions = [ params ] ;
}
if ( ! Array . isArray ( nameFunction ) ) {
nameFunction = [ nameFunction ] ;
}
return extractions
. map ( ( extraction ) => {
let jsonCol = ` ${ columnize _ (
extraction . column || extraction [ 0 ] ,
this . builder ,
this . client ,
this . bindingsHolder
) } , $ { this . _jsonPathWrap ( extraction ) } ` ;
nameFunction . forEach ( ( f ) => {
jsonCol = f + '(' + jsonCol + ')' ;
} ) ;
const alias = extraction . alias || extraction [ 2 ] ;
return alias
? this . client . alias ( jsonCol , this . formatter . wrap ( alias ) )
: jsonCol ;
} )
. join ( ', ' ) ;
}
_jsonSet ( nameFunction , params ) {
const jsonSet = ` ${ nameFunction } ( ${ columnize _ (
params . column ,
this . builder ,
this . client ,
this . bindingsHolder
) } , $ { this . client . parameter (
params . path ,
this . builder ,
this . bindingsHolder
) } , $ { this . client . parameter (
params . value ,
this . builder ,
this . bindingsHolder
) } ) ` ;
return params . alias
? this . client . alias ( jsonSet , this . formatter . wrap ( params . alias ) )
: jsonSet ;
}
_whereJsonPath ( nameFunction , statement ) {
return ` ${ nameFunction } ( ${ this . _columnClause (
statement
) } , $ { this . _jsonPathWrap ( { path : statement . jsonPath } ) } ) $ { operator _ (
statement . operator ,
this . builder ,
this . client ,
this . bindingsHolder
) } $ { this . _jsonValueClause ( statement ) } ` ;
}
_onJsonPathEquals ( nameJoinFunction , clause ) {
return (
nameJoinFunction +
'(' +
wrap _ (
clause . columnFirst ,
undefined ,
this . builder ,
this . client ,
this . bindingsHolder
) +
', ' +
this . client . parameter (
clause . jsonPathFirst ,
this . builder ,
this . bindingsHolder
) +
') = ' +
nameJoinFunction +
'(' +
wrap _ (
clause . columnSecond ,
undefined ,
this . builder ,
this . client ,
this . bindingsHolder
) +
', ' +
this . client . parameter (
clause . jsonPathSecond ,
this . builder ,
this . bindingsHolder
) +
')'
) ;
}
2020-02-01 21:52:00 +05:30
}
2015-05-09 13:58:18 -04:00
2019-06-04 00:37:17 +02:00
module . exports = QueryCompiler ;