From 77889091d78f559b1a844dd0c463af80b5975fb5 Mon Sep 17 00:00:00 2001 From: Patrice Bender Date: Tue, 10 Dec 2024 14:31:05 +0100 Subject: [PATCH 1/5] fix: add missing date function mapping --- postgres/lib/cql-functions.js | 1 + 1 file changed, 1 insertion(+) diff --git a/postgres/lib/cql-functions.js b/postgres/lib/cql-functions.js index 6c91c152b..1dbd16870 100644 --- a/postgres/lib/cql-functions.js +++ b/postgres/lib/cql-functions.js @@ -20,6 +20,7 @@ const StandardFunctions = { month: x => `date_part('month', ${castVal(x)})`, day: x => `date_part('day', ${castVal(x)})`, time: x => `to_char(${castVal(x)}, 'HH24:MI:SS')`, + date: x => `to_char(${castVal(x)}, 'YYYY-MM-DD')`, hour: x => `date_part('hour', ${castVal(x)})`, minute: x => `date_part('minute', ${castVal(x)})`, second: x => `floor(date_part('second', ${castVal(x)}))`, From ff71fcbb18a839fc795a18a28bd34533ba2fa8ed Mon Sep 17 00:00:00 2001 From: Patrice Bender Date: Fri, 13 Dec 2024 12:48:47 +0100 Subject: [PATCH 2/5] add missing totalseconds 4 hana / correct 4 pg --- db-service/lib/cql-functions.js | 6 ++--- hana/lib/cql-functions.js | 40 ++++++++++++++++++++++++++++++++- postgres/lib/cql-functions.js | 30 +++++++++++-------------- 3 files changed, 55 insertions(+), 21 deletions(-) diff --git a/db-service/lib/cql-functions.js b/db-service/lib/cql-functions.js index 2f1f8f7f0..0f53234d7 100644 --- a/db-service/lib/cql-functions.js +++ b/db-service/lib/cql-functions.js @@ -224,16 +224,16 @@ const StandardFunctions = { // odata spec defines the value format for totalseconds as a duration like: P12DT23H59M59.999999999999S // P -> duration indicator - // D -> days, T -> Time seperator, H -> hours, M -> minutes, S -> fractional seconds + // D -> days, T -> Time separator, H -> hours, M -> minutes, S -> fractional seconds // By splitting the DT and calculating the seconds of the time separate from the day // it possible to determine the full amount of seconds by adding them together as fractionals and multiplying // the number of seconds in a day // As sqlite is most accurate with juliandays it is better to do then then using actual second function // while the odata specification states that the seconds has to be fractional which only julianday allows /** - * Generates SQL statement that produces an OData compliant duration string like: P12DT23H59M59.999999999999S + * Generates SQL statement that produces a number for a OData compliant duration string like: P12DT23H59M59.999999999999S * @param {string} x - * @returns {string} + * @returns {number} */ totalseconds: x => `( ( diff --git a/hana/lib/cql-functions.js b/hana/lib/cql-functions.js index c90524b9c..7a0f5309b 100644 --- a/hana/lib/cql-functions.js +++ b/hana/lib/cql-functions.js @@ -104,7 +104,45 @@ const StandardFunctions = { maxdatetime: () => "'9999-12-31T23:59:59.999Z'", mindatetime: () => "'0001-01-01T00:00:00.000Z'", now: () => `session_context('$now')`, - fractionalseconds: x => `(TO_DECIMAL(SECOND(${x}),5,3) - TO_INTEGER(SECOND(${x})))` + fractionalseconds: x => `(TO_DECIMAL(SECOND(${x}),5,3) - TO_INTEGER(SECOND(${x})))`, + totalseconds: x => { + // 1. Extract and convert days to seconds + // 2. Extract and convert hours to seconds + // 3. Extract and convert minutes to seconds + // 4. Extract seconds (including fractional part) and convert to double + // --> Add all together + return `( + CAST( + SUBSTRING(${x}, 2, LOCATE(${x}, 'DT') - 2 + ) AS INTEGER) * 86400 + ) + ( + ( + CAST( + SUBSTRING( + ${x}, + LOCATE(${x}, 'DT') + 2, + LOCATE(${x}, 'H') - LOCATE(${x}, 'DT') - 2 + ) AS INTEGER + ) * 3600 + ) + ( + CAST( + SUBSTRING( + ${x}, + LOCATE(${x}, 'H') + 1, + LOCATE(${x}, 'M') - LOCATE(${x}, 'H') - 1 + ) AS INTEGER + ) * 60 + ) + ( + CAST( + SUBSTRING( + ${x}, + LOCATE(${x}, 'M') + 1, + LOCATE(${x}, 'S') - LOCATE(${x}, 'M') - 1 + ) AS DOUBLE + ) + ) + )`; + }, } const HANAFunctions = { diff --git a/postgres/lib/cql-functions.js b/postgres/lib/cql-functions.js index 1dbd16870..9d8fe2c5c 100644 --- a/postgres/lib/cql-functions.js +++ b/postgres/lib/cql-functions.js @@ -25,25 +25,21 @@ const StandardFunctions = { minute: x => `date_part('minute', ${castVal(x)})`, second: x => `floor(date_part('second', ${castVal(x)}))`, fractionalseconds: x => `CAST(date_part('second', ${castVal(x)}) - floor(date_part('second', ${castVal(x)})) AS DECIMAL)`, + // 1. Extract and convert days to seconds + // 2. Extract and convert hours to seconds + // 3. Extract and convert minutes to seconds + // 4. Extract seconds (including fractional part) and convert to double + // --> Add all together totalseconds: x => `( + CAST(substring(${x}, 2, strpos(${x}, 'DT') - 2) AS INTEGER) * 86400 + ) + ( ( - ( - CAST(substring(${x},2,strpos(${x},'DT') - 2) AS INTEGER) - ) + ( - EXTRACT (EPOCH FROM - CAST( - replace( - replace( - replace( - substring(${x},strpos(${x},'DT') + 2), - 'H',':' - ),'M',':' - ),'S','Z' - ) - as TIME) - ) - 0.5 - ) - ) * 86400 + CAST(split_part(substring(${x}, strpos(${x}, 'DT') + 2), 'H', 1) AS INTEGER) * 3600 + ) + ( + CAST(split_part(split_part(substring(${x}, strpos(${x}, 'DT') + 2), 'H', 2), 'M', 1) AS INTEGER) * 60 + ) + ( + CAST(replace(split_part(split_part(substring(${x}, strpos(${x}, 'DT') + 2), 'M', 2), 'S', 1), 'Z', '') AS DOUBLE PRECISION) + ) )`, now: function() { return this.session_context({val: '$now'}) From 362ab68d774c4cfa8d0522435478ffe78834bad4 Mon Sep 17 00:00:00 2001 From: Patrice Bender Date: Fri, 13 Dec 2024 13:13:00 +0100 Subject: [PATCH 3/5] add missing tests --- test/compliance/SELECT.test.js | 2 +- test/compliance/functions.test.js | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/test/compliance/SELECT.test.js b/test/compliance/SELECT.test.js index f585220ad..cc16cb8b5 100644 --- a/test/compliance/SELECT.test.js +++ b/test/compliance/SELECT.test.js @@ -1146,7 +1146,7 @@ describe('SELECT', () => { { xpr: [ref, op, SELECT(ref).from(targetName)] }, { xpr: [{ list: [ref] }, op, SELECT(ref).from(targetName)] }, { xpr: [{ list: [ref, ref] }, op, SELECT([{ ...ref, as: 'a' }, { ...ref, as: 'b' }]).from(targetName)] }, - // Repreating the previous statements replaceing ref with null + // Repeating the previous statements replacing ref with null { xpr: [unified.null, op, { list: [ref] }] }, { xpr: [unified.null, op, { list: [ref, ref] }] }, { xpr: [{ list: [unified.null] }, op, { list: [{ list: [ref] }] }] }, diff --git a/test/compliance/functions.test.js b/test/compliance/functions.test.js index 09cee81de..bc56b72ec 100644 --- a/test/compliance/functions.test.js +++ b/test/compliance/functions.test.js @@ -1261,4 +1261,21 @@ describe('functions', () => { } }) }) + + describe('odata', () => { + test('totalseconds', async () => { + const cqn = SELECT.one + .from('edge.hana.functions.timestamps') + .columns("totalseconds('P12DT23H59M59.999999999999S') as ts") + const res = await cds.run(cqn) + expect(res).to.have.property('ts').that.equals(1123200) + }) + + test('date', async () => { + const cqn = SELECT.one.from('edge.hana.functions.timestamps').columns('date(a) as dateOnly', 'a as fullTimestamp') + const res = await cds.run(cqn) + expect(res).to.have.property('fullTimestamp').that.equals('2627-10-10T23:00:00.000Z') + expect(res).to.have.property('dateOnly').that.equals('2627-10-10') + }) + }) }) From d6b9ef3eacd2d79c4fd78615ef662d10ef6666a9 Mon Sep 17 00:00:00 2001 From: Patrice Bender Date: Mon, 16 Dec 2024 12:53:53 +0100 Subject: [PATCH 4/5] is already part of db-sevice/cql-functions.js --- hana/lib/cql-functions.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/hana/lib/cql-functions.js b/hana/lib/cql-functions.js index 7a0f5309b..0910eb70f 100644 --- a/hana/lib/cql-functions.js +++ b/hana/lib/cql-functions.js @@ -101,8 +101,6 @@ const StandardFunctions = { second: x => `TO_INTEGER(SECOND(${getTimeCast(x)}))`, date: x => `TO_DATE(${x})`, time: x => `TO_TIME(${x})`, - maxdatetime: () => "'9999-12-31T23:59:59.999Z'", - mindatetime: () => "'0001-01-01T00:00:00.000Z'", now: () => `session_context('$now')`, fractionalseconds: x => `(TO_DECIMAL(SECOND(${x}),5,3) - TO_INTEGER(SECOND(${x})))`, totalseconds: x => { From 1efb2312c29e8365b7c46d7e2687ce4098bee616 Mon Sep 17 00:00:00 2001 From: Patrice Bender Date: Mon, 16 Dec 2024 16:54:26 +0100 Subject: [PATCH 5/5] Revert "is already part of db-sevice/cql-functions.js" This reverts commit d6b9ef3eacd2d79c4fd78615ef662d10ef6666a9. --- hana/lib/cql-functions.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hana/lib/cql-functions.js b/hana/lib/cql-functions.js index 0910eb70f..7a0f5309b 100644 --- a/hana/lib/cql-functions.js +++ b/hana/lib/cql-functions.js @@ -101,6 +101,8 @@ const StandardFunctions = { second: x => `TO_INTEGER(SECOND(${getTimeCast(x)}))`, date: x => `TO_DATE(${x})`, time: x => `TO_TIME(${x})`, + maxdatetime: () => "'9999-12-31T23:59:59.999Z'", + mindatetime: () => "'0001-01-01T00:00:00.000Z'", now: () => `session_context('$now')`, fractionalseconds: x => `(TO_DECIMAL(SECOND(${x}),5,3) - TO_INTEGER(SECOND(${x})))`, totalseconds: x => {