mirror of
https://github.com/knex/knex.git
synced 2025-12-29 07:59:31 +00:00
fix issue with null bindings, change around the query compiler
This commit is contained in:
parent
cf95f09cf0
commit
76dfc1348b
@ -1,3 +1,5 @@
|
||||
var push = Array.prototype.push;
|
||||
|
||||
module.exports = function(client) {
|
||||
|
||||
var _ = require('lodash');
|
||||
@ -6,19 +8,17 @@ module.exports = function(client) {
|
||||
return QueryCompiler.extend({
|
||||
|
||||
update: function() {
|
||||
var updateData = this.get('update');
|
||||
push.apply(this.bindings, updateData.bindings);
|
||||
var where = this.where();
|
||||
var join = this.join();
|
||||
var order = this.order();
|
||||
var limit = this.limit();
|
||||
|
||||
var joinSql = join.sql ? ' ' + join.sql : '';
|
||||
var orderSql = order.sql ? ' ' + order.sql : '';
|
||||
var limitSql = limit.sql ? ' ' + limit.sql : '';
|
||||
var updateData = this.get('update');
|
||||
return {
|
||||
sql: 'update ' + this.tableName + joinSql + ' set ' + updateData.columns + ' ' + where.sql + orderSql + limitSql,
|
||||
bindings: _.compact(updateData.bindings.concat(where.bindings, order.bindings, limit.bindings))
|
||||
};
|
||||
var joinSql = join ? ' ' + join : '';
|
||||
var orderSql = order ? ' ' + order : '';
|
||||
var limitSql = limit ? ' ' + limit : '';
|
||||
return 'update ' + this.tableName + joinSql + ' set ' + updateData.columns + ' ' + where + orderSql + limitSql;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
var push = Array.prototype.push;
|
||||
|
||||
// Extend the base compiler with the necessary grammar
|
||||
module.exports = function(client) {
|
||||
@ -9,9 +10,7 @@ module.exports = function(client) {
|
||||
|
||||
// Compiles a truncate query.
|
||||
truncate: function() {
|
||||
return {
|
||||
sql: 'truncate ' + this.tableName + ' restart identity'
|
||||
};
|
||||
return 'truncate ' + this.tableName + ' restart identity';
|
||||
},
|
||||
|
||||
// Compiles an `insert` query, allowing for multiple
|
||||
@ -25,24 +24,19 @@ module.exports = function(client) {
|
||||
sql += insertData.columns + ' values ' + insertData.value;
|
||||
}
|
||||
var returning = this.get('returning');
|
||||
return {
|
||||
sql: sql + (returning.value ? ' ' + returning.value : ''),
|
||||
bindings: _.flatten(insertData.bindings)
|
||||
};
|
||||
push.apply(this.bindings, _.flatten(insertData.bindings));
|
||||
return sql + (returning.value ? ' ' + returning.value : '');
|
||||
},
|
||||
|
||||
// TODO: Update all the response thingers here.
|
||||
|
||||
// Compiles an `update` query, allowing for a return value.
|
||||
update: function() {
|
||||
var wheres = this.where();
|
||||
var updateData = this.get('update');
|
||||
var returning = this.get('returning');
|
||||
var returnVal = (returning.value ? ' ' + returning.value : '');
|
||||
return {
|
||||
sql: 'update ' + this.tableName + ' set ' + updateData.columns + ' ' + wheres.sql + returnVal,
|
||||
bindings: updateData.bindings.concat(wheres.bindings),
|
||||
};
|
||||
push.apply(this.bindings, updateData.bindings);
|
||||
return 'update ' + this.tableName + ' set ' + updateData.columns + ' ' + this.where() + returnVal;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@ -20,7 +20,9 @@ _.extend(Runner.prototype, {
|
||||
run: function Runner$run(target, method) {
|
||||
if (_.isArray(target)) {
|
||||
var runner = this;
|
||||
if (target.length === 1) return this.query(target[0]);
|
||||
if (target.length === 1) {
|
||||
return this.query(target[0]);
|
||||
}
|
||||
return Promise.reduce(target, function(memo, block) {
|
||||
return runner.run(block).then(function(resp) {
|
||||
memo.push(resp);
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
// Extend the base compiler with the necessary grammar
|
||||
var push = Array.prototype.push;
|
||||
|
||||
module.exports = function(client) {
|
||||
|
||||
var QueryCompiler = require('../../../query/compiler')(client);
|
||||
@ -24,10 +26,8 @@ module.exports = function(client) {
|
||||
// grammar insert builder because no special syntax is needed for the single
|
||||
// row inserts in SQLite. However, if there are multiples, we'll continue.
|
||||
if (insert.rawData.length <= 1) {
|
||||
return {
|
||||
sql: sql,
|
||||
bindings: insert.bindings
|
||||
};
|
||||
push.apply(this.bindings, insert.bindings);
|
||||
return sql;
|
||||
}
|
||||
|
||||
var blocks = [];
|
||||
@ -44,23 +44,19 @@ module.exports = function(client) {
|
||||
}
|
||||
blocks[i] = block.join(', ');
|
||||
}
|
||||
push.apply(this.bindings, insert.bindings);
|
||||
|
||||
return {
|
||||
sql: sql + ' select ' + blocks.join(' union all select '),
|
||||
bindings: insert.bindings
|
||||
};
|
||||
return sql + ' select ' + blocks.join(' union all select ');
|
||||
},
|
||||
|
||||
// Compiles an `update` query.
|
||||
update: function() {
|
||||
var updateData = this.get('update');
|
||||
push.apply(this.bindings, updateData.bindings);
|
||||
var wheres = this.where();
|
||||
var joins = this.join();
|
||||
var join = joins.sql ? ' ' + joins.sql : '';
|
||||
var updateData = this.get('update');
|
||||
return {
|
||||
sql: 'update ' + this.tableName + join + ' set ' + updateData.columns + ' ' + wheres.sql,
|
||||
bindings: updateData.bindings.concat(wheres.bindings)
|
||||
};
|
||||
return 'update ' + this.tableName + join + ' set ' + updateData.columns + ' ' + wheres;
|
||||
},
|
||||
|
||||
// Compile a truncate table statement into SQL.
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
var _ = require('lodash');
|
||||
var Helpers = require('../helpers');
|
||||
var push = Array.prototype.push;
|
||||
var concat = Array.prototype.concat;
|
||||
|
||||
module.exports = function() {
|
||||
|
||||
@ -13,22 +12,32 @@ module.exports = function() {
|
||||
// The "QueryCompiler" takes all of the query statements which have been
|
||||
// assembed in the
|
||||
var QueryCompiler = function(builder) {
|
||||
this.tableName = builder._table();
|
||||
this.singles = builder.singles;
|
||||
this.grouped = _.groupBy(builder.statements, 'type');
|
||||
this.tableName = builder._table();
|
||||
this.singles = builder.singles;
|
||||
this.grouped = _.groupBy(builder.statements, 'type');
|
||||
this.bindings = [];
|
||||
this.statements = [];
|
||||
};
|
||||
|
||||
_.extend(QueryCompiler.prototype, {
|
||||
|
||||
compiled: function(target) {
|
||||
var sql = this[target]();
|
||||
if (_.isString(sql)) {
|
||||
return {
|
||||
sql: sql,
|
||||
bindings: this.bindings
|
||||
};
|
||||
} else {
|
||||
return sql;
|
||||
}
|
||||
},
|
||||
|
||||
get: function(elem) {
|
||||
var item = this.grouped[elem];
|
||||
return item ? item[0] : {value: '', columns: ''};
|
||||
},
|
||||
|
||||
compiled: function(target) {
|
||||
return this[target]();
|
||||
},
|
||||
|
||||
// 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.
|
||||
@ -37,14 +46,11 @@ module.exports = function() {
|
||||
for (var i = 0, l = components.length; i < l; i++) {
|
||||
var component = components[i];
|
||||
if (this.grouped[component] || component === 'columns') {
|
||||
statements.push(this[component](this));
|
||||
var statement = this[component](this);
|
||||
this.statements.push(statement);
|
||||
}
|
||||
}
|
||||
var bindings = concat.apply([], _.compact(_.pluck(statements, 'bindings')));
|
||||
return {
|
||||
sql: _.compact(_.pluck(statements, 'sql')).join(' '),
|
||||
bindings: bindings
|
||||
};
|
||||
return _.compact(this.statements).join(' ');
|
||||
},
|
||||
|
||||
// Alias to `select` with some post-processing on the output.
|
||||
@ -56,40 +62,31 @@ module.exports = function() {
|
||||
// inserts using a single query statement.
|
||||
insert: function() {
|
||||
var insertData = this.get('insert');
|
||||
return {
|
||||
sql: 'insert into ' + this.tableName + ' ' +
|
||||
insertData.columns + ' values ' + insertData.value,
|
||||
bindings: insertData.bindings
|
||||
};
|
||||
push.apply(this.bindings, insertData.bindings);
|
||||
return 'insert into ' + this.tableName + ' ' + insertData.columns + ' values ' + insertData.value;
|
||||
},
|
||||
|
||||
// Compiles the columns in the query, specifying if an item was distinct.
|
||||
columns: function() {
|
||||
var distinct = false;
|
||||
var bindings = [];
|
||||
if (this.onlyUnions()) return {};
|
||||
if (this.onlyUnions()) return;
|
||||
var sql = _.compact(_.map(this.grouped.columns, function(block) {
|
||||
if (block.distinct) distinct = true;
|
||||
push.apply(bindings, block.bindings);
|
||||
push.apply(this.bindings, block.bindings);
|
||||
return block.value;
|
||||
}, this));
|
||||
return {
|
||||
sql: 'select ' + (distinct ? 'distinct ' : '') +
|
||||
(sql.join(', ') || '*') + (this.tableName ? ' from ' + this.tableName : ''),
|
||||
bindings: bindings // _.flatten(bindings, true)
|
||||
};
|
||||
return 'select ' + (distinct ? 'distinct ' : '') +
|
||||
(sql.join(', ') || '*') + (this.tableName ? ' from ' + this.tableName : '');
|
||||
},
|
||||
|
||||
// Compiles all each of the `join` clauses on the query,
|
||||
// including any nested join queries.
|
||||
join: function() {
|
||||
return {
|
||||
sql: _.map(this.grouped.join, function(item, i) {
|
||||
var sql = '';
|
||||
if (i > 0) sql += item.bool || '';
|
||||
return sql + item.value;
|
||||
}).join(' ')
|
||||
};
|
||||
return _.map(this.grouped.join, function(item, i) {
|
||||
var sql = '';
|
||||
if (i > 0) sql += item.bool || '';
|
||||
return sql + item.value;
|
||||
}).join(' ');
|
||||
},
|
||||
|
||||
// Compiles all `where` statements on the query.
|
||||
@ -120,11 +117,8 @@ module.exports = function() {
|
||||
if (i > 0 || !onlyUnions) sql += union.clause + ' ';
|
||||
sql += union.value;
|
||||
}
|
||||
return {
|
||||
type: 'union',
|
||||
sql: sql,
|
||||
bindings: _.flatten(_.pluck(this.grouped.union, 'bindings'))
|
||||
};
|
||||
push.apply(this.bindings, _.flatten(_.pluck(this.grouped.union, 'bindings')));
|
||||
return sql;
|
||||
},
|
||||
|
||||
// If we haven't specified any columns or a `tableName`, we're assuming this
|
||||
@ -135,64 +129,48 @@ module.exports = function() {
|
||||
|
||||
limit: function() {
|
||||
var limit = this.get('limit');
|
||||
return {
|
||||
sql: limit.value,
|
||||
bindings: limit.bindings
|
||||
};
|
||||
push.apply(this.bindings, limit.bindings);
|
||||
return limit.value;
|
||||
},
|
||||
|
||||
offset: function() {
|
||||
var offset = this.get('offset');
|
||||
return {
|
||||
sql: offset.value,
|
||||
bindings: offset.bindings
|
||||
};
|
||||
push.apply(this.bindings, offset.bindings);
|
||||
return offset.value;
|
||||
},
|
||||
|
||||
// Compiles a `delete` query.
|
||||
delete: function() {
|
||||
var wheres = this.where();
|
||||
return {
|
||||
sql: 'delete from ' + this.tableName + ' ' + wheres.sql,
|
||||
bindings: wheres.bindings
|
||||
};
|
||||
push.apply(this.bindings, wheres.bindings);
|
||||
return 'delete from ' + this.tableName + ' ' + wheres;
|
||||
},
|
||||
|
||||
// Compiles a `truncate` query.
|
||||
truncate: function() {
|
||||
return {
|
||||
sql: 'truncate ' + this.tableName
|
||||
};
|
||||
return 'truncate ' + this.tableName;
|
||||
},
|
||||
|
||||
lock: function() {
|
||||
return {
|
||||
sql: _.pluck(this.grouped.lock, 'value').join(' ')
|
||||
};
|
||||
return _.pluck(this.grouped.lock, 'value').join(' ');
|
||||
},
|
||||
|
||||
// Compiles the `order by` statements.
|
||||
_groupsOrders: function(type) {
|
||||
var items = _.pluck(this.grouped[type], 'value');
|
||||
if (items.length > 0) {
|
||||
return {
|
||||
sql: type + ' by ' + items.join(', ')
|
||||
};
|
||||
return type + ' by ' + items.join(', ');
|
||||
}
|
||||
return {};
|
||||
},
|
||||
|
||||
// The same logic is used for compiling "where" statements as it is
|
||||
// for "having" statements.
|
||||
_havingWhere: function(type) {
|
||||
var bindings = [];
|
||||
return {
|
||||
sql: _.map(this.grouped[type], function(item, i) {
|
||||
push.apply(bindings, item.bindings);
|
||||
return (i === 0 ? type + ' ' : '') + item.bool + ' ' + item.value;
|
||||
}).join(' ').replace(/and |or /, ''),
|
||||
bindings: _.flatten(bindings)
|
||||
};
|
||||
return _.map(this.grouped[type], function(item, i) {
|
||||
push.apply(this.bindings, _.flatten(item.bindings));
|
||||
return (i === 0 ? type + ' ' : '') + item.bool + ' ' + item.value;
|
||||
}, this).join(' ').replace(/and |or /, '');
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@ -46,7 +46,7 @@ module.exports = function(client) {
|
||||
},
|
||||
|
||||
toSql: function(target) {
|
||||
return new client.QueryCompiler(this)[(target || this._method || 'select')]();
|
||||
return new client.QueryCompiler(this).compiled((target || this._method || 'select'));
|
||||
},
|
||||
|
||||
// Create a shallow clone of the current query builder.
|
||||
|
||||
@ -10,7 +10,7 @@ module.exports = function(knex) {
|
||||
.testSql(function(tester) {
|
||||
tester('mysql', 'truncate `test_table_two`');
|
||||
tester('postgresql', 'truncate "test_table_two" restart identity');
|
||||
tester('sqlite3', 'delete from sqlite_sequence where name = "test_table_two"');
|
||||
tester('sqlite3', "delete from sqlite_sequence where name = \"test_table_two\"");
|
||||
})
|
||||
.then(function() {
|
||||
|
||||
|
||||
@ -31,6 +31,23 @@ module.exports = function(knex) {
|
||||
});
|
||||
});
|
||||
|
||||
it('should allow for null updates', function() {
|
||||
return knex('accounts')
|
||||
.where('id', 1000)
|
||||
.update({
|
||||
first_name: null,
|
||||
last_name: 'Test',
|
||||
email:'test100@example.com'
|
||||
}).testSql(function(tester) {
|
||||
tester(
|
||||
'mysql',
|
||||
'update `accounts` set `email` = ?, `first_name` = ?, `last_name` = ? where `id` = ?',
|
||||
['test100@example.com', null, 'Test', 1000],
|
||||
0
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('should increment a value', function() {
|
||||
|
||||
return knex('accounts').select('logins').where('id', 1).tap(function() {
|
||||
|
||||
@ -417,6 +417,12 @@ module.exports = function(pgclient, mysqlclient, sqlite3client, stateless) {
|
||||
expect(chain.bindings).to.eql(['foo', 'bar', 1]);
|
||||
});
|
||||
|
||||
it("should allow for 'null' updates", function() {
|
||||
chain = sql().update({email: null, 'name': 'bar'}).table('users').where('id', 1).toSql();
|
||||
expect(chain.sql).to.equal('update "users" set "email" = ?, "name" = ? where "id" = ?');
|
||||
expect(chain.bindings).to.eql([null, 'bar', 1]);
|
||||
});
|
||||
|
||||
it("order by, limit", function() {
|
||||
chain = mysql().from('users').where('id', '=', 1).orderBy('foo', 'desc').limit(5).update({email: 'foo', name: 'bar'}).toSql();
|
||||
expect(chain.sql).to.equal('update `users` set `email` = ?, `name` = ? where `id` = ? order by `foo` desc limit ?');
|
||||
@ -460,8 +466,8 @@ module.exports = function(pgclient, mysqlclient, sqlite3client, stateless) {
|
||||
chain = sql().table('users').truncate().toSql();
|
||||
expect(chain.sql).to.equal('truncate "users" restart identity');
|
||||
chain = sqlite3().table('users').truncate().toSql();
|
||||
expect(chain.sql).to.equal('delete from sqlite_sequence where name = "users"');
|
||||
expect(chain.output).to.be.a.function;
|
||||
expect(chain.sql).to.equal("delete from sqlite_sequence where name = \"users\"");
|
||||
expect(typeof chain.output).to.equal('function');
|
||||
});
|
||||
|
||||
it("postgres insert get id", function() {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user