mirror of
https://github.com/knex/knex.git
synced 2025-12-27 15:08:47 +00:00
Build native sql for a dialect without to string (#2237)
* Exposed also positionBinding method to client base class and refactored calls to em * Added toSQL().toNative() getter which returns dialect specfic sql and bindings. * Refactored toSQL method implementation
This commit is contained in:
parent
491cc788c2
commit
15639d03db
@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
if [ -n "$(which docker)" ]; then
|
||||
if [ -n "$(docker info)" ]; then
|
||||
DOCKER_IMAGES=("mysql:5.7" "postgres:9.6")
|
||||
for image in ${DOCKER_IMAGES[@]}; do
|
||||
if [ -z "$(docker images -q ${image})" ]; then
|
||||
|
||||
@ -133,6 +133,7 @@ assign(Client.prototype, {
|
||||
|
||||
query(connection, obj) {
|
||||
if (typeof obj === 'string') obj = {sql: obj}
|
||||
obj.sql = this.positionBindings(obj.sql);
|
||||
obj.bindings = this.prepBindings(obj.bindings)
|
||||
debugQuery(obj.sql)
|
||||
this.emit('query', assign({__knexUid: connection.__knexUid}, obj))
|
||||
@ -146,9 +147,10 @@ assign(Client.prototype, {
|
||||
|
||||
stream(connection, obj, stream, options) {
|
||||
if (typeof obj === 'string') obj = {sql: obj}
|
||||
obj.sql = this.positionBindings(obj.sql);
|
||||
obj.bindings = this.prepBindings(obj.bindings)
|
||||
this.emit('query', assign({__knexUid: connection.__knexUid}, obj))
|
||||
debugQuery(obj.sql)
|
||||
obj.bindings = this.prepBindings(obj.bindings)
|
||||
debugBindings(obj.bindings)
|
||||
return this._stream(connection, obj, stream, options)
|
||||
},
|
||||
@ -157,6 +159,10 @@ assign(Client.prototype, {
|
||||
return bindings;
|
||||
},
|
||||
|
||||
positionBindings(sql) {
|
||||
return sql;
|
||||
},
|
||||
|
||||
wrapIdentifier(value) {
|
||||
if (this.config.wrapIdentifier) {
|
||||
return this.config.wrapIdentifier(value, this.wrapIdentifierImpl);
|
||||
|
||||
@ -114,8 +114,6 @@ assign(Client_MSSQL.prototype, {
|
||||
_stream(connection, obj, stream, options) {
|
||||
options = options || {}
|
||||
if (!obj || typeof obj === 'string') obj = {sql: obj}
|
||||
// convert ? params into positional bindings (@p1)
|
||||
obj.sql = this.positionBindings(obj.sql);
|
||||
return new Promise((resolver, rejecter) => {
|
||||
stream.on('error', (err) => {
|
||||
rejecter(err)
|
||||
@ -142,8 +140,6 @@ assign(Client_MSSQL.prototype, {
|
||||
_query(connection, obj) {
|
||||
const client = this;
|
||||
if (!obj || typeof obj === 'string') obj = {sql: obj}
|
||||
// convert ? params into positional bindings (@p1)
|
||||
obj.sql = this.positionBindings(obj.sql);
|
||||
return new Promise((resolver, rejecter) => {
|
||||
const { sql } = obj
|
||||
if (!sql) return resolver()
|
||||
|
||||
@ -116,7 +116,6 @@ assign(Client_Oracle.prototype, {
|
||||
},
|
||||
|
||||
_stream(connection, obj, stream, options) {
|
||||
obj.sql = this.positionBindings(obj.sql);
|
||||
return new Promise(function (resolver, rejecter) {
|
||||
stream.on('error', (err) => {
|
||||
if (isConnectionError(err)) {
|
||||
@ -134,9 +133,6 @@ assign(Client_Oracle.prototype, {
|
||||
// and any other necessary prep work.
|
||||
_query(connection, obj) {
|
||||
|
||||
// convert ? params into positional bindings (:1)
|
||||
obj.sql = this.positionBindings(obj.sql);
|
||||
|
||||
if (!obj.sql) throw new Error('The query is empty');
|
||||
|
||||
return connection.executeAsync(obj.sql, obj.bindings).then(function(response) {
|
||||
|
||||
@ -233,10 +233,6 @@ Client_Oracledb.prototype.destroyRawConnection = function(connection) {
|
||||
// Runs the query on the specified connection, providing the bindings
|
||||
// and any other necessary prep work.
|
||||
Client_Oracledb.prototype._query = function(connection, obj) {
|
||||
// Convert ? params into positional bindings (:1)
|
||||
obj.sql = this.positionBindings(obj.sql);
|
||||
obj.bindings = this.prepBindings(obj.bindings) || [];
|
||||
|
||||
return new Promise(function(resolver, rejecter) {
|
||||
if (!obj.sql) {
|
||||
return rejecter(new Error('The query is empty'));
|
||||
|
||||
@ -173,7 +173,7 @@ assign(Client_PG.prototype, {
|
||||
|
||||
_stream(connection, obj, stream, options) {
|
||||
const PGQueryStream = process.browser ? undefined : require('pg-query-stream');
|
||||
const sql = obj.sql = this.positionBindings(obj.sql)
|
||||
const sql = obj.sql;
|
||||
return new Promise(function(resolver, rejecter) {
|
||||
const queryStream = connection.query(new PGQueryStream(sql, obj.bindings, options));
|
||||
queryStream.on('error', function(error) { stream.emit('error', error); });
|
||||
@ -192,7 +192,7 @@ assign(Client_PG.prototype, {
|
||||
// Runs the query on the specified connection, providing the bindings
|
||||
// and any other necessary prep work.
|
||||
_query(connection, obj) {
|
||||
let sql = obj.sql = this.positionBindings(obj.sql)
|
||||
let sql = obj.sql;
|
||||
if (obj.options) sql = extend({text: sql}, obj.options);
|
||||
return new Promise(function(resolver, rejecter) {
|
||||
connection.query(sql, obj.bindings, function(err, response) {
|
||||
|
||||
@ -44,36 +44,42 @@ assign(QueryCompiler.prototype, {
|
||||
this._undefinedInWhereClause = false;
|
||||
|
||||
method = method || this.method
|
||||
let val = this[method]()
|
||||
const defaults = {
|
||||
const val = this[method]() || '';
|
||||
|
||||
const query = {
|
||||
method,
|
||||
options: reduce(this.options, assign, {}),
|
||||
timeout: this.timeout,
|
||||
cancelOnTimeout: this.cancelOnTimeout,
|
||||
bindings: this.formatter.bindings,
|
||||
__knexQueryUid: uuid.v4()
|
||||
bindings: this.formatter.bindings || [],
|
||||
__knexQueryUid: uuid.v4(),
|
||||
toNative: () => ({
|
||||
sql: this.client.positionBindings(query.sql),
|
||||
bindings: this.client.prepBindings(query.bindings)
|
||||
})
|
||||
};
|
||||
if (isString(val)) {
|
||||
val = {sql: val};
|
||||
}
|
||||
|
||||
defaults.bindings = defaults.bindings || [];
|
||||
if (isString(val)) {
|
||||
query.sql = val;
|
||||
} else {
|
||||
assign(query, val);
|
||||
}
|
||||
|
||||
if (method === 'select' || method === 'first') {
|
||||
if(this.single.as) {
|
||||
defaults.as = this.single.as;
|
||||
query.as = this.single.as;
|
||||
}
|
||||
}
|
||||
|
||||
if(this._undefinedInWhereClause) {
|
||||
debugBindings(defaults.bindings)
|
||||
debugBindings(query.bindings)
|
||||
throw new Error(
|
||||
`Undefined binding(s) detected when compiling ` +
|
||||
`${method.toUpperCase()} query: ${val.sql}`
|
||||
`${method.toUpperCase()} query: ${query.sql}`
|
||||
);
|
||||
}
|
||||
|
||||
return assign(defaults, val);
|
||||
return query;
|
||||
},
|
||||
|
||||
// Compiles the `select` statement, or nested sub-selects by calling each of
|
||||
|
||||
@ -21,9 +21,9 @@ function canRunDockerTests() {
|
||||
const isLinux = os.platform() === 'linux';
|
||||
const isDarwin = os.platform() === 'darwin'
|
||||
// dont even try on windows / osx for now
|
||||
let hasDocker = false;
|
||||
let hasDockerStarted = false;
|
||||
if (isLinux || isDarwin) {
|
||||
hasDocker = proc.execSync('docker -v 1>/dev/null 2>&1 ; echo $?').toString('utf-8') === '0\n';
|
||||
hasDockerStarted = proc.execSync('docker info 1>/dev/null 2>&1 ; echo $?').toString('utf-8') === '0\n';
|
||||
}
|
||||
return hasDocker;
|
||||
return hasDockerStarted;
|
||||
}
|
||||
|
||||
@ -69,6 +69,17 @@ function testsql(chain, valuesToCheck, selectedClients) {
|
||||
})
|
||||
}
|
||||
|
||||
function testNativeSql(chain, valuesToCheck, selectedClients) {
|
||||
selectedClients = selectedClients || clients;
|
||||
Object.keys(valuesToCheck).forEach(function(key) {
|
||||
var newChain = chain.clone();
|
||||
newChain.client = selectedClients[key];
|
||||
var sqlAndBindings = newChain.toSQL().toNative();
|
||||
var checkValue = valuesToCheck[key];
|
||||
verifySqlResult(key, checkValue, sqlAndBindings);
|
||||
})
|
||||
}
|
||||
|
||||
function testquery(chain, valuesToCheck, selectedClients) {
|
||||
selectedClients = selectedClients || clients;
|
||||
Object.keys(valuesToCheck).forEach(function(key) {
|
||||
@ -4317,6 +4328,35 @@ describe("QueryBuilder", function() {
|
||||
});
|
||||
});
|
||||
|
||||
it("should return dialect specific sql and bindings with toSQL().toNative()", function() {
|
||||
testNativeSql(qb().from('table').where('isIt', true), {
|
||||
mssql: {
|
||||
sql: 'select * from [table] where [isIt] = @p0',
|
||||
bindings: [true]
|
||||
},
|
||||
mysql: {
|
||||
sql: 'select * from `table` where `isIt` = ?',
|
||||
bindings: [true]
|
||||
},
|
||||
sqlite3: {
|
||||
sql: 'select * from `table` where `isIt` = ?',
|
||||
bindings: [true]
|
||||
},
|
||||
postgres: {
|
||||
sql: 'select * from "table" where "isIt" = $1',
|
||||
bindings: [true]
|
||||
},
|
||||
oracledb: {
|
||||
sql: 'select * from "table" where "isIt" = :1',
|
||||
bindings: [1]
|
||||
},
|
||||
oracle: {
|
||||
sql: 'select * from "table" where "isIt" = :1',
|
||||
bindings: [1]
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("nested and chained wrapped 'with' clause", function() {
|
||||
testsql(qb().with('firstWithClause', function() {
|
||||
this.with('firstWithSubClause', function() {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user