2015-05-09 13:58:18 -04:00
// Query Compiler
// -------
2019-06-04 00:37:17 +02:00
const helpers = require ( '../helpers' ) ;
const Raw = require ( '../raw' ) ;
const JoinClause = require ( './joinclause' ) ;
const debug = require ( 'debug' ) ;
2016-05-11 15:26:53 +02:00
2019-06-04 00:37:17 +02:00
const {
2018-07-09 08:10:34 -04:00
assign ,
bind ,
compact ,
groupBy ,
isEmpty ,
isString ,
isUndefined ,
map ,
omitBy ,
reduce ,
2018-10-06 16:10:56 +02:00
has ,
keys ,
2019-06-04 00:37:17 +02:00
} = require ( 'lodash' ) ;
const uuid = require ( 'uuid' ) ;
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
2015-05-09 13:58:18 -04:00
// 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 ) {
2018-07-09 08:10:34 -04:00
this . client = client ;
2016-05-18 19:59:24 +10:00
this . method = builder . _method || 'select' ;
this . options = builder . _options ;
this . single = builder . _single ;
this . timeout = builder . _timeout || false ;
2016-05-26 11:06:33 -07:00
this . cancelOnTimeout = builder . _cancelOnTimeout || false ;
2016-05-18 19:59:24 +10:00
this . grouped = groupBy ( builder . _statements , 'grouping' ) ;
2018-07-09 08:10:34 -04:00
this . formatter = client . formatter ( builder ) ;
2015-05-09 13:58:18 -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' ,
2015-05-09 13:58:18 -04:00
] ;
assign ( QueryCompiler . prototype , {
// Used when the insert call is empty.
_emptyInsertValue : 'default values' ,
// 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 ;
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 ,
2017-09-27 13:12:40 +03:00
bindings : this . formatter . bindings || [ ] ,
2018-07-09 08:10:34 -04:00
_ _knexQueryUid : uuid . v4 ( ) ,
2015-05-09 13:58:18 -04: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 ` +
2018-07-09 08:10:34 -04:00
` ${ method . toUpperCase ( ) } 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 ;
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 ;
2015-05-09 13:58:18 -04:00
} ,
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
} ;
} ,
// 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 || [ ] ;
2016-09-13 12:14:04 +02:00
let sql = this . with ( ) + ` insert into ${ this . tableName } ` ;
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 ) {
2018-07-09 08:10:34 -04:00
sql += ` ( ${ this . formatter . columnize ( insertData . columns ) } ` ;
sql += ') values (' ;
let i = - 1 ;
2015-05-09 13:58:18 -04:00
while ( ++ i < insertData . values . length ) {
2018-07-09 08:10:34 -04:00
if ( i !== 0 ) sql += '), (' ;
sql += this . formatter . parameterize (
insertData . values [ i ] ,
this . client . valueForUndefined
) ;
2015-05-09 13:58:18 -04:00
}
sql += ')' ;
} 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 ;
} ,
// 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 } ` : '' )
) ;
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 ( ) {
2016-05-17 01:01:34 +10:00
let distinct = false ;
2018-07-09 08:10:34 -04:00
if ( this . onlyUnions ( ) ) return '' ;
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 ] ;
2018-07-09 08:10:34 -04:00
if ( stmt . distinct ) distinct = true ;
2015-05-09 13:58:18 -04:00
if ( stmt . type === 'aggregate' ) {
2018-07-09 08:10:34 -04:00
sql . push ( this . aggregate ( stmt ) ) ;
} else if ( stmt . type === 'aggregateRaw' ) {
sql . push ( this . aggregateRaw ( stmt ) ) ;
} else if ( stmt . value && stmt . value . length > 0 ) {
sql . push ( this . formatter . columnize ( stmt . value ) ) ;
2015-05-09 13:58:18 -04:00
}
}
}
if ( sql . length === 0 ) sql = [ '*' ] ;
2018-07-09 08:10:34 -04:00
return (
` select ${ distinct ? 'distinct ' : '' } ` +
sql . join ( ', ' ) +
( this . tableName
? ` from ${ this . single . only ? 'only ' : '' } ${ this . tableName } `
: '' )
) ;
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 ' : '' ;
2018-07-09 08:10:34 -04:00
const wrap = ( identifier ) => this . formatter . wrap ( identifier ) ;
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 ) ) {
return aggregateArray ( value ) ;
}
if ( typeof value === 'object' ) {
const keys = Object . keys ( value ) ;
const alias = keys [ 0 ] ;
const column = value [ alias ] ;
if ( Array . isArray ( column ) ) {
return aggregateArray ( column , alias ) ;
}
return aggregateString ( column , alias ) ;
}
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 ' ) ;
2015-05-09 13:58:18 -04:00
if ( splitOn !== - 1 ) {
2018-03-12 22:42:43 +01:00
const column = value . slice ( 0 , splitOn ) ;
const alias = value . slice ( splitOn + 4 ) ;
return aggregateString ( column , alias ) ;
2015-05-09 13:58:18 -04:00
}
2018-03-12 22:42:43 +01:00
return aggregateString ( value ) ;
} ,
aggregate ( stmt ) {
return this . _aggregate ( stmt ) ;
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 ' : '' ;
return ` ${ stmt . method } ( ${ distinct + this . formatter . unwrapRaw ( stmt . value ) } ) ` ;
} ,
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 ] ;
const table = join . schema ? ` ${ join . schema } . ${ join . table } ` : join . table ;
2018-07-09 08:10:34 -04:00
if ( i > 0 ) sql += ' ' ;
2015-05-09 13:58:18 -04:00
if ( join . joinType === 'raw' ) {
2018-07-09 08:10:34 -04:00
sql += this . formatter . unwrapRaw ( join . table ) ;
2015-05-09 13:58:18 -04:00
} else {
2018-07-09 08:10:34 -04:00
sql += join . joinType + ' join ' + this . formatter . wrap ( table ) ;
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
}
2016-05-17 01:01:34 +10:00
const val = this [ clause . type ] . call ( this , clause ) ;
2016-05-11 15:26:53 +02:00
if ( val ) {
sql += val ;
}
2015-05-09 13:58:18 -04:00
}
}
}
return sql ;
} ,
2016-09-19 18:20:12 +02:00
onBetween ( statement ) {
2018-07-09 08:10:34 -04:00
return (
this . formatter . wrap ( statement . column ) +
' ' +
this . _not ( statement , 'between' ) +
' ' +
map ( statement . value , bind ( this . formatter . parameter , this . formatter ) ) . join (
' and '
)
) ;
2016-09-19 18:20:12 +02:00
} ,
onNull ( statement ) {
2018-07-09 08:10:34 -04:00
return (
this . formatter . wrap ( statement . column ) +
' is ' +
this . _not ( statement , 'null' )
) ;
2016-09-19 18:20:12 +02:00
} ,
onExists ( statement ) {
2018-07-09 08:10:34 -04:00
return (
this . _not ( statement , 'exists' ) +
' (' +
this . formatter . rawOrFn ( statement . value ) +
')'
) ;
2016-09-19 18:20:12 +02:00
} ,
onIn ( statement ) {
if ( Array . isArray ( statement . column ) ) return this . multiOnIn ( statement ) ;
2018-07-09 08:10:34 -04:00
return (
this . formatter . wrap ( statement . column ) +
' ' +
this . _not ( statement , 'in ' ) +
this . wrap ( this . formatter . parameterize ( statement . value ) )
) ;
2016-09-19 18:20:12 +02:00
} ,
multiOnIn ( statement ) {
2018-07-09 08:10:34 -04:00
let i = - 1 ,
sql = ` ( ${ this . formatter . columnize ( statement . column ) } ) ` ;
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 += '),(' ;
sql += this . formatter . parameterize ( statement . value [ i ] ) ;
2016-09-19 18:20:12 +02:00
}
2018-07-09 08:10:34 -04:00
return sql + '))' ;
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 (
stmt . hasOwnProperty ( 'value' ) &&
helpers . containsUndefined ( stmt . value )
) {
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 ( ' ' ) : '' ;
} ,
2016-05-06 13:28:50 +10:00
group ( ) {
2015-05-09 13:58:18 -04:00
return this . _groupsOrders ( 'group' ) ;
} ,
2016-05-06 13:28:50 +10:00
order ( ) {
2015-05-09 13:58:18 -04:00
return this . _groupsOrders ( 'order' ) ;
} ,
// 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 ( ' ' ) : '' ;
} ,
2016-09-19 18:20:12 +02:00
havingRaw ( statement ) {
return this . _not ( statement , '' ) + this . formatter . unwrapRaw ( statement . value ) ;
} ,
havingWrapped ( statement ) {
2018-07-09 08:10:34 -04:00
const val = this . formatter . rawOrFn ( statement . value , 'where' ) ;
return ( val && this . _not ( statement , '' ) + '(' + val . slice ( 6 ) + ')' ) || '' ;
2016-09-19 18:20:12 +02:00
} ,
havingBasic ( statement ) {
2018-07-09 08:10:34 -04:00
return (
this . _not ( statement , '' ) +
this . formatter . wrap ( statement . column ) +
' ' +
this . formatter . operator ( statement . operator ) +
' ' +
this . formatter . parameter ( statement . value )
) ;
2016-09-19 18:20:12 +02:00
} ,
havingNull ( statement ) {
2018-07-09 08:10:34 -04:00
return (
this . formatter . wrap ( statement . column ) +
' is ' +
this . _not ( statement , 'null' )
) ;
2016-09-19 18:20:12 +02:00
} ,
havingExists ( statement ) {
2018-07-09 08:10:34 -04:00
return (
this . _not ( statement , 'exists' ) +
' (' +
this . formatter . rawOrFn ( statement . value ) +
')'
) ;
2016-09-19 18:20:12 +02:00
} ,
havingBetween ( statement ) {
2018-07-09 08:10:34 -04:00
return (
this . formatter . wrap ( statement . column ) +
' ' +
this . _not ( statement , 'between' ) +
' ' +
map ( statement . value , bind ( this . formatter . parameter , this . formatter ) ) . join (
' and '
)
) ;
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 (
this . formatter . wrap ( statement . column ) +
' ' +
this . _not ( statement , 'in ' ) +
this . wrap ( this . formatter . parameterize ( statement . value ) )
) ;
2016-09-19 18:20:12 +02:00
} ,
multiHavingIn ( statement ) {
2018-07-09 08:10:34 -04:00
let i = - 1 ,
sql = ` ( ${ this . formatter . columnize ( statement . column ) } ) ` ;
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 += '),(' ;
sql += this . formatter . parameterize ( statement . value [ i ] ) ;
2016-09-19 18:20:12 +02:00
}
2018-07-09 08:10:34 -04:00
return sql + '))' ;
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 + ' ' ;
2016-05-17 01:01:34 +10:00
const statement = this . formatter . rawOrFn ( union . value ) ;
2015-05-09 13:58:18 -04:00
if ( statement ) {
if ( union . wrap ) sql += '(' ;
sql += statement ;
if ( union . wrap ) sql += ')' ;
}
}
return sql ;
} ,
// 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 ( ) {
2018-07-09 08:10:34 -04:00
return ! this . grouped . columns && this . grouped . union && ! this . tableName ;
2015-05-09 13:58:18 -04:00
} ,
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 '' ;
2016-05-17 01:01:34 +10:00
return ` limit ${ this . formatter . parameter ( this . single . limit ) } ` ;
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 '' ;
2016-05-17 01:01:34 +10:00
return ` offset ${ this . formatter . parameter ( this . single . offset ) } ` ;
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 ( ) ;
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
` delete from ${ this . single . only ? 'only ' : '' } ${ tableName } ` +
2018-07-09 08:10:34 -04:00
( wheres ? ` ${ wheres } ` : '' )
) ;
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 } ` ;
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
}
} ,
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 = '' ;
2016-05-11 15:26:53 +02:00
wrapJoin . clauses . forEach ( function ( wrapClause , ii ) {
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 ;
}
} ) ;
if ( sql . length ) {
2016-05-17 01:01:34 +10:00
return ` ( ${ sql } ) ` ;
2016-05-11 15:26:53 +02:00
}
return '' ;
} ,
onBasic ( clause ) {
2016-05-17 01:01:34 +10:00
return (
2018-07-09 08:10:34 -04:00
this . formatter . wrap ( clause . column ) +
' ' +
this . formatter . operator ( clause . operator ) +
' ' +
2016-05-17 01:01:34 +10:00
this . formatter . wrap ( clause . value )
) ;
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 (
this . formatter . wrap ( clause . column ) +
' ' +
this . formatter . operator ( clause . operator ) +
' ' +
this . formatter . parameter ( clause . value )
) ;
} ,
2016-05-11 15:26:53 +02:00
onRaw ( clause ) {
return this . formatter . unwrapRaw ( clause . value ) ;
} ,
2016-05-11 16:22:15 +02:00
onUsing ( clause ) {
2018-08-29 19:43:45 +02:00
return '(' + this . formatter . columnize ( clause . column ) + ')' ;
2016-05-11 16:22:15 +02:00
} ,
2015-05-09 13:58:18 -04:00
// Where Clause
// ------
2016-05-06 13:28:50 +10:00
whereIn ( statement ) {
2018-02-22 07:51:09 +11:00
let columns = null ;
if ( Array . isArray ( statement . column ) ) {
2018-07-09 08:10:34 -04:00
columns = ` ( ${ this . formatter . columnize ( statement . column ) } ) ` ;
2018-02-22 07:51:09 +11:00
} else {
columns = this . formatter . wrap ( statement . column ) ;
2015-05-09 13:58:18 -04:00
}
2018-02-22 07:51:09 +11:00
const values = this . formatter . values ( statement . value ) ;
return ` ${ columns } ${ this . _not ( statement , 'in ' ) } ${ values } ` ;
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 (
this . formatter . wrap ( statement . column ) +
' is ' +
this . _not ( statement , 'null' )
) ;
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 , '' ) +
this . formatter . wrap ( statement . column ) +
' ' +
this . formatter . operator ( statement . operator ) +
' ' +
2018-10-03 11:26:19 -04:00
( statement . asColumn
? this . formatter . wrap ( statement . value )
: this . formatter . parameter ( statement . value ) )
2018-07-09 08:10:34 -04:00
) ;
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' ) +
' (' +
this . formatter . rawOrFn ( statement . value ) +
')'
) ;
2015-05-09 13:58:18 -04:00
} ,
2016-05-06 13:28:50 +10:00
whereWrapped ( statement ) {
2018-07-09 08:10:34 -04:00
const val = this . formatter . rawOrFn ( statement . value , 'where' ) ;
return ( val && this . _not ( statement , '' ) + '(' + val . slice ( 6 ) + ')' ) || '' ;
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 (
this . formatter . wrap ( statement . column ) +
' ' +
this . _not ( statement , 'between' ) +
' ' +
map ( statement . value , bind ( this . formatter . parameter , this . formatter ) ) . join (
' and '
)
) ;
2015-05-09 13:58:18 -04:00
} ,
// Compiles a "whereRaw" query.
2016-05-06 13:28:50 +10:00
whereRaw ( statement ) {
2016-05-14 22:36:13 +02:00
return this . _not ( statement , '' ) + this . formatter . unwrapRaw ( statement . value ) ;
2015-05-09 13:58:18 -04:00
} ,
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 ;
} ,
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 ( ', ' ) } ` ;
2016-09-13 12:14:04 +02:00
} ,
withWrapped ( statement ) {
const val = this . formatter . rawOrFn ( statement . value ) ;
2018-07-09 08:10:34 -04:00
return (
( val &&
this . formatter . columnize ( statement . alias ) + ' as (' + val + ')' ) ||
''
) ;
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 ;
} ,
2016-03-08 08:41:13 +01:00
2016-05-06 13:28:50 +10:00
_prepInsert ( data ) {
2016-05-17 01:01:34 +10:00
const isRaw = this . formatter . rawOrFn ( data ) ;
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
} ;
} ,
// "Preps" the update.
2018-10-06 16:10:56 +02:00
_prepUpdate ( data = { } ) {
const { counter = { } } = this . single ;
for ( const column of keys ( counter ) ) {
//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 (
2017-07-26 11:36:50 +03:00
this . formatter . wrap ( columns [ i ] ) +
2018-07-09 08:10:34 -04:00
' = ' +
this . formatter . parameter ( data [ columns [ i ] ] )
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.' ,
] . join ( ' ' )
) ;
2018-02-15 13:28:35 +01:00
}
2015-05-09 13:58:18 -04:00
return vals ;
} ,
// 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 '' ;
2016-05-17 01:01:34 +10:00
const { formatter } = this ;
2018-07-09 08:10:34 -04:00
const sql = items . map ( ( item ) => {
const column =
item . value instanceof Raw
? formatter . unwrapRaw ( item . value )
: formatter . columnize ( item . value ) ;
const direction =
type === 'order' && item . type !== 'orderByRaw'
? ` ${ formatter . direction ( item . direction ) } `
: '' ;
2016-05-17 01:01:34 +10:00
return column + direction ;
2015-05-09 13:58:18 -04:00
} ) ;
return sql . length ? type + ' by ' + sql . join ( ', ' ) : '' ;
2018-07-09 08:10:34 -04:00
} ,
} ) ;
2015-05-09 13:58:18 -04:00
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' , {
2016-05-06 13:28:50 +10:00
get ( ) {
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
if ( tableName && schemaName ) tableName = ` ${ schemaName } . ${ tableName } ` ;
this . _tableName = tableName ? this . formatter . wrap ( tableName ) : '' ;
2015-05-09 13:58:18 -04:00
}
return this . _tableName ;
2018-07-09 08:10:34 -04:00
} ,
2015-05-09 13:58:18 -04:00
} ) ;
2019-06-04 00:37:17 +02:00
module . exports = QueryCompiler ;