mirror of
https://github.com/knex/knex.git
synced 2025-12-29 07:59:31 +00:00
move operator to formatters, add nicer syntax for where in, where between, where is null
This commit is contained in:
parent
f0d0e3d354
commit
5726c3250f
@ -13,6 +13,13 @@ function Formatter_MySQL() {
|
||||
}
|
||||
inherits(Formatter_MySQL, Formatter);
|
||||
|
||||
Formatter_MySQL.prototype.operators = [
|
||||
'=', '<', '>', '<=', '>=', '<>', '!=',
|
||||
'like', 'not like', 'between', 'ilike',
|
||||
'&', '|', '^', '<<', '>>',
|
||||
'rlike', 'regexp', 'not regexp'
|
||||
];
|
||||
|
||||
// Wraps a value (column, tableName) with the correct ticks.
|
||||
Formatter_MySQL.prototype.wrapValue = function(value) {
|
||||
return (value !== '*' ? '`' + value + '`' : '*');
|
||||
|
||||
@ -14,6 +14,12 @@ function Formatter_PG() {
|
||||
}
|
||||
inherits(Formatter_PG, Formatter);
|
||||
|
||||
Formatter_PG.prototype.operators = [
|
||||
'=', '<', '>', '<=', '>=', '<>', '!=',
|
||||
'like', 'not like', 'between', 'ilike',
|
||||
'&', '|', '#', '<<', '>>', '&&', '^', '@>', '<@', '||'
|
||||
];
|
||||
|
||||
// Wraps a value (column, tableName) with the correct ticks.
|
||||
Formatter_PG.prototype.wrapValue = function(value) {
|
||||
return (value !== '*' ? '"' + value + '"' : '*');
|
||||
|
||||
@ -13,6 +13,12 @@ function Formatter_SQLite3() {
|
||||
}
|
||||
inherits(Formatter_SQLite3, Formatter);
|
||||
|
||||
Formatter_SQLite3.prototype.operators = [
|
||||
'=', '<', '>', '<=', '>=', '<>', '!=',
|
||||
'like', 'not like', 'between', 'ilike',
|
||||
'&', '|', '<<', '>>'
|
||||
];
|
||||
|
||||
// Wraps a value (column, tableName) with the correct ticks.
|
||||
Formatter_SQLite3.prototype.wrapValue = function(value) {
|
||||
return (value !== '*' ? '"' + value + '"' : '*');
|
||||
|
||||
@ -6,9 +6,6 @@ var QueryBuilder = require('./query/builder');
|
||||
var Raw = require('./raw');
|
||||
var push = Array.prototype.push;
|
||||
|
||||
// All operators used in the `where` clause generation.
|
||||
var operators = ['=', '<', '>', '<=', '>=', '<>', '!=', 'like', 'not like', 'between', 'ilike'];
|
||||
|
||||
// Valid values for the `order by` clause generation.
|
||||
var orderBys = ['asc', 'desc'];
|
||||
|
||||
@ -18,7 +15,6 @@ var orderBys = ['asc', 'desc'];
|
||||
// arbitrarily added to queries.
|
||||
function Formatter() {
|
||||
this.bindings = [];
|
||||
this.errors = [];
|
||||
}
|
||||
|
||||
// Turns a list of values into a list of ?'s, joining them with commas unless
|
||||
@ -101,8 +97,8 @@ Formatter.prototype.columnize = function(target) {
|
||||
Formatter.prototype.operator = function(value) {
|
||||
var raw;
|
||||
if (raw = this.checkRaw(value)) return raw;
|
||||
if (!_.contains(operators, value)) {
|
||||
this.errors.push(new Error('The operator "' + value + '" is not permitted'));
|
||||
if (!_.contains(this.operators, value)) {
|
||||
throw new TypeError('The operator "' + value + '" is not permitted');
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
@ -22,12 +22,6 @@ function QueryBuilder() {
|
||||
}
|
||||
inherits(QueryBuilder, EventEmitter);
|
||||
|
||||
// All operators used in the `where` clause generation.
|
||||
var operators = ['=', '<', '>', '<=', '>=', '<>', '!=', 'like', 'not like',
|
||||
'between', 'ilike', '&', '&&', '|', '^', '#', '<<', '>>', '@>', '<@', '||'];
|
||||
|
||||
var nullOperators = ['is', 'is not'];
|
||||
|
||||
// Valid values for the `order by` clause generation.
|
||||
var orderBys = ['asc', 'desc'];
|
||||
|
||||
@ -162,7 +156,7 @@ QueryBuilder.prototype.andWhere = function(column, operator, value) {
|
||||
}
|
||||
|
||||
// Allow a raw statement to be passed along to the query.
|
||||
if (column instanceof Raw) return this._whereRaw(column);
|
||||
if (column instanceof Raw) return this.whereRaw(column);
|
||||
|
||||
// Allows `where({id: 2})` syntax.
|
||||
if (_.isObject(column)) return this._objectWhere(column);
|
||||
@ -173,28 +167,38 @@ QueryBuilder.prototype.andWhere = function(column, operator, value) {
|
||||
if (arguments.length === 2) {
|
||||
value = operator;
|
||||
operator = '=';
|
||||
|
||||
// If the value is null, and it's a two argument query,
|
||||
// we assume we're going for a `whereNull`.
|
||||
if (value === null) {
|
||||
return this.whereNull(column);
|
||||
}
|
||||
}
|
||||
|
||||
// lower case the operator for comparison purposes
|
||||
operator = operator.toLowerCase();
|
||||
var checkOperator = ('' + operator).toLowerCase().trim();
|
||||
|
||||
// Ensure that the operator / query combo is legal.
|
||||
if (!_.contains(operators, operator)) {
|
||||
if (_.contains(nullOperators, operator)) {
|
||||
if (value === null || _.isString(value) && value.toLowerCase() === 'null') {
|
||||
return operator === 'is' ? this.whereNull(column, bool) : this.whereNull(column, bool, 'NotNull');
|
||||
}
|
||||
this._errors.push(new Error('Invalid where in clause'));
|
||||
// If there are 3 arguments, check whether 'in' is one of them.
|
||||
if (arguments.length === 3) {
|
||||
if (checkOperator === 'in' || checkOperator === 'not in') {
|
||||
return this.whereIn(arguments[0], arguments[2], (checkOperator === 'not in'));
|
||||
}
|
||||
if (checkOperator === 'between' || checkOperator === 'not between') {
|
||||
return this.whereBetween(arguments[0], arguments[2], (checkOperator === 'not between'));
|
||||
}
|
||||
this._errors.push(new Error('Invalid operator: ' + operator));
|
||||
}
|
||||
|
||||
// If the value is null, and the operator is equals, assume that we're
|
||||
// going for a `whereNull` statement here.
|
||||
if (value === null && operator === '=') {
|
||||
return this.whereNull(column);
|
||||
// If the value is still null, check whether they're meaning
|
||||
// where value is null
|
||||
if (value === null) {
|
||||
|
||||
// Check for .where(key, 'is', null) or .where(key, 'is not', 'null');
|
||||
if (checkOperator === 'is' || checkOperator === 'is not') {
|
||||
return this.whereNull(column, bool, (checkOperator === 'is not'));
|
||||
}
|
||||
}
|
||||
|
||||
// Push onto the where statement stack.
|
||||
this._statements.push({
|
||||
grouping: 'where',
|
||||
type: 'whereBasic',
|
||||
@ -205,32 +209,23 @@ QueryBuilder.prototype.andWhere = function(column, operator, value) {
|
||||
});
|
||||
return this;
|
||||
};
|
||||
|
||||
// Processes an object literal provided in a "where" clause.
|
||||
QueryBuilder.prototype._objectWhere = function(obj) {
|
||||
var boolVal = this._bool();
|
||||
for (var key in obj) {
|
||||
this[boolVal + 'Where'](key, '=', obj[key]);
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
// Adds an `or where` clause to the query.
|
||||
QueryBuilder.prototype.orWhere = function() {
|
||||
return this._bool('or').where.apply(this, arguments);
|
||||
};
|
||||
|
||||
// [Deprecated]
|
||||
QueryBuilder.prototype.orWhereRaw = function(sql, bindings) {
|
||||
return this._bool('or').whereRaw(sql, bindings);
|
||||
};
|
||||
QueryBuilder.prototype.whereRaw = function(sql, bindings) {
|
||||
helpers.deprecate('Knex: .whereRaw is deprecated, please use .where(knex.raw(QUERY, [bindings]))');
|
||||
return this._whereRaw(sql, bindings);
|
||||
// Processes an object literal provided in a "where" clause.
|
||||
QueryBuilder.prototype._objectWhere = function(obj) {
|
||||
var boolVal = this._bool();
|
||||
for (var key in obj) {
|
||||
this[boolVal + 'Where'](key, obj[key]);
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
// Adds a raw `where` clause to the query.
|
||||
QueryBuilder.prototype._whereRaw = function(sql, bindings) {
|
||||
QueryBuilder.prototype.whereRaw =
|
||||
QueryBuilder.prototype.andWhereRaw = function(sql, bindings) {
|
||||
var raw = (sql instanceof Raw ? sql : new Raw(sql, bindings));
|
||||
this._statements.push({
|
||||
grouping: 'where',
|
||||
@ -240,6 +235,9 @@ QueryBuilder.prototype._whereRaw = function(sql, bindings) {
|
||||
});
|
||||
return this;
|
||||
};
|
||||
QueryBuilder.prototype.orWhereRaw = function(sql, bindings) {
|
||||
return this._bool('or').whereRaw(sql, bindings);
|
||||
};
|
||||
|
||||
// Helper for compiling any advanced `where` queries.
|
||||
QueryBuilder.prototype.whereWrapped = function(callback) {
|
||||
@ -460,7 +458,6 @@ QueryBuilder.prototype._havingRaw = function(sql, bindings) {
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
// Only allow a single "offset" to be set for the current query.
|
||||
QueryBuilder.prototype.offset = function(value) {
|
||||
this._single.offset = value;
|
||||
|
||||
@ -25,9 +25,9 @@ Transaction.prototype.containerObject = function(runner) {
|
||||
__transactor__ : {_runner: runner}
|
||||
});
|
||||
|
||||
// Remove the ability to start a transaction from
|
||||
// within a transaction.
|
||||
transactor.transaction = void 0;
|
||||
// Remove the ability to start a transaction or destroy
|
||||
// the entire pool within a transaction.
|
||||
transactor.destroy = transactor.transaction = void 0;
|
||||
|
||||
// Commits the transaction:
|
||||
transactor.commit = function(message) {
|
||||
|
||||
@ -49,12 +49,24 @@ module.exports = function(pgclient, mysqlclient, sqlite3client) {
|
||||
expect(chain.bindings).to.eql([1, 2]);
|
||||
});
|
||||
|
||||
it("where betweens, alternate", function() {
|
||||
chain = sql().select('*').from('users').where('id', 'BeTween', [1, 2]).toSQL();
|
||||
expect(chain.sql).to.equal('select * from "users" where "id" between ? and ?');
|
||||
expect(chain.bindings).to.eql([1, 2]);
|
||||
});
|
||||
|
||||
it("where not between", function() {
|
||||
chain = sql().select('*').from('users').whereNotBetween('id', [1, 2]).toSQL();
|
||||
expect(chain.sql).to.equal('select * from "users" where "id" not between ? and ?');
|
||||
expect(chain.bindings).to.eql([1, 2]);
|
||||
});
|
||||
|
||||
it("where not between, alternate", function() {
|
||||
chain = sql().select('*').from('users').where('id', 'not between ', [1, 2]).toSQL();
|
||||
expect(chain.sql).to.equal('select * from "users" where "id" not between ? and ?');
|
||||
expect(chain.bindings).to.eql([1, 2]);
|
||||
});
|
||||
|
||||
it("basic or wheres", function() {
|
||||
chain = sql().select('*').from('users').where('id', '=', 1).orWhere('email', '=', 'foo').toSQL();
|
||||
expect(chain.sql).to.equal('select * from "users" where "id" = ? or "email" = ?');
|
||||
@ -298,6 +310,13 @@ module.exports = function(pgclient, mysqlclient, sqlite3client) {
|
||||
expect(chain.sql).to.equal('select * from "orders" where exists (select * from "products" where "products"."id" = "orders"."id")');
|
||||
});
|
||||
|
||||
it("where exists", function() {
|
||||
chain = sql().select('*').from('orders').whereExists(function(qb) {
|
||||
qb.select('*').from('products').where('products.id', '=', raw('"orders"."id"'));
|
||||
}).toSQL();
|
||||
expect(chain.sql).to.equal('select * from "orders" where exists (select * from "products" where "products"."id" = "orders"."id")');
|
||||
});
|
||||
|
||||
it("where not exists", function() {
|
||||
chain = sql().select('*').from('orders').whereNotExists(function(qb) {
|
||||
qb.select('*').from('products').where('products.id', '=', raw('"orders"."id"'));
|
||||
@ -609,6 +628,26 @@ module.exports = function(pgclient, mysqlclient, sqlite3client) {
|
||||
expect(q2.toSQL().sql).to.equal('insert into "recipients" (recipient_id, email) select \'user\', \'user@foo.com\' where not exists (select 1 from "recipients" where "recipient_id" = ?)');
|
||||
});
|
||||
|
||||
it('throws if you try to use an invalid operator', function() {
|
||||
var err;
|
||||
try {
|
||||
sql().select('*').where('id', 'isnt', 1).toString();
|
||||
} catch (e) {
|
||||
err = e.message;
|
||||
}
|
||||
expect(err).to.equal("The operator \"isnt\" is not permitted");
|
||||
});
|
||||
|
||||
it('throws if you try to use an invalid operator in an inserted statement', function() {
|
||||
var err, obj = sql().select('*').where('id', 'isnt', 1);
|
||||
try {
|
||||
sql().select('*').from('users').where('id', 'in', obj).toString();
|
||||
} catch (e) {
|
||||
err = e.message;
|
||||
}
|
||||
expect(err).to.equal("The operator \"isnt\" is not permitted");
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
};
|
||||
Loading…
x
Reference in New Issue
Block a user