@bitblit/ratchet-rdbms
Version:
Ratchet tooling for working with relational databases
137 lines • 4.9 kB
JavaScript
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