From 88ea3fdc07fc2a826907645520a6da5ab5353997 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Mon, 22 Jul 2024 21:00:20 +0100 Subject: [PATCH] unit tests and logic added to prevent coalesce being used by oracle --- packages/backend-core/src/sql/sql.ts | 5 + packages/backend-core/src/sql/utils.ts | 6 +- .../server/src/integrations/tests/sql.spec.ts | 109 +++++++++++++++--- 3 files changed, 100 insertions(+), 20 deletions(-) diff --git a/packages/backend-core/src/sql/sql.ts b/packages/backend-core/src/sql/sql.ts index a4b924bf54..327f7955cb 100644 --- a/packages/backend-core/src/sql/sql.ts +++ b/packages/backend-core/src/sql/sql.ts @@ -435,6 +435,11 @@ class InternalBuilder { )} = ? THEN 1 ELSE 0 END = 1`, [value] ) + } else if (SqlClient.ORACLE) { + query = query[fnc]( + `${quotedIdentifier(this.client, key)} = ?`, + [value] + ) } else { query = query[fnc]( `COALESCE(${quotedIdentifier(this.client, key)} = ?, FALSE)`, diff --git a/packages/backend-core/src/sql/utils.ts b/packages/backend-core/src/sql/utils.ts index 67b5d2081b..71959b5661 100644 --- a/packages/backend-core/src/sql/utils.ts +++ b/packages/backend-core/src/sql/utils.ts @@ -153,9 +153,9 @@ export function isValidTime(value: string) { } export function sqlLog(client: string, query: string, values?: any[]) { - if (!environment.SQL_LOGGING_ENABLE) { - return - } + // if (!environment.SQL_LOGGING_ENABLE) { + // return + // } let string = `[SQL] [${client.toUpperCase()}] query="${query}"` if (values) { string += ` values="${values.join(", ")}"` diff --git a/packages/server/src/integrations/tests/sql.spec.ts b/packages/server/src/integrations/tests/sql.spec.ts index b595508093..a408836da0 100644 --- a/packages/server/src/integrations/tests/sql.spec.ts +++ b/packages/server/src/integrations/tests/sql.spec.ts @@ -34,13 +34,13 @@ function endpoint(table: any, operation: any) { } function generateReadJson({ - table, - fields, - filters, - sort, - paginate, -}: any = {}): QueryJson { - const tableObj = { ...TABLE } + table, + fields, + filters, + sort, + paginate, + }: any = {}): QueryJson { + const tableObj = {...TABLE} if (table) { tableObj.name = table } @@ -85,7 +85,7 @@ function generateRelationshipJson(config: { schema?: string } = {}): QueryJson { column: "products", }, ], - extra: { idFilter: {} }, + extra: {idFilter: {}}, meta: { table: TABLE, }, @@ -122,7 +122,7 @@ function generateManyRelationshipJson(config: { schema?: string } = {}) { toPrimary: "product_id", }, ], - extra: { idFilter: {} }, + extra: {idFilter: {}}, meta: { table: TABLE, }, @@ -139,10 +139,17 @@ describe("SQL query builder", () => { }) it("should add the schema to the LEFT JOIN", () => { - const query = sql._query(generateRelationshipJson({ schema: "production" })) + const query = sql._query(generateRelationshipJson({schema: "production"})) expect(query).toEqual({ bindings: [500, 5000], - sql: `select "brands"."brand_id" as "brands.brand_id", "brands"."brand_name" as "brands.brand_name", "products"."product_id" as "products.product_id", "products"."product_name" as "products.product_name", "products"."brand_id" as "products.brand_id" from (select * from "production"."brands" order by "test"."id" asc limit $1) as "brands" left join "production"."products" as "products" on "brands"."brand_id" = "products"."brand_id" order by "test"."id" asc limit $2`, + sql: `select "brands"."brand_id" as "brands.brand_id", + "brands"."brand_name" as "brands.brand_name", + "products"."product_id" as "products.product_id", + "products"."product_name" as "products.product_name", + "products"."brand_id" as "products.brand_id" + from (select * from "production"."brands" order by "test"."id" asc limit $1) as "brands" + left join "production"."products" as "products" on "brands"."brand_id" = "products"."brand_id" + order by "test"."id" asc limit $2`, }) }) @@ -150,17 +157,31 @@ describe("SQL query builder", () => { const query = sql._query(generateRelationshipJson()) expect(query).toEqual({ bindings: [500, 5000], - sql: `select "brands"."brand_id" as "brands.brand_id", "brands"."brand_name" as "brands.brand_name", "products"."product_id" as "products.product_id", "products"."product_name" as "products.product_name", "products"."brand_id" as "products.brand_id" from (select * from "brands" order by "test"."id" asc limit $1) as "brands" left join "products" as "products" on "brands"."brand_id" = "products"."brand_id" order by "test"."id" asc limit $2`, + sql: `select "brands"."brand_id" as "brands.brand_id", + "brands"."brand_name" as "brands.brand_name", + "products"."product_id" as "products.product_id", + "products"."product_name" as "products.product_name", + "products"."brand_id" as "products.brand_id" + from (select * from "brands" order by "test"."id" asc limit $1) as "brands" + left join "products" as "products" on "brands"."brand_id" = "products"."brand_id" + order by "test"."id" asc limit $2`, }) }) it("should add the schema to both the toTable and throughTable in many-to-many join", () => { const query = sql._query( - generateManyRelationshipJson({ schema: "production" }) + generateManyRelationshipJson({schema: "production"}) ) expect(query).toEqual({ bindings: [500, 5000], - sql: `select "stores"."store_id" as "stores.store_id", "stores"."store_name" as "stores.store_name", "products"."product_id" as "products.product_id", "products"."product_name" as "products.product_name" from (select * from "production"."stores" order by "test"."id" asc limit $1) as "stores" left join "production"."stocks" as "stocks" on "stores"."store_id" = "stocks"."store_id" left join "production"."products" as "products" on "products"."product_id" = "stocks"."product_id" order by "test"."id" asc limit $2`, + sql: `select "stores"."store_id" as "stores.store_id", + "stores"."store_name" as "stores.store_name", + "products"."product_id" as "products.product_id", + "products"."product_name" as "products.product_name" + from (select * from "production"."stores" order by "test"."id" asc limit $1) as "stores" + left join "production"."stocks" as "stocks" on "stores"."store_id" = "stocks"."store_id" + left join "production"."products" as "products" on "products"."product_id" = "stocks"."product_id" + order by "test"."id" asc limit $2`, }) }) @@ -176,7 +197,13 @@ describe("SQL query builder", () => { ) expect(query).toEqual({ bindings: ["john%", limit, 5000], - sql: `select * from (select * from (select * from (select * from "test" where LOWER("test"."name") LIKE :1 order by "test"."id" asc) where rownum <= :2) "test" order by "test"."id" asc) where rownum <= :3`, + sql: `select * + from (select * + from (select * + from (select * from "test" where LOWER("test"."name") LIKE :1 order by "test"."id" asc) + where rownum <= :2) "test" + order by "test"."id" asc) + where rownum <= :3`, }) query = new Sql(SqlClient.ORACLE, limit)._query( @@ -191,7 +218,17 @@ describe("SQL query builder", () => { ) expect(query).toEqual({ bindings: ["%20%", "%25%", `%"john"%`, `%"mary"%`, limit, 5000], - sql: `select * from (select * from (select * from (select * from "test" where (COALESCE(LOWER("test"."age"), '') LIKE :1 AND COALESCE(LOWER("test"."age"), '') LIKE :2) and (COALESCE(LOWER("test"."name"), '') LIKE :3 AND COALESCE(LOWER("test"."name"), '') LIKE :4) order by "test"."id" asc) where rownum <= :5) "test" order by "test"."id" asc) where rownum <= :6`, + sql: `select * + from (select * + from (select * + from (select * + from "test" + where (COALESCE(LOWER("test"."age"), '') LIKE :1 AND COALESCE(LOWER("test"."age"), '') LIKE :2) + and (COALESCE(LOWER("test"."name"), '') LIKE :3 AND COALESCE(LOWER("test"."name"), '') LIKE :4) + order by "test"."id" asc) + where rownum <= :5) "test" + order by "test"."id" asc) + where rownum <= :6`, }) query = new Sql(SqlClient.ORACLE, limit)._query( @@ -205,7 +242,45 @@ describe("SQL query builder", () => { ) expect(query).toEqual({ bindings: [`%jo%`, limit, 5000], - sql: `select * from (select * from (select * from (select * from "test" where LOWER("test"."name") LIKE :1 order by "test"."id" asc) where rownum <= :2) "test" order by "test"."id" asc) where rownum <= :3`, + sql: `select * + from (select * + from (select * + from (select * from "test" where LOWER("test"."name") LIKE :1 order by "test"."id" asc) + where rownum <= :2) "test" + order by "test"."id" asc) + where rownum <= :3`, + }) + }) + + it("should not use a coalesce query for oracle when using the equals filter", () => { + let query = new Sql(SqlClient.ORACLE, limit)._query( + generateReadJson({ + filters: { + equal: { + name: "John", + }, + }, + })) + + expect(query).not.toEqual({ + bindings: ["John", limit, 5000], + sql: `select * from (select * from (select * from (select * from "test" where COALESCE("test"."id" = :2, FALSE) order by "test"."id" asc) where rownum <= :2) "test" order by "test"."id" asc) where rownum <= :3`, + }) + }) + + it("should use a direct equality query for oracle when using the equals filter", () => { + let query = new Sql(SqlClient.ORACLE, limit)._query( + generateReadJson({ + filters: { + equal: { + name: "John", + }, + }, + })) + + expect(query).toEqual({ + bindings: ["John", limit, 5000], + sql: `select * from (select * from (select * from (select * from "test" where "test"."name" = :1 order by "test"."id" asc) where rownum <= :2) "test" order by "test"."id" asc) where rownum <= :3`, }) }) })