Skip to content

Commit e214603

Browse files
committed
fix: 2 correctness bugs — SQL doubled-quote escaping, SetOperator cached keys
- lexer: handle SQL-standard '' (doubled single-quote) escaping in string literals - parser: unescape '' → ' in string value extraction - SetOperator.rowKey: don't cache sorted keys from first row — left/right sides may have different column sets, causing false matches in UNION/INTERSECT/EXCEPT
1 parent 017389e commit e214603

File tree

3 files changed

+7
-7
lines changed

3 files changed

+7
-7
lines changed

src/operators.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1157,10 +1157,8 @@ export class SetOperator implements Operator {
11571157
if (mode !== "union_all") this.seen = new Set();
11581158
}
11591159

1160-
private _sortedKeys: string[] | null = null;
11611160
private rowKey(row: Row): string {
1162-
if (!this._sortedKeys) this._sortedKeys = Object.keys(row).sort();
1163-
const keys = this._sortedKeys;
1161+
const keys = Object.keys(row).sort();
11641162
let result = "";
11651163
for (let i = 0; i < keys.length; i++) {
11661164
if (i > 0) result += "\x00";

src/sql/lexer.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -146,11 +146,13 @@ export function tokenize(sql: string): Token[] {
146146
continue;
147147
}
148148

149-
// String literals (single-quoted)
149+
// String literals (single-quoted, supports '' and \' escaping)
150150
if (ch === "'") {
151151
pos++;
152-
while (pos < len && sql[pos] !== "'") {
153-
if (sql[pos] === "\\" && pos + 1 < len) pos++; // skip escape
152+
while (pos < len) {
153+
if (sql[pos] === "'" && pos + 1 < len && sql[pos + 1] === "'") { pos += 2; continue; } // doubled quote
154+
if (sql[pos] === "'") break;
155+
if (sql[pos] === "\\" && pos + 1 < len) pos++; // backslash escape
154156
pos++;
155157
}
156158
if (pos >= len) throw new SqlLexerError(`Unterminated string literal starting at position ${start}`, start);

src/sql/parser.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -611,7 +611,7 @@ class Parser {
611611
// String literal
612612
if (this.match(TokenType.STRING)) {
613613
// Strip surrounding quotes
614-
const raw = tok.lexeme.slice(1, -1).replace(/\\'/g, "'");
614+
const raw = tok.lexeme.slice(1, -1).replace(/''/g, "'").replace(/\\'/g, "'");
615615
return { kind: "value", value: { type: "string", value: raw } };
616616
}
617617

0 commit comments

Comments
 (0)