UNPKG

@bitblit/ratchet-rdbms

Version:

Ratchet tooling for working with relational databases

137 lines 4.9 kB
import { Logger } from '@bitblit/ratchet-common/logger/logger'; import { ErrorRatchet } from '@bitblit/ratchet-common/lang/error-ratchet'; import { StringRatchet } from '@bitblit/ratchet-common/lang/string-ratchet'; export class QueryUtil { fields = []; replacements = {}; addReplacement(replacement, ...fields) { this.replacements = Object.assign(this.replacements, replacement); this.addFields(...fields); } appendReplacement(replacementKey, appendValue, ...fields) { this.replacements[replacementKey] = this.replacements[replacementKey] + appendValue; this.addFields(...fields); } addFields(...fields) { this.fields = this.fields.concat(...fields); } getFields() { return this.fields; } getReplacements() { return this.replacements; } static sqlInjectionUnsafeParamRenderer(value) { const rFn = (val) => (typeof val === 'string' ? '"' + val + '"' : StringRatchet.safeString(val)); const repl = Array.isArray(value) ? value.map((s) => rFn(s)).join(',') : rFn(value); return repl; } static renderQueryStringForPasteIntoTool(query, inFields, transform = QueryUtil.sqlInjectionUnsafeParamRenderer) { const fields = inFields ?? {}; let rval = QueryUtil.reformatQueryForLogging(query); if (rval) { const keys = Object.keys(fields); keys.sort((b, a) => a.length - b.length); for (const key of keys) { const val = fields[key]; const find = ':' + key; const repl = transform(val); rval = rval.split(find).join(repl); } if (!rval.endsWith(';')) { rval += ';'; } } return rval; } static reformatQueryForLogging(qry, inMaxLineLength = 80) { let maxLineLength = inMaxLineLength; if (!StringRatchet.trimToNull(qry)) { return null; } let loggableQuery = ''; let cleaned = StringRatchet.trimToEmpty(qry).split('\n').join(' ').split('\r').join(' '); while (cleaned.length > maxLineLength) { let idx = Math.min(cleaned.length, maxLineLength); while (idx > 0 && ![' ', ','].includes(cleaned.charAt(idx))) { idx--; } if (idx > 0) { loggableQuery += cleaned.substring(0, idx) + '\n'; cleaned = StringRatchet.trimToEmpty(cleaned.substring(idx)); } else { Logger.silly('Input contains a string longer than the max line length - bumping'); maxLineLength += 2; } } return loggableQuery + (cleaned.length > 0 ? cleaned : ''); } static addPrefixToFieldNames(fields, prefix = ':') { const rval = {}; Object.keys(fields).forEach((k) => { rval[prefix + k] = fields[k]; }); return rval; } static replaceNullReplacementsInQuery(query, fields) { const rval = query; Object.keys(fields).forEach((k) => { if (fields[k] === null || fields[k] === undefined) { rval.replaceAll(k, 'null'); } }); return rval; } static removeUnusedFields(query, fields, prefix) { const usedFields = QueryUtil.extractUsedNamedParams(query); const rval = {}; Object.keys(fields).forEach((k) => { if (usedFields.includes(k) || (prefix && usedFields.includes(prefix + k))) { rval[k] = fields[k]; } }); return rval; } static extractUsedNamedParams(query) { let state = 0; let idx = 0; const frags = []; const usedParams = []; let curString = ''; while (idx < query.length) { const nextChar = query.charAt(idx++); if (state === 0) { if (nextChar === ':') { frags.push(curString); curString = ':'; state = 1; } else { curString += nextChar; } } else if (state === 1) { if (!StringRatchet.stringContainsOnlyAlphanumeric(nextChar)) { usedParams.push(curString); curString = nextChar; state = 0; } else { curString += nextChar; } } else { throw ErrorRatchet.fErr('Cant happen - invalid state'); } } if (state === 0) { frags.push(curString); } else { usedParams.push(curString); } return usedParams; } } //# sourceMappingURL=query-util.js.map