From 88349195aadde36f253ab9be5f468f4aeb37a640 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Thu, 30 Mar 2023 23:40:49 +0530 Subject: [PATCH] refactor(nocodb): update formula functions to async and change return statement Signed-off-by: Pranav C --- .../lib/sql/functionMappings/mysql.ts | 200 +++++---- .../lib/sql/functionMappings/pg.ts | 40 +- .../lib/sql/functionMappings/sqlite.ts | 412 ++++++++++-------- 3 files changed, 349 insertions(+), 303 deletions(-) diff --git a/packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/functionMappings/mysql.ts b/packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/functionMappings/mysql.ts index 1c10b63434..d9a86af755 100644 --- a/packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/functionMappings/mysql.ts +++ b/packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/functionMappings/mysql.ts @@ -1,99 +1,115 @@ -// import dayjs from 'dayjs'; -// import { convertUnits } from '../helpers/convertUnits'; -// import { getWeekdayByText } from '../helpers/formulaFnHelper'; +import dayjs from 'dayjs'; +import { convertUnits } from '../helpers/convertUnits'; +import { getWeekdayByText } from '../helpers/formulaFnHelper'; import commonFns from './commonFns'; -// import type { MapFnArgs } from '../mapFunctionName'; +import type { MapFnArgs } from '../mapFunctionName'; const mysql2 = { ...commonFns, - // LEN: 'CHAR_LENGTH', - // MIN: 'LEAST', - // MAX: 'GREATEST', - // SEARCH: (args: MapFnArgs) => { - // args.pt.callee.name = 'LOCATE'; - // const temp = args.pt.arguments[0]; - // args.pt.arguments[0] = args.pt.arguments[1]; - // args.pt.arguments[1] = temp; - // }, - // INT: (args: MapFnArgs) => { - // return args.knex.raw( - // `CAST(${args.fn(args.pt.arguments[0])} as SIGNED)${args.colAlias}` - // ); - // }, - // LEFT: (args: MapFnArgs) => { - // return args.knex.raw( - // `SUBSTR(${args.fn(args.pt.arguments[0])},1,${args.fn( - // args.pt.arguments[1] - // )})${args.colAlias}` - // ); - // }, - // RIGHT: (args: MapFnArgs) => { - // return args.knex.raw( - // `SUBSTR(${args.fn(args.pt.arguments[0])}, -(${args.fn( - // args.pt.arguments[1] - // )}))${args.colAlias}` - // ); - // }, - // MID: 'SUBSTR', - // FLOAT: (args: MapFnArgs) => { - // return args.knex - // .raw( - // `CAST(CAST(${args.fn(args.pt.arguments[0])} as CHAR) AS DOUBLE)${ - // args.colAlias - // }` - // ) - // .wrap('(', ')'); - // }, - // DATEADD: ({ fn, knex, pt, colAlias }: MapFnArgs) => { - // return knex.raw( - // `CASE - // WHEN ${fn(pt.arguments[0])} LIKE '%:%' THEN - // DATE_FORMAT(DATE_ADD(${fn(pt.arguments[0])}, INTERVAL - // ${fn(pt.arguments[1])} ${String(fn(pt.arguments[2])).replace( - // /["']/g, - // '' - // )}), '%Y-%m-%d %H:%i') - // ELSE - // DATE(DATE_ADD(${fn(pt.arguments[0])}, INTERVAL - // ${fn(pt.arguments[1])} ${String(fn(pt.arguments[2])).replace( - // /["']/g, - // '' - // )})) - // END${colAlias}` - // ); - // }, - // DATETIME_DIFF: ({ fn, knex, pt, colAlias }: MapFnArgs) => { - // const datetime_expr1 = fn(pt.arguments[0]); - // const datetime_expr2 = fn(pt.arguments[1]); - // - // const unit = convertUnits( - // pt.arguments[2] ? fn(pt.arguments[2]).bindings[0] : 'seconds', - // 'mysql' - // ); - // - // if (unit === 'MICROSECOND') { - // // MySQL doesn't support millisecond - // // hence change from MICROSECOND to millisecond manually - // return knex.raw( - // `TIMESTAMPDIFF(${unit}, ${datetime_expr2}, ${datetime_expr1}) div 1000 ${colAlias}` - // ); - // } - // return knex.raw( - // `TIMESTAMPDIFF(${unit}, ${datetime_expr2}, ${datetime_expr1}) ${colAlias}` - // ); - // }, - // WEEKDAY: ({ fn, knex, pt, colAlias }: MapFnArgs) => { - // // WEEKDAY() returns an index from 0 to 6 for Monday to Sunday - // return knex.raw( - // `(WEEKDAY(${ - // pt.arguments[0].type === 'Literal' - // ? `'${dayjs(fn(pt.arguments[0])).format('YYYY-MM-DD')}'` - // : fn(pt.arguments[0]) - // }) - ${getWeekdayByText( - // pt?.arguments[1]?.value - // )} % 7 + 7) % 7 ${colAlias}` - // ); - // }, + LEN: 'CHAR_LENGTH', + MIN: 'LEAST', + MAX: 'GREATEST', + SEARCH: async (args: MapFnArgs) => { + args.pt.callee.name = 'LOCATE'; + const temp = args.pt.arguments[0]; + args.pt.arguments[0] = args.pt.arguments[1]; + args.pt.arguments[1] = temp; + }, + INT: async (args: MapFnArgs) => { + return { + builder: args.knex.raw( + `CAST(${(await args.fn(args.pt.arguments[0])).builder} as SIGNED)${args.colAlias}`, + ), + }; + }, + LEFT: async (args: MapFnArgs) => { + return { + builder: args.knex.raw( + `SUBSTR(${(await args.fn(args.pt.arguments[0])).builder},1,${(await args.fn( + args.pt.arguments[1], + )).builder})${args.colAlias}`, + ), + }; + }, + RIGHT: async (args: MapFnArgs) => { + return { + builder: args.knex.raw( + `SUBSTR(${(await args.fn(args.pt.arguments[0])).builder}, -(${(await args.fn( + args.pt.arguments[1], + )).builder}))${args.colAlias}`, + ), + }; + }, + MID: 'SUBSTR', + FLOAT: async (args: MapFnArgs) => { + return { + builder: args.knex + .raw( + `CAST(CAST(${(await args.fn(args.pt.arguments[0])).builder} as CHAR) AS DOUBLE)${ + args.colAlias + }`, + ) + .wrap('(', ')'), + }; + }, + DATEADD: async ({ fn, knex, pt, colAlias }: MapFnArgs) => { + return { + builder: knex.raw( + `CASE + WHEN ${(await fn(pt.arguments[0])).builder} LIKE '%:%' THEN + DATE_FORMAT(DATE_ADD(${(await fn(pt.arguments[0])).builder}, INTERVAL + ${(await fn(pt.arguments[1])).builder} ${String((await fn(pt.arguments[2])).builder).replace( + /["']/g, + '', + )}), '%Y-%m-%d %H:%i') + ELSE + DATE(DATE_ADD(${(await fn(pt.arguments[0])).builder}, INTERVAL + ${(await fn(pt.arguments[1])).builder} ${String((await fn(pt.arguments[2])).builder).replace( + /["']/g, + '', + )})) + END${colAlias}`, + ), + }; + }, + DATETIME_DIFF: async ({ fn, knex, pt, colAlias }: MapFnArgs) => { + const datetime_expr1 = (await fn(pt.arguments[0])).builder; + const datetime_expr2 = (await fn(pt.arguments[1])).builder; + + const unit = convertUnits( + pt.arguments[2] ? (await fn(pt.arguments[2])).builder.bindings[0] : 'seconds', + 'mysql' + ); + + if (unit === 'MICROSECOND') { + // MySQL doesn't support millisecond + // hence change from MICROSECOND to millisecond manually + return { + builder: knex.raw( + `TIMESTAMPDIFF(${unit}, ${datetime_expr2}, ${datetime_expr1}) div 1000 ${colAlias}`, + ), + }; + } + return { + builder: knex.raw( + `TIMESTAMPDIFF(${unit}, ${datetime_expr2}, ${datetime_expr1}) ${colAlias}`, + ), + }; + }, + WEEKDAY: async ({ fn, knex, pt, colAlias }: MapFnArgs) => { + // WEEKDAY() returns an index from 0 to 6 for Monday to Sunday + return { + builder: knex.raw( + `(WEEKDAY(${ + pt.arguments[0].type === 'Literal' + ? `'${dayjs((await fn(pt.arguments[0])).builder).format('YYYY-MM-DD')}'` + : (await fn(pt.arguments[0])).builder + }) - ${getWeekdayByText( + pt?.arguments[1]?.value, + )} % 7 + 7) % 7 ${colAlias}`, + ), + }; + }, }; export default mysql2; diff --git a/packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/functionMappings/pg.ts b/packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/functionMappings/pg.ts index a03bc664e4..ebd1475efc 100644 --- a/packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/functionMappings/pg.ts +++ b/packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/functionMappings/pg.ts @@ -34,27 +34,27 @@ const pg = { }; }, MID: 'SUBSTR', - FLOAT: ({ fn, knex, pt, colAlias }: MapFnArgs) => { + FLOAT: async ({ fn, knex, pt, colAlias }: MapFnArgs) => { return { builder: knex - .raw(`CAST(${fn(pt.arguments[0])} as DOUBLE PRECISION)${colAlias}`) + .raw(`CAST(${(await fn(pt.arguments[0])).builder} as DOUBLE PRECISION)${colAlias}`) .wrap('(', ')'), }; }, - ROUND: ({ fn, knex, pt, colAlias }: MapFnArgs) => { + ROUND: async ({ fn, knex, pt, colAlias }: MapFnArgs) => { return { builder: knex.raw( - `ROUND((${fn(pt.arguments[0])})::numeric, ${ - pt?.arguments[1] ? fn(pt.arguments[1]) : 0 + `ROUND((${(await fn(pt.arguments[0])).builder})::numeric, ${ + pt?.arguments[1] ? (await fn(pt.arguments[1])).builder : 0 }) ${colAlias}` ), }; }, - DATEADD: ({ fn, knex, pt, colAlias }: MapFnArgs) => { + DATEADD: async ({ fn, knex, pt, colAlias }: MapFnArgs) => { return { builder: knex.raw( - `${fn(pt.arguments[0])} + (${fn(pt.arguments[1])} || - '${String(fn(pt.arguments[2])).replace( + `${(await fn(pt.arguments[0])).builder} + (${(await fn(pt.arguments[1])).builder} || + '${String((await fn(pt.arguments[2])).builder).replace( /["']/g, '' )}')::interval${colAlias}` @@ -62,8 +62,8 @@ const pg = { }; }, DATETIME_DIFF: async ({ fn, knex, pt, colAlias }: MapFnArgs) => { - const datetime_expr1 = fn(pt.arguments[0]); - const datetime_expr2 = fn(pt.arguments[1]); + const datetime_expr1 = (await fn(pt.arguments[0])).builder; + const datetime_expr2 = (await fn(pt.arguments[1])).builder; const rawUnit = pt.arguments[2] ? (await fn(pt.arguments[2])).builder.bindings[0] : 'seconds'; @@ -121,7 +121,7 @@ const pg = { ? `date '${dayjs((await fn(pt.arguments[0])).builder).format( 'YYYY-MM-DD' )}'` - : fn(pt.arguments[0]) + : (await fn(pt.arguments[0])).builder }) - 1 - ${getWeekdayByText( pt?.arguments[1]?.value )} % 7 + 7) ::INTEGER % 7 ${colAlias}` @@ -133,10 +133,10 @@ const pg = { builder: args.knex.raw( `CASE WHEN ${args.knex .raw( - `${args.pt.arguments + `${(await Promise.all(args.pt.arguments .map(async (ar) => (await args.fn(ar, '', 'AND')).builder.toQuery() - ) + ))) .join(' AND ')}` ) .wrap('(', ')') @@ -160,10 +160,10 @@ const pg = { ), }; }, - SUBSTR: ({ fn, knex, pt, colAlias }: MapFnArgs) => { - const str = fn(pt.arguments[0]); - const positionFrom = fn(pt.arguments[1] ?? 1); - const numberOfCharacters = fn(pt.arguments[2] ?? ''); + SUBSTR:async ({ fn, knex, pt, colAlias }: MapFnArgs) => { + const str = (await fn(pt.arguments[0])).builder; + const positionFrom = (await fn(pt.arguments[1] ?? 1)).builder; + const numberOfCharacters = (await fn(pt.arguments[2] ?? '')).builder; return { builder: knex.raw( `SUBSTR(${str}::TEXT, ${positionFrom}${ @@ -172,9 +172,9 @@ const pg = { ), }; }, - MOD: ({ fn, knex, pt, colAlias }: MapFnArgs) => { - const x = fn(pt.arguments[0]); - const y = fn(pt.arguments[1]); + MOD: async ({ fn, knex, pt, colAlias }: MapFnArgs) => { + const x = (await fn(pt.arguments[0])).builder; + const y = (await fn(pt.arguments[1])).builder; return { builder: knex.raw(`MOD((${x})::NUMERIC, (${y})::NUMERIC) ${colAlias}`), }; diff --git a/packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/functionMappings/sqlite.ts b/packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/functionMappings/sqlite.ts index 187c8cf156..bfe91ccbf5 100644 --- a/packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/functionMappings/sqlite.ts +++ b/packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/functionMappings/sqlite.ts @@ -1,198 +1,228 @@ -// import dayjs from 'dayjs'; -// import { convertUnits } from '../helpers/convertUnits'; -// import { getWeekdayByText } from '../helpers/formulaFnHelper'; -// import { -// convertToTargetFormat, -// getDateFormat, -// } from '../../../../../utils/dateTimeUtils'; +import dayjs from 'dayjs'; +import { convertUnits } from '../helpers/convertUnits'; +import { getWeekdayByText } from '../helpers/formulaFnHelper'; +import { + convertToTargetFormat, + getDateFormat, +} from '../../../../../utils/dateTimeUtils'; import commonFns from './commonFns'; -// import type { MapFnArgs } from '../mapFunctionName'; +import type { MapFnArgs } from '../mapFunctionName'; const sqlite3 = { ...commonFns, - // LEN: 'LENGTH', - // CEILING(args) { - // return args.knex.raw( - // `round(${args.fn(args.pt.arguments[0])} + 0.5)${args.colAlias}` - // ); - // }, - // FLOOR(args) { - // return args.knex.raw( - // `round(${args.fn(args.pt.arguments[0])} - 0.5)${args.colAlias}` - // ); - // }, - // MOD: (args: MapFnArgs) => { - // return args.fn({ - // type: 'BinaryExpression', - // operator: '%', - // left: args.pt.arguments[0], - // right: args.pt.arguments[1], - // }); - // }, - // REPEAT(args: MapFnArgs) { - // return args.knex.raw( - // `replace(printf('%.' || ${args.fn( - // args.pt.arguments[1] - // )} || 'c', '/'),'/',${args.fn(args.pt.arguments[0])})${args.colAlias}` - // ); - // }, - // NOW: 'DATE', - // SEARCH: 'INSTR', - // INT(args: MapFnArgs) { - // return args.knex.raw( - // `CAST(${args.fn(args.pt.arguments[0])} as INTEGER)${args.colAlias}` - // ); - // }, - // LEFT: (args: MapFnArgs) => { - // return args.knex.raw( - // `SUBSTR(${args.fn(args.pt.arguments[0])},1,${args.fn( - // args.pt.arguments[1] - // )})${args.colAlias}` - // ); - // }, - // RIGHT: (args: MapFnArgs) => { - // return args.knex.raw( - // `SUBSTR(${args.fn(args.pt.arguments[0])},-(${args.fn( - // args.pt.arguments[1] - // )}))${args.colAlias}` - // ); - // }, - // MID: 'SUBSTR', - // FLOAT: (args: MapFnArgs) => { - // return args.knex - // .raw(`CAST(${args.fn(args.pt.arguments[0])} as FLOAT)${args.colAlias}`) - // .wrap('(', ')'); - // }, - // DATEADD: ({ fn, knex, pt, colAlias }: MapFnArgs) => { - // const dateIN = fn(pt.arguments[1]); - // return knex.raw( - // `CASE - // WHEN ${fn(pt.arguments[0])} LIKE '%:%' THEN - // STRFTIME('%Y-%m-%d %H:%M', DATETIME(DATETIME(${fn( - // pt.arguments[0] - // )}, 'localtime'), - // ${dateIN > 0 ? '+' : ''}${fn(pt.arguments[1])} || ' ${String( - // fn(pt.arguments[2]) - // ).replace(/["']/g, '')}')) - // ELSE - // DATE(DATETIME(${fn(pt.arguments[0])}, 'localtime'), - // ${dateIN > 0 ? '+' : ''}${fn(pt.arguments[1])} || ' ${String( - // fn(pt.arguments[2]) - // ).replace(/["']/g, '')}') - // END${colAlias}` - // ); - // }, - // DATETIME_DIFF: ({ fn, knex, pt, colAlias }: MapFnArgs) => { - // let datetime_expr1 = fn(pt.arguments[0]); - // let datetime_expr2 = fn(pt.arguments[1]); - // // JULIANDAY takes YYYY-MM-DD - // if (datetime_expr1.sql === '?' && datetime_expr1.bindings?.[0]) { - // datetime_expr1 = `'${convertToTargetFormat( - // datetime_expr1.bindings[0], - // getDateFormat(datetime_expr1.bindings[0]), - // 'YYYY-MM-DD' - // )}'`; - // } - // - // if (datetime_expr2.sql === '?' && datetime_expr2.bindings?.[0]) { - // datetime_expr2 = `'${convertToTargetFormat( - // datetime_expr2.bindings[0], - // getDateFormat(datetime_expr2.bindings[0]), - // 'YYYY-MM-DD' - // )}'`; - // } - // - // const rawUnit = pt.arguments[2] - // ? fn(pt.arguments[2]).bindings[0] - // : 'seconds'; - // let sql; - // const unit = convertUnits(rawUnit, 'sqlite'); - // switch (unit) { - // case 'seconds': - // sql = `(strftime('%s', ${datetime_expr1}) - strftime('%s', ${datetime_expr2}))`; - // break; - // case 'minutes': - // sql = `(strftime('%s', ${datetime_expr1}) - strftime('%s', ${datetime_expr2})) / 60`; - // break; - // case 'hours': - // sql = `(strftime('%s', ${datetime_expr1}) - strftime('%s', ${datetime_expr2})) / 3600`; - // break; - // case 'milliseconds': - // sql = `(strftime('%s', ${datetime_expr1}) - strftime('%s', ${datetime_expr2})) * 1000`; - // break; - // case 'weeks': - // sql = `ROUND((JULIANDAY(${datetime_expr1}) - JULIANDAY(${datetime_expr2})) / 7)`; - // break; - // case 'months': - // sql = `(strftime('%Y', ${datetime_expr1}) - strftime('%Y', ${datetime_expr2})) * 12 + (strftime('%m', ${datetime_expr1}) - strftime('%m', ${datetime_expr2})) `; - // break; - // case 'quarters': - // sql = `(strftime('%Y', ${datetime_expr1}) - strftime('%Y', ${datetime_expr2})) * 4 + (strftime('%m', ${datetime_expr1}) - strftime('%m', ${datetime_expr2})) / 3`; - // break; - // case 'years': - // sql = `CASE - // WHEN (${datetime_expr2} < ${datetime_expr1}) THEN - // ( - // (strftime('%Y', ${datetime_expr1}) - strftime('%Y', ${datetime_expr2})) - // - (strftime('%m', ${datetime_expr1}) < strftime('%m', ${datetime_expr2}) - // OR (strftime('%m', ${datetime_expr1}) = strftime('%m', ${datetime_expr2}) - // AND strftime('%d', ${datetime_expr1}) < strftime('%d', ${datetime_expr2}))) - // ) - // WHEN (${datetime_expr2} > ${datetime_expr1}) THEN - // -1 * ( - // (strftime('%Y', ${datetime_expr2}) - strftime('%Y', ${datetime_expr1})) - // - (strftime('%m', ${datetime_expr2}) < strftime('%m', ${datetime_expr1}) - // OR (strftime('%m', ${datetime_expr2}) = strftime('%m', ${datetime_expr1}) - // AND strftime('%d', ${datetime_expr2}) < strftime('%d', ${datetime_expr1}))) - // ) - // ELSE 0 - // END`; - // break; - // case 'days': - // sql = `JULIANDAY(${datetime_expr1}) - JULIANDAY(${datetime_expr2})`; - // break; - // default: - // sql = ''; - // } - // return knex.raw(`${sql} ${colAlias}`); - // }, - // WEEKDAY: ({ fn, knex, pt, colAlias }: MapFnArgs) => { - // // strftime('%w', date) - day of week 0 - 6 with Sunday == 0 - // // WEEKDAY() returns an index from 0 to 6 for Monday to Sunday - // return knex.raw( - // `(strftime('%w', ${ - // pt.arguments[0].type === 'Literal' - // ? `'${dayjs(fn(pt.arguments[0])).format('YYYY-MM-DD')}'` - // : fn(pt.arguments[0]) - // }) - 1 - ${getWeekdayByText( - // pt?.arguments[1]?.value - // )} % 7 + 7) % 7 ${colAlias}` - // ); - // }, - // AND: (args: MapFnArgs) => { - // return args.knex.raw( - // `CASE WHEN ${args.knex - // .raw( - // `${args.pt.arguments - // .map((ar) => args.fn(ar, '', 'AND').toQuery()) - // .join(' AND ')}` - // ) - // .wrap('(', ')') - // .toQuery()} THEN 1 ELSE 0 END ${args.colAlias}` - // ); - // }, - // OR: (args: MapFnArgs) => { - // return args.knex.raw( - // `CASE WHEN ${args.knex - // .raw( - // `${args.pt.arguments - // .map((ar) => args.fn(ar, '', 'OR').toQuery()) - // .join(' OR ')}` - // ) - // .wrap('(', ')') - // .toQuery()} THEN 1 ELSE 0 END ${args.colAlias}` - // ); - // }, + LEN: 'LENGTH', + async CEILING(args) { + return args.knex.raw( + `round(${(await args.fn(args.pt.arguments[0])).builder} + 0.5)${ + args.colAlias + }` + ); + }, + async FLOOR(args) { + return args.knex.raw( + `round(${(await args.fn(args.pt.arguments[0])).builder} - 0.5)${ + args.colAlias + }` + ); + }, + MOD: async (args: MapFnArgs) => { + return ( + await args.fn({ + type: 'BinaryExpression', + operator: '%', + left: args.pt.arguments[0], + right: args.pt.arguments[1], + }) + ).builder; + }, + async REPEAT(args: MapFnArgs) { + return args.knex.raw( + `replace(printf('%.' || ${ + (await args.fn(args.pt.arguments[1])).builder + } || 'c', '/'),'/',${(await args.fn(args.pt.arguments[0])).builder})${ + args.colAlias + }` + ); + }, + NOW: 'DATE', + SEARCH: 'INSTR', + async INT(args: MapFnArgs) { + return args.knex.raw( + `CAST(${(await args.fn(args.pt.arguments[0])).builder} as INTEGER)${ + args.colAlias + }` + ); + }, + LEFT: async (args: MapFnArgs) => { + return args.knex.raw( + `SUBSTR(${(await args.fn(args.pt.arguments[0])).builder},1,${ + (await args.fn(args.pt.arguments[1])).builder + })${args.colAlias}` + ); + }, + RIGHT: async (args: MapFnArgs) => { + return args.knex.raw( + `SUBSTR(${(await args.fn(args.pt.arguments[0])).builder},-(${ + (await args.fn(args.pt.arguments[1])).builder + }))${args.colAlias}` + ); + }, + MID: 'SUBSTR', + FLOAT: async (args: MapFnArgs) => { + return args.knex + .raw( + `CAST(${(await args.fn(args.pt.arguments[0])).builder} as FLOAT)${ + args.colAlias + }` + ) + .wrap('(', ')'); + }, + DATEADD: async ({ fn, knex, pt, colAlias }: MapFnArgs) => { + const dateIN = (await fn(pt.arguments[1])).builder; + return knex.raw( + `CASE + WHEN ${(await fn(pt.arguments[0])).builder} LIKE '%:%' THEN + STRFTIME('%Y-%m-%d %H:%M', DATETIME(DATETIME(${fn( + pt.arguments[0] + )}, 'localtime'), + ${dateIN > 0 ? '+' : ''}${ + (await fn(pt.arguments[1])).builder + } || ' ${String((await fn(pt.arguments[2])).builder).replace( + /["']/g, + '' + )}')) + ELSE + DATE(DATETIME(${(await fn(pt.arguments[0])).builder}, 'localtime'), + ${dateIN > 0 ? '+' : ''}${ + (await fn(pt.arguments[1])).builder + } || ' ${String((await fn(pt.arguments[2])).builder).replace( + /["']/g, + '' + )}') + END${colAlias}` + ); + }, + DATETIME_DIFF: async ({ fn, knex, pt, colAlias }: MapFnArgs) => { + let datetime_expr1 = (await fn(pt.arguments[0])).builder; + let datetime_expr2 = (await fn(pt.arguments[1])).builder; + // JULIANDAY takes YYYY-MM-DD + if (datetime_expr1.sql === '?' && datetime_expr1.bindings?.[0]) { + datetime_expr1 = `'${convertToTargetFormat( + datetime_expr1.bindings[0], + getDateFormat(datetime_expr1.bindings[0]), + 'YYYY-MM-DD' + )}'`; + } + + if (datetime_expr2.sql === '?' && datetime_expr2.bindings?.[0]) { + datetime_expr2 = `'${convertToTargetFormat( + datetime_expr2.bindings[0], + getDateFormat(datetime_expr2.bindings[0]), + 'YYYY-MM-DD' + )}'`; + } + + const rawUnit = pt.arguments[2] + ? (await fn(pt.arguments[2])).builder.bindings[0] + : 'seconds'; + let sql; + const unit = convertUnits(rawUnit, 'sqlite'); + switch (unit) { + case 'seconds': + sql = `(strftime('%s', ${datetime_expr1}) - strftime('%s', ${datetime_expr2}))`; + break; + case 'minutes': + sql = `(strftime('%s', ${datetime_expr1}) - strftime('%s', ${datetime_expr2})) / 60`; + break; + case 'hours': + sql = `(strftime('%s', ${datetime_expr1}) - strftime('%s', ${datetime_expr2})) / 3600`; + break; + case 'milliseconds': + sql = `(strftime('%s', ${datetime_expr1}) - strftime('%s', ${datetime_expr2})) * 1000`; + break; + case 'weeks': + sql = `ROUND((JULIANDAY(${datetime_expr1}) - JULIANDAY(${datetime_expr2})) / 7)`; + break; + case 'months': + sql = `(strftime('%Y', ${datetime_expr1}) - strftime('%Y', ${datetime_expr2})) * 12 + (strftime('%m', ${datetime_expr1}) - strftime('%m', ${datetime_expr2})) `; + break; + case 'quarters': + sql = `(strftime('%Y', ${datetime_expr1}) - strftime('%Y', ${datetime_expr2})) * 4 + (strftime('%m', ${datetime_expr1}) - strftime('%m', ${datetime_expr2})) / 3`; + break; + case 'years': + sql = `CASE + WHEN (${datetime_expr2} < ${datetime_expr1}) THEN + ( + (strftime('%Y', ${datetime_expr1}) - strftime('%Y', ${datetime_expr2})) + - (strftime('%m', ${datetime_expr1}) < strftime('%m', ${datetime_expr2}) + OR (strftime('%m', ${datetime_expr1}) = strftime('%m', ${datetime_expr2}) + AND strftime('%d', ${datetime_expr1}) < strftime('%d', ${datetime_expr2}))) + ) + WHEN (${datetime_expr2} > ${datetime_expr1}) THEN + -1 * ( + (strftime('%Y', ${datetime_expr2}) - strftime('%Y', ${datetime_expr1})) + - (strftime('%m', ${datetime_expr2}) < strftime('%m', ${datetime_expr1}) + OR (strftime('%m', ${datetime_expr2}) = strftime('%m', ${datetime_expr1}) + AND strftime('%d', ${datetime_expr2}) < strftime('%d', ${datetime_expr1}))) + ) + ELSE 0 + END`; + break; + case 'days': + sql = `JULIANDAY(${datetime_expr1}) - JULIANDAY(${datetime_expr2})`; + break; + default: + sql = ''; + } + return knex.raw(`${sql} ${colAlias}`); + }, + WEEKDAY: async ({ fn, knex, pt, colAlias }: MapFnArgs) => { + // strftime('%w', date) - day of week 0 - 6 with Sunday == 0 + // WEEKDAY() returns an index from 0 to 6 for Monday to Sunday + return knex.raw( + `(strftime('%w', ${ + pt.arguments[0].type === 'Literal' + ? `'${dayjs((await fn(pt.arguments[0])).builder).format( + 'YYYY-MM-DD' + )}'` + : (await fn(pt.arguments[0])).builder + }) - 1 - ${getWeekdayByText( + pt?.arguments[1]?.value + )} % 7 + 7) % 7 ${colAlias}` + ); + }, + AND: async (args: MapFnArgs) => { + return args.knex.raw( + `CASE WHEN ${args.knex + .raw( + `${( + await Promise.all( + args.pt.arguments.map(async (ar) => + (await args.fn(ar, '', 'AND')).builder.toQuery() + ) + ) + ).join(' AND ')}` + ) + .wrap('(', ')') + .toQuery()} THEN 1 ELSE 0 END ${args.colAlias}` + ); + }, + OR: async (args: MapFnArgs) => { + return args.knex.raw( + `CASE WHEN ${args.knex + .raw( + `${( + await Promise.all( + args.pt.arguments.map(async (ar) => + (await args.fn(ar, '', 'OR')).builder.toQuery() + ) + ) + ).join(' OR ')}` + ) + .wrap('(', ')') + .toQuery()} THEN 1 ELSE 0 END ${args.colAlias}` + ); + }, }; export default sqlite3;