mirror of
https://github.com/knex/knex.git
synced 2025-12-25 14:08:30 +00:00
feat: add undefined columns to undefined binding(s) error (#3425)
This commit is contained in:
parent
75ac92f40b
commit
4ade98980e
@ -36,12 +36,12 @@ function containsUndefined(mixed) {
|
||||
if (isArray(mixed)) {
|
||||
for (let i = 0; i < mixed.length; i++) {
|
||||
if (argContainsUndefined) break;
|
||||
argContainsUndefined = this.containsUndefined(mixed[i]);
|
||||
argContainsUndefined = containsUndefined(mixed[i]);
|
||||
}
|
||||
} else if (isPlainObject(mixed)) {
|
||||
Object.keys(mixed).forEach((key) => {
|
||||
if (!argContainsUndefined) {
|
||||
argContainsUndefined = this.containsUndefined(mixed[key]);
|
||||
argContainsUndefined = containsUndefined(mixed[key]);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
@ -51,6 +51,28 @@ function containsUndefined(mixed) {
|
||||
return argContainsUndefined;
|
||||
}
|
||||
|
||||
function getUndefinedIndices(mixed) {
|
||||
const indices = [];
|
||||
|
||||
if (Array.isArray(mixed)) {
|
||||
mixed.forEach((item, index) => {
|
||||
if (containsUndefined(item)) {
|
||||
indices.push(index);
|
||||
}
|
||||
});
|
||||
} else if (isPlainObject(mixed)) {
|
||||
Object.keys(mixed).forEach((key) => {
|
||||
if (containsUndefined(mixed[key])) {
|
||||
indices.push(key);
|
||||
}
|
||||
})
|
||||
} else {
|
||||
indices.push(0);
|
||||
}
|
||||
|
||||
return indices;
|
||||
}
|
||||
|
||||
function addQueryContext(Target) {
|
||||
// Stores or returns (if called with no arguments) context passed to
|
||||
// wrapIdentifier and postProcessResponse hooks
|
||||
@ -72,4 +94,5 @@ module.exports = {
|
||||
containsUndefined,
|
||||
normalizeArr,
|
||||
resolveClientNameWithAliases,
|
||||
getUndefinedIndices,
|
||||
};
|
||||
|
||||
@ -58,6 +58,7 @@ assign(QueryCompiler.prototype, {
|
||||
// Collapse the builder into a single object
|
||||
toSQL(method, tz) {
|
||||
this._undefinedInWhereClause = false;
|
||||
this.undefinedBindingsInfo = [];
|
||||
|
||||
method = method || this.method;
|
||||
const val = this[method]() || '';
|
||||
@ -99,7 +100,7 @@ assign(QueryCompiler.prototype, {
|
||||
debugBindings(query.bindings);
|
||||
throw new Error(
|
||||
`Undefined binding(s) detected when compiling ` +
|
||||
`${method.toUpperCase()} query: ${query.sql}`
|
||||
`${method.toUpperCase()}. Undefined column(s): [${this.undefinedBindingsInfo.join(', ')}] query: ${query.sql}`
|
||||
);
|
||||
}
|
||||
|
||||
@ -372,6 +373,7 @@ assign(QueryCompiler.prototype, {
|
||||
Object.prototype.hasOwnProperty.call(stmt, 'value') &&
|
||||
helpers.containsUndefined(stmt.value)
|
||||
) {
|
||||
this.undefinedBindingsInfo.push(stmt.column)
|
||||
this._undefinedInWhereClause = true;
|
||||
}
|
||||
const val = this[stmt.type](stmt);
|
||||
|
||||
@ -104,9 +104,10 @@ assign(Raw.prototype, {
|
||||
|
||||
obj.bindings = obj.bindings || [];
|
||||
if (helpers.containsUndefined(obj.bindings)) {
|
||||
const undefinedBindingIndices = helpers.getUndefinedIndices(this.bindings);
|
||||
debugBindings(obj.bindings);
|
||||
throw new Error(
|
||||
`Undefined binding(s) detected when compiling RAW query: ` + obj.sql
|
||||
`Undefined binding(s) detected for keys [${undefinedBindingIndices}] when compiling RAW query: ${obj.sql}`
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -8562,40 +8562,64 @@ describe('QueryBuilder', function() {
|
||||
|
||||
it('Any undefined binding in a SELECT query should throw an error', function() {
|
||||
var qbuilders = [
|
||||
qb()
|
||||
.from('accounts')
|
||||
.where({ Login: void 0 })
|
||||
.select(),
|
||||
qb()
|
||||
.from('accounts')
|
||||
.where('Login', void 0)
|
||||
.select(),
|
||||
qb()
|
||||
.from('accounts')
|
||||
.where('Login', '>=', void 0)
|
||||
.select(),
|
||||
qb()
|
||||
.from('accounts')
|
||||
.whereIn('Login', ['test', 'val', void 0])
|
||||
.select(),
|
||||
qb()
|
||||
.from('accounts')
|
||||
.where({ Login: ['1', '2', '3', void 0] }),
|
||||
qb()
|
||||
.from('accounts')
|
||||
.where({ Login: { Test: '123', Value: void 0 } }),
|
||||
qb()
|
||||
.from('accounts')
|
||||
.where({ Login: ['1', ['2', [void 0]]] }),
|
||||
qb()
|
||||
.from('accounts')
|
||||
.update({ test: '1', test2: void 0 })
|
||||
.where({ abc: 'test', cba: void 0 }),
|
||||
{
|
||||
builder: qb()
|
||||
.from('accounts')
|
||||
.where({ Login: void 0 })
|
||||
.select(),
|
||||
undefinedColumns: ['Login']
|
||||
},
|
||||
{
|
||||
builder: qb()
|
||||
.from('accounts')
|
||||
.where('Login', void 0)
|
||||
.select(),
|
||||
undefinedColumns: ['Login']
|
||||
},
|
||||
{
|
||||
builder: qb()
|
||||
.from('accounts')
|
||||
.where('Login', '>=', void 0)
|
||||
.select(),
|
||||
undefinedColumns: ['Login']
|
||||
},
|
||||
{
|
||||
builder: qb()
|
||||
.from('accounts')
|
||||
.whereIn('Login', ['test', 'val', void 0])
|
||||
.select(),
|
||||
undefinedColumns: ['Login']
|
||||
},
|
||||
{
|
||||
builder: qb()
|
||||
.from('accounts')
|
||||
.where({ Login: ['1', '2', '3', void 0] }),
|
||||
undefinedColumns: ['Login']
|
||||
},
|
||||
{
|
||||
builder: qb()
|
||||
.from('accounts')
|
||||
.where({ Login: { Test: '123', Value: void 0 } }),
|
||||
undefinedColumns: ['Login']
|
||||
},
|
||||
{
|
||||
builder: qb()
|
||||
.from('accounts')
|
||||
.where({ Login: ['1', ['2', [void 0]]] }),
|
||||
undefinedColumns: ['Login']
|
||||
},
|
||||
{
|
||||
builder: qb()
|
||||
.from('accounts')
|
||||
.update({ test: '1', test2: void 0 })
|
||||
.where({ abc: 'test', cba: void 0 }),
|
||||
undefinedColumns: ['cba']
|
||||
}
|
||||
];
|
||||
qbuilders.forEach(function(qbuilder) {
|
||||
qbuilders.forEach(function({ builder, undefinedColumns}) {
|
||||
try {
|
||||
//Must be present, but makes no difference since it throws.
|
||||
testsql(qbuilder, {
|
||||
testsql(builder, {
|
||||
mysql: {
|
||||
sql: '',
|
||||
bindings: [],
|
||||
@ -8624,31 +8648,31 @@ describe('QueryBuilder', function() {
|
||||
} catch (error) {
|
||||
expect(error.message).to.contain(
|
||||
'Undefined binding(s) detected when compiling ' +
|
||||
qbuilder._method.toUpperCase() +
|
||||
' query:'
|
||||
builder._method.toUpperCase() +
|
||||
`. Undefined column(s): [${undefinedColumns.join(', ')}] query:`
|
||||
); //This test is not for asserting correct queries
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('Any undefined binding in a RAW query should throw an error', function() {
|
||||
var expectedErrorMessageContains =
|
||||
'Undefined binding(s) detected when compiling RAW query:'; //This test is not for asserting correct queries
|
||||
var raws = [
|
||||
raw('?', [undefined]),
|
||||
raw(':col = :value', { col: 'test', value: void 0 }),
|
||||
raw('? = ?', ['test', void 0]),
|
||||
raw('? = ?', ['test', { test: void 0 }]),
|
||||
raw('?', [['test', void 0]]),
|
||||
{ query: raw('?', [undefined]), undefinedIndices: [0] },
|
||||
{ query: raw(':col = :value', { col: 'test', value: void 0 }), undefinedIndices: ['value'] },
|
||||
{ query: raw('? = ?', ['test', void 0]), undefinedIndices: [1] },
|
||||
{ query: raw('? = ?', ['test', { test: void 0 }]), undefinedIndices: [1] },
|
||||
{ query: raw('?', [['test', void 0]]), undefinedIndices: [0] },
|
||||
];
|
||||
raws.forEach(function(raw) {
|
||||
raws.forEach(function({ query, undefinedIndices }) {
|
||||
try {
|
||||
raw = raw.toSQL();
|
||||
query.toSQL();
|
||||
expect(true).to.equal(
|
||||
false,
|
||||
'Expected to throw error in compilation about undefined bindings.'
|
||||
);
|
||||
} catch (error) {
|
||||
var expectedErrorMessageContains =
|
||||
`Undefined binding(s) detected for keys [${undefinedIndices.join(', ')}] when compiling RAW query:`;
|
||||
expect(error.message).to.contain(expectedErrorMessageContains); //This test is not for asserting correct queries
|
||||
}
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user