knex/src/raw.js

162 lines
4.0 KiB
JavaScript
Raw Normal View History

2015-05-09 13:58:18 -04:00
// Raw
// -------
var inherits = require('inherits')
var EventEmitter = require('events').EventEmitter
2016-03-02 16:52:32 +01:00
import {assign, reduce, isPlainObject, isObject, isUndefined, isNumber} from 'lodash'
2015-05-09 13:58:18 -04:00
function Raw(client) {
this.client = client
this.sql = ''
this.bindings = []
this._cached = undefined
// Todo: Deprecate
this._wrappedBefore = undefined
this._wrappedAfter = undefined
2016-01-30 00:18:23 +01:00
this._debug = client && client.config && client.config.debug
2015-05-09 13:58:18 -04:00
}
inherits(Raw, EventEmitter)
assign(Raw.prototype, {
set: function(sql, bindings) {
2015-05-09 13:58:18 -04:00
this._cached = undefined
this.sql = sql
2016-03-02 16:52:32 +01:00
this.bindings = (isObject(bindings) || isUndefined(bindings)) ? bindings : [bindings]
2015-05-09 13:58:18 -04:00
return this
},
timeout: function(ms) {
if(isNumber(ms) && ms > 0) {
this._timeout = ms;
}
return this;
},
2015-05-09 13:58:18 -04:00
// Wraps the current sql with `before` and `after`.
wrap: function(before, after) {
this._cached = undefined
this._wrappedBefore = before
this._wrappedAfter = after
return this
},
// Calls `toString` on the Knex object.
toString: function() {
return this.toQuery()
},
// Returns the raw sql for the query.
toSQL: function() {
if (this._cached) return this._cached
if (Array.isArray(this.bindings)) {
this._cached = replaceRawArrBindings(this)
} else if (this.bindings && isPlainObject(this.bindings)) {
2015-05-09 13:58:18 -04:00
this._cached = replaceKeyBindings(this)
} else {
this._cached = {
method: 'raw',
sql: this.sql,
bindings: _.isUndefined(this.bindings) ? void 0 : [this.bindings]
2015-05-09 13:58:18 -04:00
}
}
if (this._wrappedBefore) {
this._cached.sql = this._wrappedBefore + this._cached.sql
}
if (this._wrappedAfter) {
this._cached.sql = this._cached.sql + this._wrappedAfter
}
this._cached.options = reduce(this._options, assign, {})
if(this._timeout) {
this._cached.timeout = this._timeout;
}
this._cached.bindings = this.client.prepBindings(this._cached.bindings || []);
2015-05-09 13:58:18 -04:00
return this._cached
}
})
function replaceRawArrBindings(raw) {
var expectedBindings = raw.bindings.length
var values = raw.bindings
var client = raw.client
var index = 0;
var bindings = []
var sql = raw.sql.replace(/\\?\?\??/g, function(match) {
if (match === '\\?') {
return match
}
2015-05-09 13:58:18 -04:00
var value = values[index++]
2015-05-09 13:58:18 -04:00
if (value && typeof value.toSQL === 'function') {
var bindingSQL = value.toSQL()
if (bindingSQL.bindings !== undefined) {
bindings = bindings.concat(bindingSQL.bindings)
}
2015-05-09 13:58:18 -04:00
return bindingSQL.sql
}
if (match === '??') {
2015-11-06 01:19:47 +02:00
return client.formatter().columnize(value)
2015-05-09 13:58:18 -04:00
}
bindings.push(value)
return '?'
})
if (expectedBindings !== index) {
throw new Error('Expected ' + expectedBindings + ' bindings, saw ' + index)
}
return {
method: 'raw',
sql: sql,
bindings: bindings
}
}
function replaceKeyBindings(raw) {
var values = raw.bindings
var client = raw.client
var sql = raw.sql, bindings = []
2016-03-07 16:01:02 +01:00
var regex = new RegExp('(\\:\\w+\\:?)', 'g')
sql = raw.sql.replace(regex, function(full) {
var key = full.trim();
2015-05-09 13:58:18 -04:00
var isIdentifier = key[key.length - 1] === ':'
var value = isIdentifier ? values[key.slice(1, -1)] : values[key.slice(1)]
if (value === undefined) {
return full;
}
2015-05-09 13:58:18 -04:00
if (value && typeof value.toSQL === 'function') {
var bindingSQL = value.toSQL()
if (bindingSQL.bindings !== undefined) {
bindings = bindings.concat(bindingSQL.bindings)
}
2015-05-09 13:58:18 -04:00
return full.replace(key, bindingSQL.sql)
}
if (isIdentifier) {
2015-11-06 01:19:47 +02:00
return full.replace(key, client.formatter().columnize(value))
2015-05-09 13:58:18 -04:00
}
bindings.push(value)
return full.replace(key, '?')
})
return {
method: 'raw',
sql: sql,
bindings: bindings
}
}
// Allow the `Raw` object to be utilized with full access to the relevant
// promise API.
require('./interface')(Raw)
module.exports = Raw