UNPKG

@dossierhq/database-adapter

Version:

A library for adapting Dossier to a database, such as SQLite or PostgreSQL.

135 lines 4.95 kB
/// <reference types="./SqlQueryBuilder.d.ts" /> export const DEFAULT = Symbol('DEFAULT'); const ValueReferenceSymbol = Symbol('ValueReference'); const RawSqlSymbol = Symbol('RawSql'); function createSqlQuery(config) { const query = { text: '', values: [] }; const sql = (strings, ...args) => { for (let i = 0; i < strings.length; i++) { if (i > 0) { addValueToQuery(config, query, args[i - 1]); } addTextToQuery(query, strings[i], i === 0); } }; const addValue = (value) => addValueReference(query, value); const removeTrailingWhere = () => { if (query.text.endsWith('WHERE')) { query.text = query.text.slice(0, query.text.length - 'WHERE'.length).trimEnd(); } }; return { sql, query, addValue, removeTrailingWhere }; } function createRawSql(sql) { return { marker: RawSqlSymbol, sql }; } function addValueReference(query, value) { query.values.push(value); return { marker: ValueReferenceSymbol, index: query.values.length }; } function addValueToQuery(config, query, value) { if (value === DEFAULT) { query.text += 'DEFAULT'; } else if (typeof value === 'object' && value && 'marker' in value && value.marker === ValueReferenceSymbol) { query.text += `${config.indexPrefix}${value.index}`; } else if (typeof value === 'object' && value && 'marker' in value && value.marker === RawSqlSymbol) { query.text += value.sql; } else { query.values.push(value); query.text += `${config.indexPrefix}${query.values.length}`; // 1-based index } } function addTextToQuery(query, text, addSeparator) { let existingText = query.text; let textToAdd = text; if (existingText.endsWith('WHERE') && textToAdd.startsWith('AND')) { textToAdd = textToAdd.slice('AND'.length).trimStart(); } else if (existingText.endsWith('WHERE') && textToAdd.startsWith('ORDER')) { existingText = existingText.slice(0, existingText.length - 'WHERE'.length).trimEnd(); } else if (existingText.endsWith('VALUES') && textToAdd.startsWith(',')) { textToAdd = textToAdd.slice(','.length); } let separator = ''; if (existingText && addSeparator) { //TODO simplify this const currentEndsWithPunctuation = endsWithPunctuation(existingText); const newStartsWithBracket = startsWithPunctuation(textToAdd); const currentEndsWithKeyword = endsWithKeyword(existingText); const newStartsWithKeyword = startsWithKeyword(textToAdd); const currentEndsWithOperator = endsWithOperator(existingText); const newStartsWithOperator = startsWithOperator(textToAdd); if (currentEndsWithPunctuation || newStartsWithBracket) { separator = ''; } else if (currentEndsWithOperator || newStartsWithOperator) { separator = ' '; } else if (currentEndsWithKeyword && !newStartsWithKeyword) { separator = ' '; } else if (!currentEndsWithKeyword && !newStartsWithKeyword) { separator = ' '; } else if (!currentEndsWithKeyword && newStartsWithKeyword) { separator = ' '; } else if (currentEndsWithKeyword && newStartsWithKeyword) { separator = ' '; } } query.text = existingText + separator + textToAdd; } function startsWithPunctuation(query) { const firstChar = query[0]; return firstChar === ')' || firstChar === ',' || firstChar === '.' || firstChar === ' '; } function endsWithPunctuation(query) { const lastChar = query[query.length - 1]; return lastChar === '(' || lastChar === ',' || lastChar === '.' || lastChar === ' '; } function startsWithKeyword(query) { return !!/^[A-Z]+\w/.exec(query); } function endsWithKeyword(query) { return !!/\w[A-Z]+$/.exec(query); } function startsWithOperator(query) { return !!/^[=!<>]+/.exec(query); } function endsWithOperator(query) { return !!/[=!<>]+$/.exec(query); } export function createPostgresSqlQuery() { return createSqlQuery({ indexPrefix: '$' }); } export function buildPostgresSqlQuery(callback) { const { query, ...builder } = createPostgresSqlQuery(); callback(builder); return query; } export function createSqliteSqlQuery() { const sqlBuilder = createSqlQuery({ indexPrefix: '?' }); const addValueList = (list) => { const values = list.map((it) => sqlBuilder.addValue(it)); return createRawSql('(' + values.map((it) => `?${it.index}`).join(', ') + ')'); }; const sqliteBuilder = { ...sqlBuilder, addValueList }; return sqliteBuilder; } export function buildSqliteSqlQuery(callback) { const { query, ...builder } = createSqliteSqlQuery(); callback(builder); return query; } //# sourceMappingURL=SqlQueryBuilder.js.map