Skip to content

Commit

Permalink
perf: faster escape-identifier when it doesn't contain quotes (#223)
Browse files Browse the repository at this point in the history
  • Loading branch information
ForbesLindesay authored Feb 7, 2022
1 parent c8d8471 commit 49eb2bb
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 15 deletions.
48 changes: 48 additions & 0 deletions packages/escape-identifier/src/__tests__/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import {
escapeMySqlIdentifier,
escapePostgresIdentifier,
escapeSQLiteIdentifier,
} from '..';

test(`escapePostgresIdentifier`, () => {
const e = (id: string) => expect(escapePostgresIdentifier(id));

e(`foo`).toBe('"foo"');
e(`foo_'bar'_BAZ`).toBe('"foo_\'bar\'_BAZ"');
e(`foo bar bing`).toBe('"foo bar bing"');
e(`hello "world"`).toBe('"hello ""world"""');
// {
// const start = Date.now();
// for (let i = 0; i < 10_000_000; i++) {
// escapePostgresIdentifier(`hello "world"`);
// }
// const end = Date.now();
// console.log(end - start);
// }
// {
// const start = Date.now();
// for (let i = 0; i < 10_000_000; i++) {
// escapePostgresIdentifier(`hello_world`);
// }
// const end = Date.now();
// console.log(end - start);
// }
});

test(`escapeMySqlIdentifier`, () => {
const e = (id: string) => expect(escapeMySqlIdentifier(id));

e(`foo`).toBe('`foo`');
e(`foo_'bar'_BAZ`).toBe("`foo_'bar'_BAZ`");
e(`foo bar bing`).toBe('`foo bar bing`');
e(`hello \`world\``).toBe('`hello ``world```');
});

test(`escapeSQLiteIdentifier`, () => {
const e = (id: string) => expect(escapeSQLiteIdentifier(id));

e(`foo`).toBe('"foo"');
e(`foo_'bar'_BAZ`).toBe('"foo_\'bar\'_BAZ"');
e(`foo bar bing`).toBe('"foo bar bing"');
e(`hello "world"`).toBe('"hello ""world"""');
});
26 changes: 11 additions & 15 deletions packages/escape-identifier/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,22 +81,17 @@ export function escapeSQLiteIdentifier(str: string) {
}

function quoteString(str: string, quoteChar: string) {
let escaped = quoteChar;

for (const c of str) {
if (c === quoteChar) escaped += c + c;
else escaped += c;
}

escaped += quoteChar;

return escaped;
if (!str.includes(quoteChar)) return quoteChar + str + quoteChar;
return (
quoteChar + str.split(quoteChar).join(quoteChar + quoteChar) + quoteChar
);
}

const NON_ASCII = /[^\u0001-\u007f]/;
function assertAscii(str: string, db: string, unicodeAvailable: boolean) {
if (!/^[\u0001-\u007f]+$/.test(str)) {
if (NON_ASCII.test(str)) {
throw new Error(
`${db} identifiers may only contain ASCII characters${
`${db} identifiers must only contain ASCII characters${
unicodeAvailable
? ` (to use unicode, pass {extended: true} when escaping the identifier)`
: ``
Expand All @@ -105,12 +100,13 @@ function assertAscii(str: string, db: string, unicodeAvailable: boolean) {
}
}

const NON_UNICODE = /[^\u0001-\uffff]/;
function assertUnicode(str: string, db: string) {
// U+0001 .. U+007F
// U+0080 .. U+FFFF
if (!/^[\u0001-\uffff]+$/.test(str)) {
if (NON_UNICODE.test(str)) {
throw new Error(
`${db} identifiers should only contain charcters in the range: U+0001 .. U+FFFF`,
`${db} identifiers must only contain characters in the range: U+0001 .. U+FFFF`,
);
}
}
Expand All @@ -123,7 +119,7 @@ function minLength(str: string, db: string) {
function maxLength(str: string, length: number, db: string, ref: string) {
if (str.length > length) {
throw new Error(
`${db} identifiers should not be longer than ${length} characters. ${str}`,
`${db} identifiers must not be longer than ${length} characters. ${str}`,
);
}
}

0 comments on commit 49eb2bb

Please sign in to comment.