knex/src/raw.js

191 lines
4.1 KiB
JavaScript
Raw Normal View History

2015-05-09 13:58:18 -04:00
// Raw
// -------
import inherits from 'inherits';
import * as helpers from './helpers';
import { EventEmitter } from 'events';
2016-09-16 16:04:11 -04:00
import debug from 'debug'
2016-03-02 16:52:32 +01:00
import { assign, reduce, isPlainObject, isObject, isUndefined, isNumber } from 'lodash'
import Formatter from './formatter'
2015-05-09 13:58:18 -04:00
2016-11-22 21:58:21 +13:00
import uuid from 'uuid';
2016-06-17 09:19:20 -07:00
2016-09-16 16:04:11 -04:00
const debugBindings = debug('knex:bindings')
const fakeClient = {
Add queryContext to schema and query builders (#2314) * feat(query-builder): add hookContext for wrapIdentifier * refactor: use isUndefined * test(transaction): test passing of hookContext * feat(runnner): pass context to postProcessResponse * test(runner): test postProcessResponse for raw responses * test(raw): test passing of hookContext * feat: add hookContext to Raw and SchemaBuilder * test(transaction): fix test for hookContext * chore: fix lint error * fix: check for hookContext before calling it * test(transaction): fix hookContext test * chore: remove whitespace * test(hookContext): test cloning of context object * refactor: hookContext -> queryContext * minor: use more descriptive variable name i.e. refactor: `context` => `queryContext` * fix: remove unnecessary checks for query builder * fix(Raw): pass query builder to formatter * fix(SchemaCompiler): pass schema builder to formatter * refactor: add addQueryContext helper * feat: add queryContext to TableBuilder and ColumnBuilder * fix(TableCompiler): pass table builder to formatter * fix(ColumnCompiler): pass column builder to formatter * fix(pushQuery): fix passing builder to formatter * test(Schema|Table|ColumnCompiler): test passing queryContext * fix(SchemaCompiler): pass queryContext to TableCompiler * fix(TableCompiler): pass queryContext to ColumnCompiler * test: add queryContext tests for all schema dialects * test(TableCompiler): test overwriting queryContext from SchemaCompiler * test(Raw): test passing queryContext to wrapIdentifier * tests: run all the tests
2018-02-01 23:41:01 +01:00
formatter(builder) {
return new Formatter(fakeClient, builder)
}
}
function Raw(client = fakeClient) {
this.client = client
2015-05-09 13:58:18 -04:00
this.sql = ''
2015-05-09 13:58:18 -04:00
this.bindings = []
// Todo: Deprecate
this._wrappedBefore = undefined
this._wrappedAfter = undefined
this._debug = client && client.config && client.config.debug
2015-05-09 13:58:18 -04:00
}
inherits(Raw, EventEmitter)
assign(Raw.prototype, {
set(sql, bindings) {
this.sql = sql
2016-06-14 19:50:51 +10:00
this.bindings = (
(isObject(bindings) && !bindings.toSQL) ||
isUndefined(bindings)
) ? bindings : [bindings]
2015-05-09 13:58:18 -04:00
return this
},
2016-05-26 11:06:33 -07:00
timeout(ms, {cancel} = {}) {
if (isNumber(ms) && ms > 0) {
this._timeout = ms;
2016-05-26 11:06:33 -07:00
if (cancel) {
this.client.assertCanCancelQuery();
this._cancelOnTimeout = true;
2016-05-26 11:06:33 -07:00
}
}
return this;
},
2015-05-09 13:58:18 -04:00
// Wraps the current sql with `before` and `after`.
wrap(before, after) {
2015-05-09 13:58:18 -04:00
this._wrappedBefore = before
this._wrappedAfter = after
2015-05-09 13:58:18 -04:00
return this
},
// Calls `toString` on the Knex object.
toString() {
2015-05-09 13:58:18 -04:00
return this.toQuery()
},
// Returns the raw sql for the query.
toSQL(method, tz) {
let obj
Add queryContext to schema and query builders (#2314) * feat(query-builder): add hookContext for wrapIdentifier * refactor: use isUndefined * test(transaction): test passing of hookContext * feat(runnner): pass context to postProcessResponse * test(runner): test postProcessResponse for raw responses * test(raw): test passing of hookContext * feat: add hookContext to Raw and SchemaBuilder * test(transaction): fix test for hookContext * chore: fix lint error * fix: check for hookContext before calling it * test(transaction): fix hookContext test * chore: remove whitespace * test(hookContext): test cloning of context object * refactor: hookContext -> queryContext * minor: use more descriptive variable name i.e. refactor: `context` => `queryContext` * fix: remove unnecessary checks for query builder * fix(Raw): pass query builder to formatter * fix(SchemaCompiler): pass schema builder to formatter * refactor: add addQueryContext helper * feat: add queryContext to TableBuilder and ColumnBuilder * fix(TableCompiler): pass table builder to formatter * fix(ColumnCompiler): pass column builder to formatter * fix(pushQuery): fix passing builder to formatter * test(Schema|Table|ColumnCompiler): test passing queryContext * fix(SchemaCompiler): pass queryContext to TableCompiler * fix(TableCompiler): pass queryContext to ColumnCompiler * test: add queryContext tests for all schema dialects * test(TableCompiler): test overwriting queryContext from SchemaCompiler * test(Raw): test passing queryContext to wrapIdentifier * tests: run all the tests
2018-02-01 23:41:01 +01:00
const formatter = this.client.formatter(this)
2015-05-09 13:58:18 -04:00
if (Array.isArray(this.bindings)) {
obj = replaceRawArrBindings(this, formatter)
} else if (this.bindings && isPlainObject(this.bindings)) {
obj = replaceKeyBindings(this, formatter)
2015-05-09 13:58:18 -04:00
} else {
obj = {
2015-05-09 13:58:18 -04:00
method: 'raw',
sql: this.sql,
bindings: isUndefined(this.bindings) ? [] : [this.bindings]
2015-05-09 13:58:18 -04:00
}
}
2015-05-09 13:58:18 -04:00
if (this._wrappedBefore) {
obj.sql = this._wrappedBefore + obj.sql
2015-05-09 13:58:18 -04:00
}
if (this._wrappedAfter) {
obj.sql = obj.sql + this._wrappedAfter
2015-05-09 13:58:18 -04:00
}
obj.options = reduce(this._options, assign, {})
if (this._timeout) {
obj.timeout = this._timeout;
2016-05-26 11:06:33 -07:00
if (this._cancelOnTimeout) {
obj.cancelOnTimeout = this._cancelOnTimeout;
2016-05-26 11:06:33 -07:00
}
}
obj.bindings = obj.bindings || [];
if (helpers.containsUndefined(obj.bindings)) {
2016-09-16 16:04:11 -04:00
debugBindings(obj.bindings)
throw new Error(
`Undefined binding(s) detected when compiling RAW query: ` +
obj.sql
);
}
obj.__knexQueryUid = uuid.v4();
return obj
2015-05-09 13:58:18 -04:00
}
})
function replaceRawArrBindings(raw, formatter) {
const expectedBindings = raw.bindings.length
const values = raw.bindings
let index = 0;
const sql = raw.sql.replace(/\\?\?\??/g, function(match) {
if (match === '\\?') {
return match
}
const value = values[index++]
2015-05-09 13:58:18 -04:00
if (match === '??') {
return formatter.columnize(value)
2015-05-09 13:58:18 -04:00
}
return formatter.parameter(value)
2015-05-09 13:58:18 -04:00
})
if (expectedBindings !== index) {
throw new Error(`Expected ${expectedBindings} bindings, saw ${index}`)
2015-05-09 13:58:18 -04:00
}
return {
method: 'raw',
sql,
bindings: formatter.bindings
2015-05-09 13:58:18 -04:00
}
}
function replaceKeyBindings(raw, formatter) {
const values = raw.bindings
let { sql } = raw
2015-05-09 13:58:18 -04:00
const regex = /\\?(:(\w+):(?=::)|:(\w+):(?!:)|:(\w+))/g
sql = raw.sql.replace(regex, function(match, p1, p2, p3, p4) {
if (match !== p1) {
return p1
}
const part = p2 || p3 || p4
const key = match.trim();
const isIdentifier = key[key.length - 1] === ':'
const value = values[part]
if (value === undefined) {
if (values.hasOwnProperty(part)) {
formatter.bindings.push(value);
}
return match
}
2015-05-09 13:58:18 -04:00
if (isIdentifier) {
return match.replace(p1, formatter.columnize(value))
2015-05-09 13:58:18 -04:00
}
return match.replace(p1, formatter.parameter(value))
2015-05-09 13:58:18 -04:00
})
return {
method: 'raw',
sql,
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.
require('./interface')(Raw)
Add queryContext to schema and query builders (#2314) * feat(query-builder): add hookContext for wrapIdentifier * refactor: use isUndefined * test(transaction): test passing of hookContext * feat(runnner): pass context to postProcessResponse * test(runner): test postProcessResponse for raw responses * test(raw): test passing of hookContext * feat: add hookContext to Raw and SchemaBuilder * test(transaction): fix test for hookContext * chore: fix lint error * fix: check for hookContext before calling it * test(transaction): fix hookContext test * chore: remove whitespace * test(hookContext): test cloning of context object * refactor: hookContext -> queryContext * minor: use more descriptive variable name i.e. refactor: `context` => `queryContext` * fix: remove unnecessary checks for query builder * fix(Raw): pass query builder to formatter * fix(SchemaCompiler): pass schema builder to formatter * refactor: add addQueryContext helper * feat: add queryContext to TableBuilder and ColumnBuilder * fix(TableCompiler): pass table builder to formatter * fix(ColumnCompiler): pass column builder to formatter * fix(pushQuery): fix passing builder to formatter * test(Schema|Table|ColumnCompiler): test passing queryContext * fix(SchemaCompiler): pass queryContext to TableCompiler * fix(TableCompiler): pass queryContext to ColumnCompiler * test: add queryContext tests for all schema dialects * test(TableCompiler): test overwriting queryContext from SchemaCompiler * test(Raw): test passing queryContext to wrapIdentifier * tests: run all the tests
2018-02-01 23:41:01 +01:00
helpers.addQueryContext(Raw);
2015-05-09 13:58:18 -04:00
export default Raw