mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-18 21:16:47 +00:00
fix(server): plan 4 — RULE12 paren-count scanner ignores string literals
The entity generator's RULE12_fixGarbledCheckExpressions CHECK-expression
paren-balance scanner counted every `(`/`)` character regardless of
whether it fell inside a single-quoted SQL string literal. A CHECK
expression like `status IN ('confirmed)', 'tentative')` has a literal `)`
inside its first string value; the scanner would hit depth 0 at that
in-string character and truncate the expression, losing its own real
closing paren. The scanner now tracks single-quote string state (handling
the doubled `''` SQLite escape) and skips parens while inside one.
Latent-bug fix — no shipped entity's CHECK expression has hit this shape
yet (3d's ledger: "nothing affected today").
This commit is contained in:
@@ -939,6 +939,18 @@ export interface CheckExpressionFixup {
|
||||
* `end_date >= start_date` — or balanced-but-never-closing nested ones) is
|
||||
* left byte-identical: this only fires on the specific over-capture shape,
|
||||
* never on a clean expression.
|
||||
*
|
||||
* The walk skips a `(`/`)` that falls inside a single-quoted SQL string
|
||||
* literal (a doubled `''` is SQLite's escape for a literal quote *inside*
|
||||
* the literal, so it does not end the string) — an expression like
|
||||
* `status IN ('confirmed)', 'tentative')` has a literal `)` inside its first
|
||||
* string value that is not a real closing paren; without this tracking the
|
||||
* scanner would hit depth 0 at that spurious character and truncate a
|
||||
* perfectly clean expression (losing its own genuine trailing `)` in the
|
||||
* process, corrupting otherwise-valid SQL), not just leave RULE12-003's kind
|
||||
* of case ambiguous the way a false paren-count fires here (Task 8a's own
|
||||
* carry — "nothing affected today" per 3d's ledger, a latent-bug fix, not an
|
||||
* active one — no shipped entity's CHECK expression has hit this shape yet).
|
||||
*/
|
||||
export function RULE12_fixGarbledCheckExpressions(metadata: EntityMetadata[]): CheckExpressionFixup[] {
|
||||
const fixups: CheckExpressionFixup[] = [];
|
||||
@@ -948,9 +960,25 @@ export function RULE12_fixGarbledCheckExpressions(metadata: EntityMetadata[]): C
|
||||
const expr = check.expression;
|
||||
let depth = 1;
|
||||
let cut = -1;
|
||||
let inString = false;
|
||||
for (let i = 0; i < expr.length; i++) {
|
||||
if (expr[i] === '(') depth++;
|
||||
else if (expr[i] === ')') {
|
||||
const ch = expr[i];
|
||||
if (inString) {
|
||||
if (ch === "'") {
|
||||
if (expr[i + 1] === "'") {
|
||||
i++; // a doubled '' is an escaped quote inside the literal, not its end
|
||||
continue;
|
||||
}
|
||||
inString = false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (ch === "'") {
|
||||
inString = true;
|
||||
continue;
|
||||
}
|
||||
if (ch === '(') depth++;
|
||||
else if (ch === ')') {
|
||||
depth--;
|
||||
if (depth === 0) {
|
||||
cut = i;
|
||||
|
||||
@@ -549,6 +549,18 @@ describe('RULE12_fixGarbledCheckExpressions', () => {
|
||||
const fixups = RULE12_fixGarbledCheckExpressions([meta]);
|
||||
expect(fixups).toEqual([]);
|
||||
});
|
||||
|
||||
it("RULE12-007: a quoted ')' inside a CHECK expression's own string literal is not mistaken for a closing paren — left byte-identical, no fixup", () => {
|
||||
const meta = fixtureMeta('SchoolHolidayPeriods', 'school_holiday_periods', []);
|
||||
// The IN-list's first value itself contains a literal ')'. A naive
|
||||
// char-by-char paren count would hit depth 0 at that in-string
|
||||
// character and truncate the expression, losing the list's own real
|
||||
// closing paren — this fixture pins that it does not.
|
||||
meta.checks = [{ name: 'sh_status_check', expression: "status IN ('confirmed)', 'tentative')" }];
|
||||
const fixups = RULE12_fixGarbledCheckExpressions([meta]);
|
||||
expect(meta.checks[0].expression).toBe("status IN ('confirmed)', 'tentative')");
|
||||
expect(fixups).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('RULE13_pinDeleteRuleDrift', () => {
|
||||
|
||||
Reference in New Issue
Block a user