diff --git a/src/index.ts b/src/index.ts index 82ec561..3cee1fa 100644 --- a/src/index.ts +++ b/src/index.ts @@ -29,6 +29,7 @@ const CHARS_ESCAPE_MAP: Record = { const charCode = { singleQuote: 39, + backtick: 96, backslash: 92, dash: 45, slash: 47, @@ -110,6 +111,23 @@ const skipSqlContext = (sql: string, position: number): number => { return sql.length; } + if (currentChar === charCode.backtick) { + const length = sql.length; + + for (let cursor = position + 1; cursor < length; cursor++) { + if (sql.charCodeAt(cursor) !== charCode.backtick) continue; + + if (sql.charCodeAt(cursor + 1) === charCode.backtick) { + cursor++; + continue; + } + + return cursor + 1; + } + + return length; + } + if (currentChar === charCode.dash && nextChar === charCode.dash) { const lineBreak = sql.indexOf('\n', position + 2); return lineBreak === -1 ? sql.length : lineBreak + 1; @@ -132,6 +150,7 @@ const findNextPlaceholder = (sql: string, start: number): number => { if ( code === charCode.singleQuote || + code === charCode.backtick || code === charCode.dash || code === charCode.slash ) { @@ -153,6 +172,7 @@ const findSetKeyword = (sql: string, startFrom = 0): number => { if ( code === charCode.singleQuote || + code === charCode.backtick || code === charCode.dash || code === charCode.slash ) { diff --git a/test/everyday-queries.test.ts b/test/everyday-queries.test.ts index 9c03a63..b7d0356 100644 --- a/test/everyday-queries.test.ts +++ b/test/everyday-queries.test.ts @@ -544,3 +544,75 @@ describe('Nice to have: Boolean in various contexts', () => { assert.equal(sql, 'UPDATE t SET `active` = true, `archived` = false'); }); }); + +describe('Critical: Backtick-quoted identifiers with comment-like sequences', () => { + test('database/table names with double dashes', () => { + const sql = format( + 'INSERT INTO `db--name`.`table`(`a`, `b`) VALUES (?, ?)', + [1, 'hello'] + ); + assert.equal( + sql, + "INSERT INTO `db--name`.`table`(`a`, `b`) VALUES (1, 'hello')" + ); + }); + + test('column names with double dashes', () => { + const sql = format( + 'INSERT INTO t (`col--1`, `col--2`) VALUES (?, ?)', + [1, 2] + ); + assert.equal(sql, 'INSERT INTO t (`col--1`, `col--2`) VALUES (1, 2)'); + }); + + test('backticks with block comment markers', () => { + const sql = format('INSERT INTO `table/*name*/` VALUES (?)', [1]); + assert.equal(sql, 'INSERT INTO `table/*name*/` VALUES (1)'); + }); + + test('escaped backticks inside identifiers', () => { + const sql = format('INSERT INTO `table``name` VALUES (?)', [1]); + assert.equal(sql, 'INSERT INTO `table``name` VALUES (1)'); + }); + + test('multiple backtick identifiers with mixed comment markers', () => { + const sql = format( + 'SELECT * FROM `db--1`.`table/*test*/` WHERE `col--id` = ?', + [42] + ); + assert.equal( + sql, + 'SELECT * FROM `db--1`.`table/*test*/` WHERE `col--id` = 42' + ); + }); + + test('UPDATE with backtick identifiers containing dashes', () => { + const sql = format('UPDATE `table--name` SET `col--1` = ? WHERE id = ?', [ + 'value', + 1, + ]); + assert.equal( + sql, + "UPDATE `table--name` SET `col--1` = 'value' WHERE id = 1" + ); + }); + + test('SELECT with ?? and backtick-quoted values with dashes', () => { + const sql = format('SELECT ?? FROM `users--table` WHERE id = ?', [ + ['col--1', 'col--2'], + 1, + ]); + assert.equal( + sql, + 'SELECT `col--1`, `col--2` FROM `users--table` WHERE id = 1' + ); + }); + + test('SELECT with ?? without any backticks in query', () => { + const sql = format('SELECT ?? FROM users WHERE id = ?', [ + ['id', 'name'], + 1, + ]); + assert.equal(sql, 'SELECT `id`, `name` FROM users WHERE id = 1'); + }); +});