Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 26 additions & 55 deletions src/formatter/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@ import { INVALID_PARAMETERS } from "../common/errors";

const CHARS_GLOBAL_REGEXP = /[\0\b\t\n\r\x1a"'\\]/g; // eslint-disable-line no-control-regex

const COMMENTS_REGEXP =
/("(""|[^"])*")|('(''|[^'])*')|(--[^\n\r]*)|(\/\*[\w\W]*?(?=\*\/)\*\/)/gm;

const CHARS_ESCAPE_MAP: Record<string, string> = {
"\0": "\\0",
"\b": "\\b",
Expand All @@ -19,19 +16,6 @@ const CHARS_ESCAPE_MAP: Record<string, string> = {
"\\": "\\\\"
};

const removeComments = (query: string) => {
query = query.replace(COMMENTS_REGEXP, match => {
if (
(match[0] === '"' && match[match.length - 1] === '"') ||
(match[0] === "'" && match[match.length - 1] === "'")
)
return match;

return "";
});
return query;
};

const zeroPad = (param: number, length: number, direction = "left") => {
let paded = param.toString();
while (paded.length < length) {
Expand Down Expand Up @@ -73,52 +57,40 @@ export class QueryFormatter {
namedParams: Record<string, unknown>
) {
params = [...params];
const regex = /''|""|``|\\\\|\\'|\\"|'|"|`|\?|::|:(\w+)/g;

const STATE = {
WHITESPACE: 0,
SINGLE_QUOTE: 1,
DOUBLE_QUOTE: 2,
BACKTICK: 3
};

const stateSwitches: Record<string, number> = {
"'": STATE.SINGLE_QUOTE,
'"': STATE.DOUBLE_QUOTE,
"`": STATE.BACKTICK
};

let state = STATE.WHITESPACE;

query = query.replace(regex, (str, paramName: string | undefined) => {
if (str in stateSwitches) {
if (state === STATE.WHITESPACE) {
state = stateSwitches[str];
} else if (state === stateSwitches[str]) {
state = STATE.WHITESPACE;
}
}

if (str === "?") {
if (state !== STATE.WHITESPACE) return str;

if (params.length == 0) {
throw new Error("Too few parameters given");
// Matches:
// - ' strings with \ escapes
// - " strings with \ escapes
// - /* */ comments
// - -- comments
// - ? parameters
// - :: operator
// - :named parameters
const tokenizer =
/'(?:[^'\\]+|\\.)*'|"(?:[^"\\]+|\\.)*"|\/\*[\s\S]*\*\/|--.*|(\?)|::|:(\w+)/g;

query = query.replace(
tokenizer,
(str, param: string | undefined, paramName: string | undefined) => {
if (param) {
if (params.length == 0) {
throw new Error("Too few parameters given");
}

return this.escape(params.shift());
}

return this.escape(params.shift());
} else if (paramName) {
if (state !== STATE.WHITESPACE) return str;
if (paramName) {
if (!Object.prototype.hasOwnProperty.call(namedParams, paramName)) {
throw new Error(`Parameter named "${paramName}" not given`);
}

if (!Object.prototype.hasOwnProperty.call(namedParams, paramName)) {
throw new Error(`Parameter named "${paramName}" not given`);
return this.escape(namedParams[paramName]);
}

return this.escape(namedParams[paramName]);
} else {
return str;
}
});
);

if (params.length) {
throw new Error("Too many parameters given");
Expand Down Expand Up @@ -270,7 +242,6 @@ export class QueryFormatter {
parameters?: unknown[],
namedParameters?: Record<string, unknown>
): string {
query = removeComments(query);
if (parameters || namedParameters) {
if (parameters) {
checkArgumentValid(Array.isArray(parameters), INVALID_PARAMETERS);
Expand Down
17 changes: 9 additions & 8 deletions test/unit/statement.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,6 @@ describe("format query", () => {
`"select 1 from table where bar = 2"`
);
});
it("format 3", () => {
const queryFormatter = new QueryFormatter();
const query = "select * from table where bar = ? and foo = `some'?`";
const formattedQuery = queryFormatter.formatQuery(query, [1]);
expect(formattedQuery).toMatchInlineSnapshot(
`"select * from table where bar = 1 and foo = \`some'?\`"`
);
});
it("escape boolean", () => {
const queryFormatter = new QueryFormatter();
const query = "select * from table where bar = ?;";
Expand Down Expand Up @@ -128,6 +120,15 @@ describe("format query", () => {
expect(true).toEqual(true);
}
});
it("format with comments in strings", () => {
const queryFormatter = new QueryFormatter();
const query =
"SELECT 'str \\' ? -- not comment', /* comment? */ ? -- comment?";
const formattedQuery = queryFormatter.formatQuery(query, ["foo"]);
expect(formattedQuery).toMatchInlineSnapshot(
`"SELECT 'str \\\\' ? -- not comment', /* comment? */ 'foo' -- comment?"`
);
});
it("format tuple", () => {
const queryFormatter = new QueryFormatter();
const query = "select foo from bar where foo in ?";
Expand Down