2015-05-09 13:58:18 -04:00
|
|
|
// Query Compiler
|
|
|
|
// -------
|
2016-05-17 01:01:34 +10:00
|
|
|
import * as helpers from '../helpers';
|
|
|
|
import Raw from '../raw';
|
|
|
|
import JoinClause from './joinclause';
|
2018-07-09 08:10:34 -04:00
|
|
|
import debug from 'debug';
|
2016-05-11 15:26:53 +02:00
|
|
|
|
2016-05-17 01:01:34 +10:00
|
|
|
import {
|
2018-07-09 08:10:34 -04:00
|
|
|
assign,
|
|
|
|
bind,
|
|
|
|
compact,
|
|
|
|
groupBy,
|
|
|
|
isEmpty,
|
|
|
|
isString,
|
|
|
|
isUndefined,
|
|
|
|
map,
|
|
|
|
omitBy,
|
|
|
|
reduce,
|
2016-05-17 01:01:34 +10:00
|
|
|
} from 'lodash';
|
2016-11-22 21:58:21 +13:00
|
|
|
import uuid from '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
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
// Compile the "counter".
|
2016-05-06 13:28:50 +10:00
|
|
|
counter() {
|
2016-05-17 01:01:34 +10:00
|
|
|
const { counter } = this.single;
|
|
|
|
const toUpdate = {};
|
2018-07-09 08:10:34 -04:00
|
|
|
toUpdate[counter.column] = this.client.raw(
|
|
|
|
this.formatter.wrap(counter.column) +
|
|
|
|
' ' +
|
|
|
|
(counter.symbol || '+') +
|
|
|
|
' ' +
|
|
|
|
counter.amount
|
|
|
|
);
|
2015-05-09 13:58:18 -04:00
|
|
|
this.single.update = toUpdate;
|
|
|
|
return this.update();
|
|
|
|
},
|
|
|
|
|
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-02 22:49:12 -04:00
|
|
|
onColumn(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) +
|
|
|
|
' ' +
|
|
|
|
this.formatter.parameter(statement.value)
|
|
|
|
);
|
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;
|
|
|
|
while (++i < withs.length) {
|
2018-07-09 08:10:34 -04:00
|
|
|
const stmt = withs[i];
|
|
|
|
const val = this[stmt.type](stmt);
|
2016-09-13 12:14:04 +02:00
|
|
|
sql.push(val);
|
|
|
|
}
|
|
|
|
return 'with ' + sql.join(', ') + ' ';
|
|
|
|
},
|
|
|
|
|
|
|
|
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.
|
2016-05-06 13:28:50 +10:00
|
|
|
_prepUpdate(data) {
|
2018-07-09 08:10:34 -04:00
|
|
|
data = omitBy(data, isUndefined);
|
|
|
|
const vals = [];
|
|
|
|
const columns = Object.keys(data);
|
|
|
|
let i = -1;
|
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
|
|
|
});
|
|
|
|
|
2016-05-17 01:01:34 +10:00
|
|
|
export default QueryCompiler;
|