Mustafa Balila 9659a20753
Fix whereILike issue with sqlite (#5604) (#5687)
Co-authored-by: Raz Luvaton <16746759+rluvaton@users.noreply.github.com>
2024-04-07 17:33:41 +03:00

910 lines
28 KiB
JavaScript

'use strict';
const { expect } = require('chai');
require('assert');
require('../../../../lib/execution/runner');
const {
isMysql,
isPostgreSQL,
isMssql,
isSQLite,
isOracle,
} = require('../../../util/db-helpers');
const {
createUsers,
createAccounts,
createCompositeKeyTable,
createTestTableTwo,
dropTables,
createDefaultTable,
createCities,
} = require('../../../util/tableCreatorHelper');
const {
insertAccounts,
insertCities,
} = require('../../../util/dataInsertHelper');
const {
getAllDbs,
getKnexForDb,
} = require('../../util/knex-instance-provider');
const logger = require('../../../integration/logger');
const { TEST_TIMESTAMP } = require('../../../util/constants.js');
const { assertJsonEquals } = require('../../../util/assertHelper');
describe('Where', function () {
getAllDbs().forEach((db) => {
describe(db, () => {
let knex;
before(async () => {
knex = logger(getKnexForDb(db));
await dropTables(knex);
await createUsers(knex);
await createAccounts(knex);
await createCompositeKeyTable(knex);
await createTestTableTwo(knex);
await createDefaultTable(knex);
await createDefaultTable(knex, true);
});
beforeEach(async () => {
await knex('accounts').truncate();
await insertAccounts(knex);
});
after(async () => {
return knex.destroy();
});
describe('simple "where" cases', function () {
it('allows key, value', async function () {
await knex('accounts')
.where('id', 1)
.select('first_name', 'last_name')
.testSql(function (tester) {
tester(
['mysql', 'sqlite3'],
'select `first_name`, `last_name` from `accounts` where `id` = ?',
[1],
[
{
first_name: 'Test',
last_name: 'User',
},
]
);
tester(
['pg', 'pgnative', 'pg-redshift', 'oracledb'],
'select "first_name", "last_name" from "accounts" where "id" = ?',
[1],
[
{
first_name: 'Test',
last_name: 'User',
},
]
);
tester(
'mssql',
'select [first_name], [last_name] from [accounts] where [id] = ?',
[1],
[
{
first_name: 'Test',
last_name: 'User',
},
]
);
});
});
it('allows key, operator, value', async function () {
await knex('accounts')
.where('id', 1)
.select('first_name', 'last_name')
.testSql(function (tester) {
tester(
['mysql', 'sqlite3'],
'select `first_name`, `last_name` from `accounts` where `id` = ?',
[1],
[
{
first_name: 'Test',
last_name: 'User',
},
]
);
tester(
['pg', 'pgnative', 'pg-redshift', 'oracledb'],
'select "first_name", "last_name" from "accounts" where "id" = ?',
[1],
[
{
first_name: 'Test',
last_name: 'User',
},
]
);
tester(
'mssql',
'select [first_name], [last_name] from [accounts] where [id] = ?',
[1],
[
{
first_name: 'Test',
last_name: 'User',
},
]
);
});
});
it('allows selecting columns with an array', async function () {
await knex('accounts')
.where('id', '>', 1)
.select(['email', 'logins'])
.testSql(function (tester) {
tester(
['mysql', 'sqlite3'],
'select `email`, `logins` from `accounts` where `id` > ?',
[1]
);
tester(
['pg', 'pgnative', 'pg-redshift', 'oracledb'],
'select "email", "logins" from "accounts" where "id" > ?',
[1]
);
tester(
'mssql',
'select [email], [logins] from [accounts] where [id] > ?',
[1]
);
});
});
it('allows a hash of where attrs', async function () {
await knex('accounts')
.where({ id: 1 })
.select('*')
.testSql(function (tester) {
tester(
['mysql', 'sqlite3'],
'select * from `accounts` where `id` = ?',
[1],
[
{
id: 1,
first_name: 'Test',
last_name: 'User',
email: 'test1@example.com',
logins: 1,
balance: 0,
about: 'Lorem ipsum Dolore labore incididunt enim.',
created_at: TEST_TIMESTAMP,
updated_at: TEST_TIMESTAMP,
phone: null,
},
]
);
tester(
['pg', 'pgnative', 'pg-redshift', 'oracledb'],
'select * from "accounts" where "id" = ?',
[1],
[
{
id: '1',
first_name: 'Test',
last_name: 'User',
email: 'test1@example.com',
logins: 1,
balance: 0,
about: 'Lorem ipsum Dolore labore incididunt enim.',
created_at: TEST_TIMESTAMP,
updated_at: TEST_TIMESTAMP,
phone: null,
},
]
);
tester(
'mssql',
'select * from [accounts] where [id] = ?',
[1],
[
{
id: '1',
first_name: 'Test',
last_name: 'User',
email: 'test1@example.com',
logins: 1,
balance: 0,
about: 'Lorem ipsum Dolore labore incididunt enim.',
created_at: TEST_TIMESTAMP,
updated_at: TEST_TIMESTAMP,
phone: null,
},
]
);
});
});
it('allows where id: undefined or id: null as a where null clause', async function () {
await knex('accounts')
.where({ id: null })
.select('first_name', 'email')
.testSql(function (tester) {
tester(
['mysql', 'sqlite3'],
'select `first_name`, `email` from `accounts` where `id` is null',
[],
[]
);
tester(
['pg', 'pgnative', 'pg-redshift', 'oracledb', 'cockroachdb'],
'select "first_name", "email" from "accounts" where "id" is null',
[],
[]
);
tester(
'mssql',
'select [first_name], [email] from [accounts] where [id] is null',
[],
[]
);
});
});
it('allows where id = 0', async function () {
await knex('accounts')
.where({ id: 0 })
.select()
.testSql(function (tester) {
tester(
['mysql', 'sqlite3'],
'select * from `accounts` where `id` = ?',
[0],
[]
);
tester(
['pg', 'pgnative', 'pg-redshift', 'oracledb', 'cockroachdb'],
'select * from "accounts" where "id" = ?',
[0],
[]
);
tester(
'mssql',
'select * from [accounts] where [id] = ?',
[0],
[]
);
});
});
});
it('does "orWhere" cases', async function () {
await knex('accounts')
.where('id', 1)
.orWhere('id', '>', 2)
.select('first_name', 'last_name');
});
it('does "andWhere" cases', async function () {
await knex('accounts')
.select('first_name', 'last_name', 'about')
.where('id', 1)
.andWhere('email', 'test1@example.com');
});
it('takes a function to wrap nested where statements', async () => {
await knex('accounts')
.where(function () {
this.where('id', 2);
this.orWhere('id', 3);
})
.select('*');
});
it('handles "where in" cases', async () => {
await knex('accounts').whereIn('id', [1, 2, 3]).select();
});
it('handles "or where in" cases', async () => {
await knex('accounts')
.where('email', 'test1@example.com')
.orWhereIn('id', [2, 3, 4])
.select();
});
it('handles multi-column "where in" cases', async function () {
await knex('composite_key_test').insert([
{
column_a: 1,
column_b: 1,
details: 'One, One, One',
status: 1,
},
{
column_a: 1,
column_b: 2,
details: 'One, Two, Zero',
status: 0,
},
{
column_a: 2,
column_b: 2,
details: 'Two, Two, Zero',
status: 0,
},
]);
if (!isMssql(knex)) {
await knex('composite_key_test')
.whereIn(
['column_a', 'column_b'],
[
[1, 1],
[1, 2],
]
)
.orderBy('status', 'desc')
.select()
.testSql(function (tester) {
tester(
'mysql',
'select * from `composite_key_test` where (`column_a`, `column_b`) in ((?, ?), (?, ?)) order by `status` desc',
[1, 1, 1, 2],
[
{
column_a: 1,
column_b: 1,
details: 'One, One, One',
status: 1,
},
{
column_a: 1,
column_b: 2,
details: 'One, Two, Zero',
status: 0,
},
]
);
tester(
'sqlite3',
'select * from `composite_key_test` where (`column_a`, `column_b`) in ( values (?, ?), (?, ?)) order by `status` desc',
[1, 1, 1, 2],
[
{
column_a: 1,
column_b: 1,
details: 'One, One, One',
status: 1,
},
{
column_a: 1,
column_b: 2,
details: 'One, Two, Zero',
status: 0,
},
]
);
tester(
['pg', 'pgnative', 'pg-redshift', 'oracledb'],
'select * from "composite_key_test" where ("column_a", "column_b") in ((?, ?), (?, ?)) order by "status" desc',
[1, 1, 1, 2],
[
{
column_a: 1,
column_b: 1,
details: 'One, One, One',
status: 1,
},
{
column_a: 1,
column_b: 2,
details: 'One, Two, Zero',
status: 0,
},
]
);
tester(
'cockroachdb',
'select * from "composite_key_test" where ("column_a", "column_b") in ((?, ?), (?, ?)) order by "status" desc',
[1, 1, 1, 2],
[
{
column_a: '1',
column_b: '1',
details: 'One, One, One',
status: 1,
},
{
column_a: '1',
column_b: '2',
details: 'One, Two, Zero',
status: 0,
},
]
);
});
}
});
it('handles multi-column "where in" cases with where', async function () {
if (!isSQLite(knex) && !isMssql(knex)) {
await knex('composite_key_test')
.where('status', 1)
.whereIn(
['column_a', 'column_b'],
[
[1, 1],
[1, 2],
]
)
.select()
.testSql(function (tester) {
tester(
'mysql',
'select * from `composite_key_test` where `status` = ? and (`column_a`, `column_b`) in ((?, ?), (?, ?))',
[1, 1, 1, 1, 2],
[
{
column_a: 1,
column_b: 1,
details: 'One, One, One',
status: 1,
},
]
);
tester(
['pg', 'pgnative', 'pg-redshift', 'oracledb'],
'select * from "composite_key_test" where "status" = ? and ("column_a", "column_b") in ((?, ?), (?, ?))',
[1, 1, 1, 1, 2],
[
{
column_a: 1,
column_b: 1,
details: 'One, One, One',
status: 1,
},
]
);
tester(
'cockroachdb',
'select * from "composite_key_test" where "status" = ? and ("column_a", "column_b") in ((?, ?), (?, ?))',
[1, 1, 1, 1, 2],
[
{
column_a: '1',
column_b: '1',
details: 'One, One, One',
status: 1,
},
]
);
});
}
});
it('handles "where exists"', async () => {
await knex('accounts')
.whereExists(function () {
this.select('id').from('test_table_two').where({ id: 1 });
})
.select();
});
it('handles "where between"', async () => {
await knex('accounts').whereBetween('id', [1, 100]).select();
});
it('handles "or where between"', async () => {
await knex('accounts')
.whereBetween('id', [1, 100])
.orWhereBetween('id', [200, 300])
.select();
});
it('does where(raw)', async function () {
if (isOracle(knex)) {
// special case for oracle
await knex('accounts')
.whereExists(function () {
this.select(knex.raw(1))
.from('test_table_two')
.where(
knex.raw('"test_table_two"."account_id" = "accounts"."id"')
);
})
.select();
} else {
await knex('accounts')
.whereExists(function () {
this.select(knex.raw(1))
.from('test_table_two')
.where(knex.raw('test_table_two.account_id = accounts.id'));
})
.select();
}
});
it('does sub-selects', async () => {
await knex('accounts')
.whereIn('id', function () {
this.select('account_id').from('test_table_two').where('status', 1);
})
.select('first_name', 'last_name');
});
it('supports the <> operator', async () => {
await knex('accounts').where('id', '<>', 2).select('email', 'logins');
});
it('Allows for knex.Raw passed to the `where` clause', async () => {
if (isOracle(knex)) {
await knex('accounts')
.where(knex.raw('"id" = 2'))
.select('email', 'logins');
} else {
await knex('accounts')
.where(knex.raw('id = 2'))
.select('email', 'logins');
}
});
describe('where like', async function () {
beforeEach(function () {
if (
!(
isPostgreSQL(knex) ||
isSQLite(knex) ||
isMssql(knex) ||
isMysql(knex)
)
) {
return this.skip();
}
});
it('finds data using whereILike', async () => {
const result = await knex('accounts')
.select('*')
.whereILike('email', 'test1%');
expect(result[0].email).to.equal('test1@example.com');
});
it('finds data using whereILike when different case sensitivity', async () => {
const result = await knex('accounts').whereILike('email', 'TEST1%');
expect(result[0].email).to.equal('test1@example.com');
});
it('finds data using whereLike', async () => {
const result = await knex('accounts')
.select('*')
.whereLike('email', 'test1%');
expect(result[0].email).to.equal('test1@example.com');
});
it('finds data using orWhereLike', async () => {
const result = await knex('accounts')
.select('*')
.whereLike('email', 'test1%')
.orWhereLike('email', 'test2%');
expect(result[0].email).to.equal('test1@example.com');
expect(result[1].email).to.equal('test2@example.com');
});
it('finds data using andWhereLike', async () => {
const result = await knex('accounts')
.select('*')
.whereLike('first_name', 'Te%')
.andWhereLike('email', '%example.com');
expect(result.length).to.equal(8);
expect(result[0].email).to.equal('test1@example.com');
expect(result[1].email).to.equal('test2@example.com');
expect(result[2].email).to.equal('test3@example.com');
expect(result[3].email).to.equal('test4@example.com');
expect(result[4].email).to.equal('test5@example.com');
expect(result[5].email).to.equal('test6@example.com');
expect(result[6].email).to.equal('test7@example.com');
expect(result[7].email).to.equal('test8@example.com');
});
it('finds data using orWhereILike', async () => {
const result = await knex('accounts')
.select('*')
.whereILike('email', 'TEST1%')
.orWhereILike('email', 'TeSt2%');
expect(result[0].email).to.equal('test1@example.com');
expect(result[1].email).to.equal('test2@example.com');
});
it('finds data using andWhereILike', async () => {
const result = await knex('accounts')
.select('*')
.whereILike('first_name', 'te%')
.andWhereILike('email', '%examPle.COm');
expect(result.length).to.equal(8);
expect(result[0].email).to.equal('test1@example.com');
expect(result[1].email).to.equal('test2@example.com');
expect(result[2].email).to.equal('test3@example.com');
expect(result[3].email).to.equal('test4@example.com');
expect(result[4].email).to.equal('test5@example.com');
expect(result[5].email).to.equal('test6@example.com');
expect(result[6].email).to.equal('test7@example.com');
expect(result[7].email).to.equal('test8@example.com');
});
it("doesn't find data using whereLike when different case sensitivity", async function () {
const result = await knex('accounts').whereLike('email', 'Test1%');
// sqlite only supports case-insensitive search
if (isSQLite(knex)) {
this.skip();
}
expect(result).to.deep.equal([]);
});
it('supports only case-insensitive searches in sqlite', async function () {
const result = await knex('accounts').whereILike('email', 'Test1%');
if (!isSQLite(knex)) {
this.skip();
}
expect(result.length).to.equal(1);
expect(result[0].email).to.equal('test1@example.com');
});
});
it('Retains array bindings, #228', function () {
const raw = knex.raw(
'select * from table t where t.id = ANY( ?::int[] )',
[[1, 2, 3]]
);
const raw2 = knex.raw('select "stored_procedure"(?, ?, ?)', [
1,
2,
['a', 'b', 'c'],
]);
const expected1 = [[1, 2, 3]];
const expected2 = [1, 2, ['a', 'b', 'c']];
expect(raw.toSQL().bindings).to.eql(
knex.client.prepBindings(expected1)
);
expect(raw2.toSQL().bindings).to.eql(
knex.client.prepBindings(expected2)
);
//Also expect raw's bindings to not have been modified by calling .toSQL() (preserving original bindings)
expect(raw.bindings).to.eql(expected1);
expect(raw2.bindings).to.eql(expected2);
});
describe('json wheres', () => {
before(async () => {
await knex.schema.dropTableIfExists('cities');
await createCities(knex);
});
beforeEach(async () => {
await knex('cities').truncate();
await insertCities(knex);
});
it('where json object', async () => {
const result = await knex('cities')
.select('name')
.whereJsonObject('descriptions', {
type: 'bigcity',
short: 'beautiful city',
long: 'beautiful and dirty city',
});
expect(result[0]).to.eql({
name: 'Paris',
});
});
it('where json object with string object', async () => {
const result = await knex('cities')
.select('name')
.whereJsonObject(
'descriptions',
`{
"type": "bigcity",
"short": "beautiful city",
"long": "beautiful and dirty city"
}`
);
expect(result[0]).to.eql({
name: 'Paris',
});
});
it('where not json object', async () => {
const result = await knex('cities')
.select('name')
.whereNotJsonObject('descriptions', {
type: 'bigcity',
short: 'beautiful city',
long: 'beautiful and dirty city',
});
expect(result.length).to.equal(2);
assertJsonEquals(result, [
{
name: 'Milan',
},
{
name: 'Oslo',
},
]);
});
it('where json path greater than numeric', async () => {
const result = await knex('cities')
.select('name')
.whereJsonPath('statistics', '$.roads.min', '>', 1000);
expect(result[0]).to.eql({
name: 'Paris',
});
});
it('where json path equal numeric', async () => {
const result = await knex('cities')
.select('name')
.whereJsonPath('statistics', '$.roads.min', '=', 1455);
expect(result[0]).to.eql({
name: 'Milan',
});
});
it('where and or where json path equal numeric', async () => {
const result = await knex('cities')
.select('name')
.whereJsonPath('statistics', '$.roads.min', '=', 1455)
.orWhereJsonPath('statistics', '$.roads.min', '=', 1655);
expect(result[0]).to.eql({
name: 'Milan',
});
});
it('where json path equal string starting with number', async () => {
const result = await knex('cities')
.select('name')
.whereJsonPath(
'statistics',
'$.statisticId',
'=',
'6qITEHRUNJ4bdAmA0lk82'
);
expect(result[0]).to.eql({
name: 'Paris',
});
});
it('where multiple json path', async () => {
const result = await knex('cities')
.select('name')
.whereJsonPath('statistics', '$.roads.min', '<', 2000)
.andWhereJsonPath('temperature', '$.desc', '=', 'cold');
expect(result.length).to.equal(1);
assertJsonEquals(result, [
{
name: 'Paris',
},
]);
});
it('where json superset of', async function () {
if (!(isPostgreSQL(knex) || isMysql(knex))) {
this.skip();
}
const result = await knex('cities')
.select('name')
// where descriptions json object contains type : 'bigcity'
.whereJsonSupersetOf('descriptions', {
type: 'bigcity',
});
expect(result.length).to.equal(2);
assertJsonEquals(result, [
{
name: 'Paris',
},
{
name: 'Milan',
},
]);
});
it('or where json superset of', async function () {
if (!(isPostgreSQL(knex) || isMysql(knex))) {
this.skip();
}
const result = await knex('cities')
.select('name')
// where descriptions json object contains type : 'bigcity' or 'city'
.whereJsonSupersetOf('descriptions', {
type: 'bigcity',
})
.orWhereJsonSupersetOf('descriptions', {
type: 'city',
});
expect(result.length).to.equal(3);
assertJsonEquals(result, [
{
name: 'Paris',
},
{
name: 'Milan',
},
{
name: 'Oslo',
},
]);
});
it('where json superset of with string', async function () {
if (!(isPostgreSQL(knex) || isMysql(knex))) {
this.skip();
}
const result = await knex('cities')
.select('name')
.whereJsonSupersetOf('descriptions', '{"type": "bigcity"}');
expect(result.length).to.equal(2);
assertJsonEquals(result, [
{
name: 'Paris',
},
{
name: 'Milan',
},
]);
});
it('where json subset of', async function () {
if (!(isPostgreSQL(knex) || isMysql(knex))) {
this.skip();
}
const result = await knex('cities')
.select('name')
// where temperature json object is included in given object
.whereJsonSubsetOf('temperature', {
desc: 'cold',
desc2: 'very cold',
});
expect(result.length).to.equal(1);
assertJsonEquals(result, [
{
name: 'Paris', // contains only desc: 'cold' but it's matched
},
]);
});
it('or where json subset of', async function () {
if (!(isPostgreSQL(knex) || isMysql(knex))) {
this.skip();
}
const result = await knex('cities')
.select('name')
// where temperature json object is included in given object
.whereJsonSubsetOf('temperature', {
desc: 'cold',
desc2: 'very cold',
})
.orWhereJsonSubsetOf('temperature', {
desc: 'warm',
desc2: 'very warm',
});
expect(result.length).to.equal(2);
assertJsonEquals(result, [
{
name: 'Paris',
},
{
name: 'Milan',
},
]);
});
});
});
});
});