2015-05-09 13:58:18 -04:00
|
|
|
// Raw
|
|
|
|
// -------
|
2020-10-05 23:59:12 +03:00
|
|
|
const { inherits } = require('util');
|
2019-06-04 00:37:17 +02:00
|
|
|
const { EventEmitter } = require('events');
|
|
|
|
const debug = require('debug');
|
2020-04-18 20:41:23 +03:00
|
|
|
const assign = require('lodash/assign');
|
|
|
|
const isPlainObject = require('lodash/isPlainObject');
|
|
|
|
const reduce = require('lodash/reduce');
|
2020-12-20 01:43:36 +02:00
|
|
|
|
2020-12-28 16:55:08 +02:00
|
|
|
const helpers = require('./util/helpers');
|
2019-06-04 00:37:17 +02:00
|
|
|
const saveAsyncStack = require('./util/save-async-stack');
|
2020-10-30 14:21:17 +02:00
|
|
|
const { nanoid } = require('./util/nanoid');
|
2020-10-05 21:29:39 +03:00
|
|
|
const { isNumber, isObject } = require('./util/is');
|
2020-12-28 16:55:08 +02:00
|
|
|
const {
|
|
|
|
augmentWithBuilderInterface,
|
|
|
|
} = require('./builder-interface-augmenter');
|
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
|
|
|
|
2018-10-04 00:25:05 +03:00
|
|
|
function Raw(client) {
|
2018-07-09 08:10:34 -04:00
|
|
|
this.client = client;
|
2015-05-09 13:58:18 -04:00
|
|
|
|
2018-07-09 08:10:34 -04:00
|
|
|
this.sql = '';
|
|
|
|
this.bindings = [];
|
2015-05-09 13:58:18 -04:00
|
|
|
|
|
|
|
// Todo: Deprecate
|
2018-07-09 08:10:34 -04:00
|
|
|
this._wrappedBefore = undefined;
|
|
|
|
this._wrappedAfter = undefined;
|
2018-04-25 23:11:24 +02:00
|
|
|
if (client && client.config) {
|
2018-07-09 08:10:34 -04:00
|
|
|
this._debug = client.config.debug;
|
|
|
|
saveAsyncStack(this, 4);
|
2018-04-25 23:11:24 +02:00
|
|
|
}
|
2015-05-09 13:58:18 -04:00
|
|
|
}
|
2019-06-04 00:37:17 +02:00
|
|
|
|
2018-07-09 08:10:34 -04:00
|
|
|
inherits(Raw, EventEmitter);
|
2015-05-09 13:58:18 -04:00
|
|
|
|
|
|
|
assign(Raw.prototype, {
|
2016-05-17 01:01:34 +10:00
|
|
|
set(sql, bindings) {
|
2018-07-09 08:10:34 -04:00
|
|
|
this.sql = sql;
|
|
|
|
this.bindings =
|
2020-04-18 20:41:23 +03:00
|
|
|
(isObject(bindings) && !bindings.toSQL) || bindings === undefined
|
2018-07-09 08:10:34 -04:00
|
|
|
? bindings
|
|
|
|
: [bindings];
|
2016-01-30 14:52:07 +01:00
|
|
|
|
2018-07-09 08:10:34 -04:00
|
|
|
return this;
|
2015-05-09 13:58:18 -04:00
|
|
|
},
|
|
|
|
|
2018-07-09 08:10:34 -04:00
|
|
|
timeout(ms, { cancel } = {}) {
|
2016-09-12 18:01:47 -04:00
|
|
|
if (isNumber(ms) && ms > 0) {
|
2016-03-08 13:07:50 +01:00
|
|
|
this._timeout = ms;
|
2016-05-26 11:06:33 -07:00
|
|
|
if (cancel) {
|
2016-05-27 14:47:12 -07:00
|
|
|
this.client.assertCanCancelQuery();
|
|
|
|
this._cancelOnTimeout = true;
|
2016-05-26 11:06:33 -07:00
|
|
|
}
|
2016-03-08 13:07:50 +01:00
|
|
|
}
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
2015-05-09 13:58:18 -04:00
|
|
|
// Wraps the current sql with `before` and `after`.
|
2016-05-17 01:01:34 +10:00
|
|
|
wrap(before, after) {
|
2018-07-09 08:10:34 -04:00
|
|
|
this._wrappedBefore = before;
|
|
|
|
this._wrappedAfter = after;
|
|
|
|
return this;
|
2015-05-09 13:58:18 -04:00
|
|
|
},
|
|
|
|
|
|
|
|
// Calls `toString` on the Knex object.
|
2016-05-17 01:01:34 +10:00
|
|
|
toString() {
|
2018-07-09 08:10:34 -04:00
|
|
|
return this.toQuery();
|
2015-05-09 13:58:18 -04:00
|
|
|
},
|
|
|
|
|
|
|
|
// Returns the raw sql for the query.
|
2016-05-17 01:01:34 +10:00
|
|
|
toSQL(method, tz) {
|
2018-07-09 08:10:34 -04:00
|
|
|
let obj;
|
|
|
|
const formatter = this.client.formatter(this);
|
2016-09-12 18:01:47 -04:00
|
|
|
|
2015-05-09 13:58:18 -04:00
|
|
|
if (Array.isArray(this.bindings)) {
|
2018-07-09 08:10:34 -04:00
|
|
|
obj = replaceRawArrBindings(this, formatter);
|
2015-11-06 16:17:50 -07:00
|
|
|
} else if (this.bindings && isPlainObject(this.bindings)) {
|
2018-07-09 08:10:34 -04:00
|
|
|
obj = replaceKeyBindings(this, formatter);
|
2015-05-09 13:58:18 -04:00
|
|
|
} else {
|
2016-09-12 18:01:47 -04:00
|
|
|
obj = {
|
2015-05-09 13:58:18 -04:00
|
|
|
method: 'raw',
|
|
|
|
sql: this.sql,
|
2020-04-18 20:41:23 +03:00
|
|
|
bindings: this.bindings === undefined ? [] : [this.bindings],
|
2018-07-09 08:10:34 -04:00
|
|
|
};
|
2015-05-09 13:58:18 -04:00
|
|
|
}
|
2016-09-12 18:01:47 -04:00
|
|
|
|
2015-05-09 13:58:18 -04:00
|
|
|
if (this._wrappedBefore) {
|
2018-07-09 08:10:34 -04:00
|
|
|
obj.sql = this._wrappedBefore + obj.sql;
|
2015-05-09 13:58:18 -04:00
|
|
|
}
|
|
|
|
if (this._wrappedAfter) {
|
2018-07-09 08:10:34 -04:00
|
|
|
obj.sql = obj.sql + this._wrappedAfter;
|
2015-05-09 13:58:18 -04:00
|
|
|
}
|
2016-09-12 18:01:47 -04:00
|
|
|
|
2018-07-09 08:10:34 -04:00
|
|
|
obj.options = reduce(this._options, assign, {});
|
2016-09-12 18:01:47 -04:00
|
|
|
|
|
|
|
if (this._timeout) {
|
|
|
|
obj.timeout = this._timeout;
|
2016-05-26 11:06:33 -07:00
|
|
|
if (this._cancelOnTimeout) {
|
2016-09-12 18:01:47 -04:00
|
|
|
obj.cancelOnTimeout = this._cancelOnTimeout;
|
2016-05-26 11:06:33 -07:00
|
|
|
}
|
2016-03-08 13:07:50 +01:00
|
|
|
}
|
2016-09-12 18:01:47 -04:00
|
|
|
|
|
|
|
obj.bindings = obj.bindings || [];
|
|
|
|
if (helpers.containsUndefined(obj.bindings)) {
|
2019-10-06 20:21:32 +02:00
|
|
|
const undefinedBindingIndices = helpers.getUndefinedIndices(
|
|
|
|
this.bindings
|
|
|
|
);
|
2018-07-09 08:10:34 -04:00
|
|
|
debugBindings(obj.bindings);
|
2016-09-12 18:01:47 -04:00
|
|
|
throw new Error(
|
2019-09-04 23:59:04 +03:00
|
|
|
`Undefined binding(s) detected for keys [${undefinedBindingIndices}] when compiling RAW query: ${obj.sql}`
|
2016-09-12 18:01:47 -04:00
|
|
|
);
|
2016-03-15 21:33:39 +01:00
|
|
|
}
|
2016-09-12 18:01:47 -04:00
|
|
|
|
2020-10-30 14:21:17 +02:00
|
|
|
obj.__knexQueryUid = nanoid();
|
2016-09-12 18:01:47 -04:00
|
|
|
|
2018-07-09 08:10:34 -04:00
|
|
|
return obj;
|
|
|
|
},
|
|
|
|
});
|
2015-05-09 13:58:18 -04:00
|
|
|
|
2016-09-12 18:01:47 -04:00
|
|
|
function replaceRawArrBindings(raw, formatter) {
|
2018-07-09 08:10:34 -04:00
|
|
|
const expectedBindings = raw.bindings.length;
|
|
|
|
const values = raw.bindings;
|
2016-05-18 19:59:24 +10:00
|
|
|
let index = 0;
|
2015-11-13 21:01:01 +02:00
|
|
|
|
2020-04-19 00:40:23 +02:00
|
|
|
const sql = raw.sql.replace(/\\?\?\??/g, function (match) {
|
2015-11-13 21:01:01 +02:00
|
|
|
if (match === '\\?') {
|
2018-07-09 08:10:34 -04:00
|
|
|
return match;
|
2015-11-13 21:01:01 +02:00
|
|
|
}
|
|
|
|
|
2018-07-09 08:10:34 -04:00
|
|
|
const value = values[index++];
|
2016-03-08 08:41:13 +01:00
|
|
|
|
2015-05-09 13:58:18 -04:00
|
|
|
if (match === '??') {
|
2018-07-09 08:10:34 -04:00
|
|
|
return formatter.columnize(value);
|
2015-05-09 13:58:18 -04:00
|
|
|
}
|
2018-07-09 08:10:34 -04:00
|
|
|
return formatter.parameter(value);
|
|
|
|
});
|
2015-05-09 13:58:18 -04:00
|
|
|
|
|
|
|
if (expectedBindings !== index) {
|
2018-07-09 08:10:34 -04:00
|
|
|
throw new Error(`Expected ${expectedBindings} bindings, saw ${index}`);
|
2015-05-09 13:58:18 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
method: 'raw',
|
2016-05-17 01:01:34 +10:00
|
|
|
sql,
|
2018-07-09 08:10:34 -04:00
|
|
|
bindings: formatter.bindings,
|
|
|
|
};
|
2015-05-09 13:58:18 -04:00
|
|
|
}
|
|
|
|
|
2016-09-12 18:01:47 -04:00
|
|
|
function replaceKeyBindings(raw, formatter) {
|
2018-07-09 08:10:34 -04:00
|
|
|
const values = raw.bindings;
|
|
|
|
const regex = /\\?(:(\w+):(?=::)|:(\w+):(?!:)|:(\w+))/g;
|
2018-10-22 13:38:17 +02:00
|
|
|
|
2020-04-19 00:40:23 +02:00
|
|
|
const sql = raw.sql.replace(regex, function (match, p1, p2, p3, p4) {
|
2017-02-05 12:22:26 -06:00
|
|
|
if (match !== p1) {
|
2018-07-09 08:10:34 -04:00
|
|
|
return p1;
|
2016-07-19 11:42:49 -06:00
|
|
|
}
|
|
|
|
|
2018-07-09 08:10:34 -04:00
|
|
|
const part = p2 || p3 || p4;
|
2017-02-05 12:22:26 -06:00
|
|
|
const key = match.trim();
|
2018-07-09 08:10:34 -04:00
|
|
|
const isIdentifier = key[key.length - 1] === ':';
|
|
|
|
const value = values[part];
|
2016-09-12 18:01:47 -04:00
|
|
|
|
2016-03-07 16:31:39 +01:00
|
|
|
if (value === undefined) {
|
2019-07-10 20:47:56 +02:00
|
|
|
if (Object.prototype.hasOwnProperty.call(values, part)) {
|
2017-02-05 12:22:26 -06:00
|
|
|
formatter.bindings.push(value);
|
|
|
|
}
|
|
|
|
|
2018-07-09 08:10:34 -04:00
|
|
|
return match;
|
2016-03-07 16:31:39 +01:00
|
|
|
}
|
2016-09-12 18:01:47 -04:00
|
|
|
|
2015-05-09 13:58:18 -04:00
|
|
|
if (isIdentifier) {
|
2018-07-09 08:10:34 -04:00
|
|
|
return match.replace(p1, formatter.columnize(value));
|
2015-05-09 13:58:18 -04:00
|
|
|
}
|
2017-02-05 12:22:26 -06:00
|
|
|
|
2018-07-09 08:10:34 -04:00
|
|
|
return match.replace(p1, formatter.parameter(value));
|
|
|
|
});
|
2015-05-09 13:58:18 -04:00
|
|
|
|
|
|
|
return {
|
|
|
|
method: 'raw',
|
2016-05-17 01:01:34 +10:00
|
|
|
sql,
|
2018-07-09 08:10:34 -04:00
|
|
|
bindings: formatter.bindings,
|
|
|
|
};
|
2015-05-09 13:58:18 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Allow the `Raw` object to be utilized with full access to the relevant
|
|
|
|
// promise API.
|
2020-12-28 16:55:08 +02:00
|
|
|
augmentWithBuilderInterface(Raw);
|
2018-02-01 23:41:01 +01:00
|
|
|
helpers.addQueryContext(Raw);
|
2015-05-09 13:58:18 -04:00
|
|
|
|
2019-06-04 00:37:17 +02:00
|
|
|
module.exports = Raw;
|