Files
dashlibstorage/src/providers/sqlite3/queries/translate.js
2026-10-30 15:43:28 +01:00

56 lines
2.1 KiB
JavaScript

function quoteIdentifiers(sql) {
return sql.replace(/\[([^\]]+)]/g, '"$1"');
}
function moveReturning(sql) {
const output = /\bOUTPUT\s+([\s\S]*?)(?=\bVALUES\b|\bWHERE\b|\bDEFAULT\b)/i.exec(sql);
if (!output) return sql;
const columns = output[1]
.split(',')
.map((column) => column.trim().replace(/\bINSERTED\./gi, ''))
.join(', ');
const withoutOutput = sql.slice(0, output.index) + sql.slice(output.index + output[0].length);
return `${withoutOutput.trim().replace(/;$/, '')}\nRETURNING ${columns}`;
}
function moveTop(sql) {
const matches = [...sql.matchAll(/\bTOP\s*\(?\s*(\d+)\s*\)?\s*/gi)];
if (matches.length === 0) return sql;
if (matches.length > 1) {
// Complex nested queries are supplied as compatibility exports until
// their provider-specific store paths are converted. Do not corrupt them
// by appending one LIMIT to the outer statement.
return sql;
}
return `${sql.replace(matches[0][0], '').trim().replace(/;$/, '')}\nLIMIT ${matches[0][1]}`;
}
/** Convert the common, context-free subset of MSSQL query syntax to SQLite. */
export function sqliteQuery(sql) {
if (typeof sql !== 'string') return sql;
let result = quoteIdentifiers(sql)
.replace(/\bWITH\s*\(\s*(?:NOLOCK|UPDLOCK|ROWLOCK|HOLDLOCK|READPAST)(?:\s*,\s*(?:NOLOCK|UPDLOCK|ROWLOCK|HOLDLOCK|READPAST))*\s*\)/gi, '')
.replace(/\bISNULL\s*\(/gi, 'IFNULL(')
.replace(/\bLEN\s*\(/gi, 'LENGTH(')
.replace(/\bSYSUTCDATETIME\s*\(\s*\)/gi, "strftime('%Y-%m-%dT%H:%M:%fZ', 'now')")
.replace(/\bDATEDIFF_BIG\s*\(\s*second\s*,\s*'1970-01-01'\s*,\s*([^)]+?)\s*\)/gi, "CAST(strftime('%s', $1) AS INTEGER)")
.replace(/\bCAST\s*\(([^()]+?)\s+AS\s+INT\s*\)/gi, 'CAST($1 AS INTEGER)')
.replace(/\bCONVERT\s*\(\s*VARBINARY\s*\(\s*MAX\s*\)\s*,\s*([^)]+?)\s*\)/gi, '$1');
result = moveReturning(result);
result = moveTop(result);
return result;
}
export function translateQueries(module, moduleName) {
return Object.fromEntries(
Object.entries(module).map(([name, value]) => [
name,
typeof value === 'string' && /\s/.test(value)
? sqliteQuery(value)
: value,
]),
);
}