budibase/packages/server/src/integrations/tests/sql.spec.js

333 lines
9.5 KiB
JavaScript
Raw Normal View History

const Sql = require("../base/sql")
2022-07-21 11:28:54 +02:00
const { SqlClients } = require("../utils")
const TABLE_NAME = "test"
function endpoint(table, operation) {
return {
datasourceId: "Postgres",
operation: operation,
entityId: table || TABLE_NAME,
}
}
function generateReadJson({ table, fields, filters, sort, paginate} = {}) {
return {
endpoint: endpoint(table || TABLE_NAME, "READ"),
resource: {
fields: fields || [],
},
filters: filters || {},
sort: sort || {},
paginate: paginate || {},
}
}
function generateCreateJson(table = TABLE_NAME, body = {}) {
return {
endpoint: endpoint(table, "CREATE"),
body,
}
}
function generateUpdateJson(table = TABLE_NAME, body = {}, filters = {}) {
return {
endpoint: endpoint(table, "UPDATE"),
filters,
body,
}
}
function generateDeleteJson(table = TABLE_NAME, filters = {}) {
return {
endpoint: endpoint(table, "DELETE"),
filters,
}
}
describe("SQL query builder", () => {
const limit = 500
2022-07-21 11:28:54 +02:00
const client = SqlClients.POSTGRES
let sql
beforeEach(() => {
sql = new Sql(client, limit)
})
it("should test a basic read", () => {
const query = sql._query(generateReadJson())
expect(query).toEqual({
bindings: [limit],
2021-09-23 17:56:13 +02:00
sql: `select * from (select * from "${TABLE_NAME}" limit $1) as "${TABLE_NAME}"`
})
})
it("should test a read with specific columns", () => {
2021-08-06 13:33:04 +02:00
const nameProp = `${TABLE_NAME}.name`, ageProp = `${TABLE_NAME}.age`
const query = sql._query(generateReadJson({
2021-08-06 13:33:04 +02:00
fields: [nameProp, ageProp]
}))
expect(query).toEqual({
bindings: [limit],
2021-09-23 17:56:13 +02:00
sql: `select "${TABLE_NAME}"."name" as "${nameProp}", "${TABLE_NAME}"."age" as "${ageProp}" from (select * from "${TABLE_NAME}" limit $1) as "${TABLE_NAME}"`
})
})
it("should test a where string starts with read", () => {
const query = sql._query(generateReadJson({
filters: {
string: {
name: "John",
}
}
}))
expect(query).toEqual({
bindings: ["John%", limit],
2021-09-23 17:56:13 +02:00
sql: `select * from (select * from "${TABLE_NAME}" where "${TABLE_NAME}"."name" ilike $1 limit $2) as "${TABLE_NAME}"`
})
})
it("should test a where range read", () => {
const query = sql._query(generateReadJson({
filters: {
range: {
age: {
low: 2,
high: 10,
}
}
}
}))
expect(query).toEqual({
bindings: [2, 10, limit],
2021-09-23 17:56:13 +02:00
sql: `select * from (select * from "${TABLE_NAME}" where "${TABLE_NAME}"."age" between $1 and $2 limit $3) as "${TABLE_NAME}"`
})
})
it("should test for multiple IDs with OR", () => {
const query = sql._query(generateReadJson({
filters: {
equal: {
age: 10,
name: "John",
},
allOr: true,
}
}))
expect(query).toEqual({
bindings: [10, "John", limit],
2021-09-23 17:56:13 +02:00
sql: `select * from (select * from "${TABLE_NAME}" where ("${TABLE_NAME}"."age" = $1) or ("${TABLE_NAME}"."name" = $2) limit $3) as "${TABLE_NAME}"`
})
})
it("should allow filtering on a related field", () => {
const query = sql._query(generateReadJson({
filters: {
equal: {
age: 10,
"task.name": "task 1",
},
},
}))
// order of bindings changes because relationship filters occur outside inner query
expect(query).toEqual({
bindings: [10, limit, "task 1"],
sql: `select * from (select * from "${TABLE_NAME}" where "${TABLE_NAME}"."age" = $1 limit $2) as "${TABLE_NAME}" where "task"."name" = $3`
})
})
it("should test an create statement", () => {
const query = sql._query(generateCreateJson(TABLE_NAME, {
name: "Michael",
age: 45,
}))
expect(query).toEqual({
bindings: [45, "Michael"],
sql: `insert into "${TABLE_NAME}" ("age", "name") values ($1, $2) returning *`
})
})
it("should test an update statement", () => {
const query = sql._query(generateUpdateJson(TABLE_NAME, {
name: "John"
}, {
equal: {
id: 1001,
}
}))
expect(query).toEqual({
bindings: ["John", 1001],
2021-07-02 15:49:47 +02:00
sql: `update "${TABLE_NAME}" set "name" = $1 where "${TABLE_NAME}"."id" = $2 returning *`
})
})
it("should test a delete statement", () => {
const query = sql._query(generateDeleteJson(TABLE_NAME, {
equal: {
id: 1001,
}
}))
expect(query).toEqual({
bindings: [1001],
2021-07-02 15:49:47 +02:00
sql: `delete from "${TABLE_NAME}" where "${TABLE_NAME}"."id" = $1 returning *`
})
})
it("should work with MS-SQL", () => {
2022-07-21 11:28:54 +02:00
const query = new Sql(SqlClients.MS_SQL, 10)._query(generateReadJson())
expect(query).toEqual({
bindings: [10],
2021-09-23 17:56:13 +02:00
sql: `select * from (select top (@p0) * from [${TABLE_NAME}]) as [${TABLE_NAME}]`
})
})
2022-07-21 11:28:54 +02:00
it("should work with MySQL", () => {
const query = new Sql(SqlClients.MY_SQL, 10)._query(generateReadJson())
expect(query).toEqual({
bindings: [10],
2021-09-23 17:56:13 +02:00
sql: `select * from (select * from \`${TABLE_NAME}\` limit ?) as \`${TABLE_NAME}\``
})
})
it("should use greater than when only low range specified", () => {
const date = new Date()
const query = sql._query(generateReadJson({
filters: {
range: {
property: {
low: date,
}
}
}
}))
expect(query).toEqual({
bindings: [date, limit],
sql: `select * from (select * from "${TABLE_NAME}" where "${TABLE_NAME}"."property" > $1 limit $2) as "${TABLE_NAME}"`
})
})
it("should use less than when only high range specified", () => {
const date = new Date()
const query = sql._query(generateReadJson({
filters: {
range: {
property: {
high: date,
}
}
}
}))
expect(query).toEqual({
bindings: [date, limit],
sql: `select * from (select * from "${TABLE_NAME}" where "${TABLE_NAME}"."property" < $1 limit $2) as "${TABLE_NAME}"`
})
})
it("should use greater than when only low range specified", () => {
const date = new Date()
const query = sql._query(generateReadJson({
filters: {
range: {
property: {
low: date,
}
}
}
}))
expect(query).toEqual({
bindings: [date, limit],
sql: `select * from (select * from "${TABLE_NAME}" where "${TABLE_NAME}"."property" > $1 limit $2) as "${TABLE_NAME}"`
})
})
2022-07-21 11:28:54 +02:00
it("should use like expression for MS-SQL when filter is contains", () => {
const query = new Sql(SqlClients.MS_SQL, 10)._query(generateReadJson({
filters: {
contains: {
2022-07-26 17:58:01 +02:00
age: [20],
name: ["John"]
2022-07-21 11:28:54 +02:00
}
}
}))
expect(query).toEqual({
2022-07-26 17:58:01 +02:00
bindings: [10, "%20%", `%"John"%`],
2022-07-21 11:28:54 +02:00
sql: `select * from (select top (@p0) * from [${TABLE_NAME}] where LOWER(${TABLE_NAME}.age) LIKE @p1 and LOWER(${TABLE_NAME}.name) LIKE @p2) as [${TABLE_NAME}]`
})
})
it("should use JSON_CONTAINS expression for MySQL when filter is contains", () => {
const query = new Sql(SqlClients.MY_SQL, 10)._query(generateReadJson({
filters: {
contains: {
2022-07-26 17:58:01 +02:00
age: [20],
name: ["John"]
2022-07-21 11:28:54 +02:00
}
}
}))
expect(query).toEqual({
bindings: [10],
2022-07-26 17:58:01 +02:00
sql: `select * from (select * from \`${TABLE_NAME}\` where JSON_CONTAINS(${TABLE_NAME}.age, '[20]') and JSON_CONTAINS(${TABLE_NAME}.name, '["John"]') limit ?) as \`${TABLE_NAME}\``
2022-07-21 11:28:54 +02:00
})
})
it("should use jsonb operator expression for PostgreSQL when filter is contains", () => {
const query = new Sql(SqlClients.POSTGRES, 10)._query(generateReadJson({
filters: {
contains: {
2022-07-26 17:58:01 +02:00
age: [20],
name: ["John"]
2022-07-21 11:28:54 +02:00
}
}
}))
expect(query).toEqual({
bindings: [10],
sql: `select * from (select * from \"${TABLE_NAME}\" where \"${TABLE_NAME}\".\"age\"::jsonb @> '[20]' and \"${TABLE_NAME}\".\"name\"::jsonb @> '["John"]' limit $1) as \"${TABLE_NAME}\"`
})
})
2022-07-27 12:40:46 +02:00
2022-07-27 12:56:57 +02:00
it("should use NOT like expression for MS-SQL when filter is notContains", () => {
2022-07-27 12:40:46 +02:00
const query = new Sql(SqlClients.MS_SQL, 10)._query(generateReadJson({
filters: {
notContains: {
age: [20],
name: ["John"]
}
}
}))
expect(query).toEqual({
bindings: [10, "%20%", `%"John"%`],
2022-07-27 12:56:57 +02:00
sql: `select * from (select top (@p0) * from [${TABLE_NAME}] where NOT (LOWER(${TABLE_NAME}.age) LIKE @p1) and NOT (LOWER(${TABLE_NAME}.name) LIKE @p2)) as [${TABLE_NAME}]`
2022-07-27 12:40:46 +02:00
})
})
it("should use NOT JSON_CONTAINS expression for MySQL when filter is notContains", () => {
const query = new Sql(SqlClients.MY_SQL, 10)._query(generateReadJson({
filters: {
notContains: {
age: [20],
name: ["John"]
}
}
}))
expect(query).toEqual({
bindings: [10],
sql: `select * from (select * from \`${TABLE_NAME}\` where NOT JSON_CONTAINS(${TABLE_NAME}.age, '[20]') and NOT JSON_CONTAINS(${TABLE_NAME}.name, '["John"]') limit ?) as \`${TABLE_NAME}\``
})
})
2022-07-27 12:49:45 +02:00
it("should use jsonb operator NOT expression for PostgreSQL when filter is notContains", () => {
2022-07-27 12:40:46 +02:00
const query = new Sql(SqlClients.POSTGRES, 10)._query(generateReadJson({
filters: {
notContains: {
age: [20],
name: ["John"]
}
}
}))
expect(query).toEqual({
bindings: [10],
2022-07-27 12:49:45 +02:00
sql: `select * from (select * from \"${TABLE_NAME}\" where NOT \"${TABLE_NAME}\".\"age\"::jsonb @> '[20]' and NOT \"${TABLE_NAME}\".\"name\"::jsonb @> '["John"]' limit $1) as \"${TABLE_NAME}\"`
2022-07-27 12:40:46 +02:00
})
})
})