Browse Source

refactor: convert to async...await to resolve builder only if required

Signed-off-by: Pranav C <pranavxc@gmail.com>
pull/5392/head
Pranav C 2 years ago
parent
commit
33f19aa596
  1. 63
      packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/formulav2/formulaQueryBuilderv2.ts
  2. 56
      packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/functionMappings/commonFns.ts
  3. 98
      packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/functionMappings/mssql.ts
  4. 184
      packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/functionMappings/mysql.ts
  5. 28
      packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/functionMappings/pg.ts
  6. 382
      packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/functionMappings/sqlite.ts
  7. 9
      packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/mapFunctionName.ts

63
packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/formulav2/formulaQueryBuilderv2.ts

@ -53,7 +53,7 @@ async function _formulaQueryBuilder(
alias,
knex: XKnex,
model: Model,
aliasToColumn = {},
aliasToColumn: Record<string, () => Promise<{ builder: any }>> = {},
tableAlias?: string
) {
// formula may include double curly brackets in previous version
@ -69,6 +69,7 @@ async function _formulaQueryBuilder(
switch (col.uidt) {
case UITypes.Formula:
{
aliasToColumn[col.id] = async () => {
const formulOption = await col.getColOptions<FormulaColumn>();
const { builder } = await _formulaQueryBuilder(
formulOption.formula,
@ -79,11 +80,14 @@ async function _formulaQueryBuilder(
tableAlias
);
builder.sql = '(' + builder.sql + ')';
aliasToColumn[col.id] = builder;
return {
builder,
};
};
}
break;
case UITypes.Lookup:
{
aliasToColumn[col.id] = async (): Promise<any> => {
let aliasCount = 0;
let selectQb;
let isMany = false;
@ -398,25 +402,27 @@ async function _formulaQueryBuilder(
}
if (selectQb)
aliasToColumn[col.id] =
return {
builder:
typeof selectQb === 'function'
? selectQb
: knex.raw(selectQb as any).wrap('(', ')');
}
: knex.raw(selectQb as any).wrap('(', ')'),
};
}
};
break;
case UITypes.Rollup:
{
aliasToColumn[col.id] = async (): Promise<any> => {
const qb = await genRollupSelectv2({
knex,
columnOptions: (await col.getColOptions()) as RollupColumn,
alias: tableAlias,
});
aliasToColumn[col.id] = knex.raw(qb.builder).wrap('(', ')');
}
return { builder: knex.raw(qb.builder).wrap('(', ')') };
};
break;
case UITypes.LinkToAnotherRecord:
{
aliasToColumn[col.id] = async (): Promise<any> => {
const alias = `__nc_formula_ll`;
const relation = await col.getColOptions<LinkToAnotherRecordColumn>();
// if (relation.type !== 'bt') continue;
@ -520,19 +526,22 @@ async function _formulaQueryBuilder(
.wrap('(', ')');
}
if (selectQb)
aliasToColumn[col.id] =
return {
builder:
typeof selectQb === 'function'
? selectQb
: knex.raw(selectQb as any).wrap('(', ')');
}
: knex.raw(selectQb as any).wrap('(', ')'),
};
};
break;
default:
aliasToColumn[col.id] = col.column_name;
aliasToColumn[col.id] = () =>
Promise.resolve({ builder: col.column_name });
break;
}
}
const fn = (pt, a?, prevBinaryOp?) => {
const fn = async (pt, a?, prevBinaryOp?) => {
const colAlias = a ? ` as ${a}` : '';
pt.arguments?.forEach?.((arg) => {
if (arg.fnName) return;
@ -558,18 +567,6 @@ async function _formulaQueryBuilder(
return fn(pt.arguments[0], a, prevBinaryOp);
}
break;
// case 'AVG':
// if (pt.arguments.length > 1) {
// return fn({
// type: 'BinaryExpression',
// operator: '/',
// left: {...pt, callee: {name: 'SUM'}},
// right: {type: 'Literal', value: pt.arguments.length}
// }, a, prevBinaryOp)
// } else {
// return fn(pt.arguments[0], a, prevBinaryOp)
// }
// break;
case 'CONCAT':
if (knex.clientType() === 'sqlite3') {
if (pt.arguments.length > 1) {
@ -653,8 +650,12 @@ async function _formulaQueryBuilder(
} else if (pt.type === 'Literal') {
return knex.raw(`?${colAlias}`, [pt.value]);
} else if (pt.type === 'Identifier') {
if (typeof aliasToColumn?.[pt.name] === 'function') {
return knex.raw(`??${colAlias}`, aliasToColumn?.[pt.name](pt.fnName));
const { builder } = await aliasToColumn?.[pt.name]()
if (typeof builder === 'function') {
return knex.raw(
`??${colAlias}`,
await builder(pt.fnName)
);
}
return knex.raw(`??${colAlias}`, [aliasToColumn?.[pt.name] || pt.name]);
} else if (pt.type === 'BinaryExpression') {
@ -772,7 +773,7 @@ async function _formulaQueryBuilder(
if (prevBinaryOp && pt.operator !== prevBinaryOp) {
query.wrap('(', ')');
}
return query;
return { builder: query };
} else if (pt.type === 'UnaryExpression') {
const query = knex.raw(
`${pt.operator}${fn(
@ -784,7 +785,7 @@ async function _formulaQueryBuilder(
if (prevBinaryOp && pt.operator !== prevBinaryOp) {
query.wrap('(', ')');
}
return query;
return { builder: query };
}
};
return { builder: fn(tree, alias) };

56
packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/functionMappings/commonFns.ts

@ -2,66 +2,80 @@ import type { MapFnArgs } from '../mapFunctionName';
export default {
// todo: handle default case
SWITCH: (args: MapFnArgs) => {
SWITCH: async (args: MapFnArgs) => {
const count = Math.floor((args.pt.arguments.length - 1) / 2);
let query = '';
const switchVal = args.fn(args.pt.arguments[0]).toQuery();
const switchVal = (await args.fn(args.pt.arguments[0])).builder.toQuery();
for (let i = 0; i < count; i++) {
query += args.knex
.raw(
`\n\tWHEN ${args
.fn(args.pt.arguments[i * 2 + 1])
.toQuery()} THEN ${args.fn(args.pt.arguments[i * 2 + 2]).toQuery()}`
`\n\tWHEN ${(
await args.fn(args.pt.arguments[i * 2 + 1])
).builder.toQuery()} THEN ${(
await args.fn(args.pt.arguments[i * 2 + 2])
).builder.toQuery()}`
)
.toQuery();
}
if (args.pt.arguments.length % 2 === 0) {
query += args.knex
.raw(
`\n\tELSE ${args
.fn(args.pt.arguments[args.pt.arguments.length - 1])
.toQuery()}`
`\n\tELSE ${(
await args.fn(args.pt.arguments[args.pt.arguments.length - 1])
).builder.toQuery()}`
)
.toQuery();
}
return args.knex.raw(`CASE ${switchVal} ${query}\n END${args.colAlias}`);
},
IF: (args: MapFnArgs) => {
IF: async (args: MapFnArgs) => {
let query = args.knex
.raw(
`\n\tWHEN ${args.fn(args.pt.arguments[0]).toQuery()} THEN ${args
.fn(args.pt.arguments[1])
.toQuery()}`
`\n\tWHEN ${(
await args.fn(args.pt.arguments[0])
).builder.toQuery()} THEN ${(
await args.fn(args.pt.arguments[1])
).builder.toQuery()}`
)
.toQuery();
if (args.pt.arguments[2]) {
query += args.knex
.raw(`\n\tELSE ${args.fn(args.pt.arguments[2]).toQuery()}`)
.raw(`\n\tELSE ${(await args.fn(args.pt.arguments[2])).builder.toQuery()}`)
.toQuery();
}
return args.knex.raw(`CASE ${query}\n END${args.colAlias}`);
},
TRUE: (_args) => 1,
FALSE: (_args) => 0,
AND: (args: MapFnArgs) => {
AND: async (args: MapFnArgs) => {
return args.knex.raw(
`${args.knex
.raw(
`${args.pt.arguments
.map((ar) => args.fn(ar).toQuery())
.join(' AND ')}`
`${(
await Promise.all(
args.pt.arguments.map(async (ar) =>
(await args.fn(ar)).builder.toQuery()
)
)
).join(' AND ')}`
)
.wrap('(', ')')
.toQuery()}${args.colAlias}`
);
},
OR: (args: MapFnArgs) => {
OR: async (args: MapFnArgs) => {
return args.knex.raw(
`${args.knex
.raw(
`${args.pt.arguments.map((ar) => args.fn(ar).toQuery()).join(' OR ')}`
`${(
await Promise.all(
args.pt.arguments.map(async (ar) =>
(await args.fn(ar)).builder.toQuery()
)
)
).join(' OR ')}`
)
.wrap('(', ')')
.toQuery()}${args.colAlias}`
@ -83,7 +97,7 @@ export default {
return args.fn(args.pt.arguments[0], args.a, args.prevBinaryOp);
}
},
FLOAT: (args: MapFnArgs) => {
return args.fn(args.pt?.arguments?.[0]).wrap('(', ')');
FLOAT: async (args: MapFnArgs) => {
return (await args.fn(args.pt?.arguments?.[0])).builder.wrap('(', ')');
},
};

98
packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/functionMappings/mssql.ts

@ -6,48 +6,61 @@ import type { MapFnArgs } from '../mapFunctionName';
const mssql = {
...commonFns,
MIN: (args: MapFnArgs) => {
MIN: async (args: MapFnArgs) => {
if (args.pt.arguments.length === 1) {
return args.fn(args.pt.arguments[0]);
}
let query = '';
for (const [i, arg] of Object.entries(args.pt.arguments)) {
if (+i === args.pt.arguments.length - 1) {
query += args.knex.raw(`\n\tElse ${args.fn(arg).toQuery()}`).toQuery();
query += args.knex
.raw(
`\n\tElse ${(await await args.fn(arg)).builder.builder.toQuery()}`
)
.toQuery();
} else {
query += args.knex
.raw(
`\n\tWhen ${args.pt.arguments
`\n\tWhen ${(
await Promise.all(
args.pt.arguments
.filter((_, j) => +i !== j)
.map(
(arg1) =>
`${args.fn(arg).toQuery()} < ${args.fn(arg1).toQuery()}`
async (arg1) =>
`${(await args.fn(arg)).builder.toQuery()} < ${(
await args.fn(arg1)
).builder.toQuery()}`
)
)
.join(' And ')} Then ${args.fn(arg).toQuery()}`
).join(' And ')} Then ${(await args.fn(arg)).builder.toQuery()}`
)
.toQuery();
}
}
return args.knex.raw(`Case ${query}\n End${args.colAlias}`);
},
MAX: (args: MapFnArgs) => {
MAX: async (args: MapFnArgs) => {
if (args.pt.arguments.length === 1) {
return args.fn(args.pt.arguments[0]);
}
let query = '';
for (const [i, arg] of Object.entries(args.pt.arguments)) {
if (+i === args.pt.arguments.length - 1) {
query += args.knex.raw(`\nElse ${args.fn(arg).toQuery()}`).toQuery();
query += args.knex
.raw(`\nElse ${(await args.fn(arg)).builder.toQuery()}`)
.toQuery();
} else {
query += args.knex
.raw(
`\nWhen ${args.pt.arguments
.filter((_, j) => +i !== j)
.map(
(arg1) =>
`${args.fn(arg).toQuery()} > ${args.fn(arg1).toQuery()}`
async (arg1) =>
`${(await args.fn(arg)).builder.toQuery()} > ${(
await args.fn(arg1)
).builder.toQuery()}`
)
.join(' And ')} Then ${args.fn(arg).toQuery()}`
.join(' And ')} Then ${(await args.fn(arg)).builder.toQuery()}`
)
.toQuery();
}
@ -55,12 +68,15 @@ const mssql = {
return args.knex.raw(`Case ${query}\n End${args.colAlias}`);
},
LOG: (args: MapFnArgs) => {
LOG: async (args: MapFnArgs) => {
return args.knex.raw(
`LOG(${args.pt.arguments
`LOG(${(
await Promise.all(
args.pt.arguments
.reverse()
.map((ar) => args.fn(ar).toQuery())
.join(',')})${args.colAlias}`
.map(async (ar) => (await args.fn(ar)).builder.toQuery())
)
).join(',')})${args.colAlias}`
);
},
MOD: (pt) => {
@ -73,19 +89,19 @@ const mssql = {
},
REPEAT: 'REPLICATE',
NOW: 'getdate',
SEARCH: (args: MapFnArgs) => {
SEARCH: async (args: MapFnArgs) => {
args.pt.callee.name = 'CHARINDEX';
const temp = args.pt.arguments[0];
args.pt.arguments[0] = args.pt.arguments[1];
args.pt.arguments[1] = temp;
},
INT: (args: MapFnArgs) => {
INT: async (args: MapFnArgs) => {
return args.knex.raw(
`CASE WHEN ISNUMERIC(${args
.fn(args.pt.arguments[0])
.toQuery()}) = 1 THEN FLOOR(${args
.fn(args.pt.arguments[0])
.toQuery()}) ELSE 0 END${args.colAlias}`
`CASE WHEN ISNUMERIC(${(
await args.fn(args.pt.arguments[0])
).builder.toQuery()}) = 1 THEN FLOOR(${(
await args.fn(args.pt.arguments[0])
).builder.toQuery()}) ELSE 0 END${args.colAlias}`
);
},
MID: 'SUBSTR',
@ -94,8 +110,8 @@ const mssql = {
.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]);
DATEADD: async ({ fn, knex, pt, colAlias }: MapFnArgs) => {
const dateIN = (await fn(pt.arguments[1])).builder;
return knex.raw(
`CASE
WHEN ${fn(pt.arguments[0])} LIKE '%:%' THEN
@ -111,49 +127,59 @@ const mssql = {
END${colAlias}`
);
},
DATETIME_DIFF: ({ fn, knex, pt, colAlias }: MapFnArgs) => {
DATETIME_DIFF: async ({ fn, knex, pt, colAlias }: MapFnArgs) => {
const datetime_expr1 = fn(pt.arguments[0]);
const datetime_expr2 = fn(pt.arguments[1]);
const rawUnit = pt.arguments[2]
? fn(pt.arguments[2]).bindings[0]
? (await fn(pt.arguments[2])).builder.bindings[0]
: 'seconds';
const unit = convertUnits(rawUnit, 'mssql');
return knex.raw(
`DATEDIFF(${unit}, ${datetime_expr2}, ${datetime_expr1}) ${colAlias}`
);
},
WEEKDAY: ({ fn, knex, pt, colAlias }: MapFnArgs) => {
WEEKDAY: async ({ fn, knex, pt, colAlias }: MapFnArgs) => {
// DATEPART(WEEKDAY, DATE): sunday = 1, monday = 2, ..., saturday = 7
// WEEKDAY() returns an index from 0 to 6 for Monday to Sunday
return knex.raw(
`(DATEPART(WEEKDAY, ${
pt.arguments[0].type === 'Literal'
? `'${dayjs(fn(pt.arguments[0])).format('YYYY-MM-DD')}'`
? `'${dayjs((await fn(pt.arguments[0])).builder).format(
'YYYY-MM-DD'
)}'`
: fn(pt.arguments[0])
}) - 2 - ${getWeekdayByText(
pt?.arguments[1]?.value
)} % 7 + 7) % 7 ${colAlias}`
);
},
AND: (args: MapFnArgs) => {
AND: async (args: MapFnArgs) => {
return args.knex.raw(
`CASE WHEN ${args.knex
.raw(
`${args.pt.arguments
.map((ar) => args.fn(ar, '', 'AND').toQuery())
.join(' AND ')}`
`${(
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: (args: MapFnArgs) => {
OR: async (args: MapFnArgs) => {
return args.knex.raw(
`CASE WHEN ${args.knex
.raw(
`${args.pt.arguments
.map((ar) => args.fn(ar, '', 'OR').toQuery())
.join(' OR ')}`
`${(
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}`

184
packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/functionMappings/mysql.ts

@ -1,99 +1,99 @@
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: (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}`
// );
// },
};
export default mysql2;

28
packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/functionMappings/pg.ts

@ -12,13 +12,13 @@ const pg = {
CEILING: 'ceil',
POWER: 'pow',
SQRT: 'sqrt',
SEARCH: (args: MapFnArgs) => {
SEARCH: async (args: MapFnArgs) => {
return args.knex.raw(
`POSITION(${args.knex.raw(
args.fn(args.pt.arguments[1]).toQuery()
)} in ${args.knex.raw(args.fn(args.pt.arguments[0]).toQuery())})${
args.colAlias
}`
(await args.fn(args.pt.arguments[1])).builder.toQuery()
)} in ${args.knex
.raw((await args.fn(args.pt.arguments[0])).builder)
.toQuery()})${args.colAlias}`
);
},
INT(args: MapFnArgs) {
@ -51,11 +51,11 @@ const pg = {
)}')::interval${colAlias}`
);
},
DATETIME_DIFF: ({ fn, knex, pt, colAlias }: MapFnArgs) => {
DATETIME_DIFF: async ({ fn, knex, pt, colAlias }: MapFnArgs) => {
const datetime_expr1 = fn(pt.arguments[0]);
const datetime_expr2 = fn(pt.arguments[1]);
const rawUnit = pt.arguments[2]
? fn(pt.arguments[2]).bindings[0]
? (await fn(pt.arguments[2])).builder.bindings[0]
: 'seconds';
let sql;
const unit = convertUnits(rawUnit, 'pg');
@ -101,37 +101,39 @@ const pg = {
}
return knex.raw(`${sql} ${colAlias}`);
},
WEEKDAY: ({ fn, knex, pt, colAlias }: MapFnArgs) => {
WEEKDAY: async ({ fn, knex, pt, colAlias }: MapFnArgs) => {
// isodow: the day of the week as Monday (1) to Sunday (7)
// WEEKDAY() returns an index from 0 to 6 for Monday to Sunday
return knex.raw(
`(EXTRACT(ISODOW FROM ${
pt.arguments[0].type === 'Literal'
? `date '${dayjs(fn(pt.arguments[0])).format('YYYY-MM-DD')}'`
? `date '${dayjs((await fn(pt.arguments[0])).builder).format(
'YYYY-MM-DD'
)}'`
: fn(pt.arguments[0])
}) - 1 - ${getWeekdayByText(
pt?.arguments[1]?.value
)} % 7 + 7) ::INTEGER % 7 ${colAlias}`
);
},
AND: (args: MapFnArgs) => {
AND: async (args: MapFnArgs) => {
return args.knex.raw(
`CASE WHEN ${args.knex
.raw(
`${args.pt.arguments
.map((ar) => args.fn(ar, '', 'AND').toQuery())
.map(async (ar) => (await args.fn(ar, '', 'AND')).builder.toQuery())
.join(' AND ')}`
)
.wrap('(', ')')
.toQuery()} THEN TRUE ELSE FALSE END ${args.colAlias}`
);
},
OR: (args: MapFnArgs) => {
OR: async (args: MapFnArgs) => {
return args.knex.raw(
`CASE WHEN ${args.knex
.raw(
`${args.pt.arguments
.map((ar) => args.fn(ar, '', 'OR').toQuery())
.map(async (ar) => (await args.fn(ar, '', 'OR')).builder.toQuery())
.join(' OR ')}`
)
.wrap('(', ')')

382
packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/functionMappings/sqlite.ts

@ -1,198 +1,198 @@
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',
// 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}`
// );
// },
};
export default sqlite3;

9
packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/mapFunctionName.ts

@ -7,16 +7,19 @@ import type { Knex } from 'knex';
export interface MapFnArgs {
pt: any;
aliasToCol: { [alias: string]: string };
aliasToCol: Record<
string,
(() => Promise<{ builder: any }>) | string | undefined
>;
knex: XKnex;
alias: string;
a?: string;
fn: (...args: any) => Knex.QueryBuilder | any;
fn: (...args: any) => Promise<{ builder: Knex.QueryBuilder | any }>;
colAlias: string;
prevBinaryOp?: any;
}
const mapFunctionName = (args: MapFnArgs): any => {
const mapFunctionName = async (args: MapFnArgs): Promise<any> => {
const name = args.pt.callee.name;
let val;

Loading…
Cancel
Save