Repository navigation
Fix invalid SQL for column-level CHECK constraints using range/BETWEEN expressions - #1371
Conversation
|
Pushed a fix setting TUIST_USE_SWIFTERPM: '0' in .github/workflows/build.yml to resolve the Tuist CI failure (Path not found: .../Tuist/Package.resolved) on the macOS runner. |
| run: ./run-tests.sh | ||
| - name: "Run tests (tuist)" | ||
| env: | ||
| TUIST_USE_SWIFTERPM: '0' |
There was a problem hiding this comment.
i believe this is no longer necessary now, fixed in 08a2b63
|
Removed the redundant TUIST_USE_SWIFTERPM environment variable in 93061d6. Thanks! |
|
The remaining red checks are macOS build jobs from CI. I don't have a macOS runner locally on this machine, so I can't reproduce those here; happy to adjust if someone can share the failing compiler/test output from the job. |
|
don't worry, that's been around for some time, it looks like some undefined behavior introduced in one of the recently merged PRs, i'll investigate |
|
that crash is probably the one you submitted a fix for in #1373? that PR is missing a detailed description, it looks like the issue is that the |
|
Hi @jberkel, thank you for following up! To clarify the rationale behind #1373: In contrast, sqlite3_close_v2(handle) is specifically designed by SQLite for reference-counted / GC environments:
I've also updated the description on #1373 to reflect this context. Let me know what you think! |
|
Ok, that's much clearer, I missed the |
|
No, but it only seems to fail on macos-15, perhaps it's related to something in that runner. |
The `~=` operator overloads used for Range/ClosedRange/PartialRange
pattern matching (e.g. `0...26 ~= column`) build their SQL fragment
without wrapping it in parentheses, unlike every other comparison
operator (`>`, `<`, `==`, etc.), which all self-wrap via
`Operator.infix(...)`.
This is harmless standalone (e.g. inside `.filter(...)`), but it
produces invalid SQL when used as a column-level CHECK constraint via
`table.create { t in t.column(col, check: 0...26 ~= col) }`, because
Schema.swift's per-column `definition()` relies on the check
expression already being parenthesized (it just does
`"CHECK" + " " + check`, same as the table-level `check()` builder
relies on `.prefix` to add its own parens). Since BETWEEN/range
expressions weren't self-wrapped, the generated SQL came out as:
CREATE TABLE "t" ("type" INTEGER NOT NULL CHECK "type" BETWEEN 0 AND 26)
which SQLite rejects with a syntax error near the CHECK condition,
exactly as reported in #1056.
Fixes #1056.
Fix: wrap the `~=` operators' generated SQL in parens, matching the
convention already used by every other comparison operator. Updated
the existing BETWEEN/range operator tests to expect the parenthesized
output, and added a regression test in SchemaTests that reproduces the
exact column-level CHECK scenario from the issue and asserts the
generated CREATE TABLE statement is now well-formed.
Verified by tracing the SQL-generation code path end-to-end (Operator
`infix`/`wrap` helpers, the `~=` overloads, and Schema.swift's
`definition()`/`check()`) since a full Swift toolchain wasn't
available in this environment to run `swift test` directly; the
updated/added unit tests exercise this exact path and should be run
by CI.
93061d6 to
42c271d
Compare
|
Thanks for looking into this, @jberkel! I have rebased this branch cleanly onto latest upstream master to pick up the recent CI updates and disable-macos-15 fix. |
Summary
Fixes #1056.
Column-level
CHECKconstraints built from a range pattern (e.g.t.column(type, check: 0...26 ~= type)) generate invalid SQL and SQLite rejects theCREATE TABLEstatement with a syntax error, exactly as reported in #1056:Root cause
Every comparison operator (
>,<,==,>=, etc.) self-wraps its generated SQL in parentheses viaOperator.infix(...)(seeSources/SQLite/Typed/Operators.swift), e.g.column > 0compiles to("column" > 0).The
~=operator overloads used forRange/ClosedRange/PartialRange...pattern matching (Sources/SQLite/Typed/Operators.swift:560-599) are the one exception — they build their BETWEEN/comparison SQL fragment directly without wrapping it in parens.This is harmless when the expression is used standalone (e.g.
.filter(0...26 ~= type)), but it breaks when used as a column-levelCHECKconstraint.Schema.swift's per-columndefinition()builds the CHECK clause as"CHECK" + " " + check(Sources/SQLite/Typed/Schema.swift:578), relying on the passed-in expression already being parenthesized — which holds for every other comparator, but not for the un-wrapped BETWEEN/range output. (The table-levelcheck()builder inSchema.swift:438doesn't hit this because it uses"CHECK".prefix(condition), which unconditionally adds its own parens — that's why the issue reporter found "table checks" worked around the bug.)Fix
Wrap the SQL generated by all 10
~=overloads (ClosedRange,Range,PartialRangeThrough,PartialRangeUpTo,PartialRangeFrom, each with an optional-column variant) in parentheses, matching the convention every other comparison operator already follows. This makes range-based conditions self-contained/composable like the rest of the operator library, and fixes the column-level CHECK bug without touchingSchema.swiftat all.Testing
Tests/SQLiteTests/Typed/OperatorsTests.swiftto expect the now-parenthesized output (purely a string-format change; the underlying SQL semantics/bindings are unchanged since parens don't alter meaning in these positions).test_column_withRangeCheck_compilesValidCheckConstraintinTests/SQLiteTests/Schema/SchemaTests.swift, which reproduces the exact column-level CHECK scenario from Column check constraints generate a syntax error #1056 and asserts the generatedCREATE TABLEstatement is now well-formed:CREATE TABLE "table" ("int64" INTEGER NOT NULL CHECK ("int64" BETWEEN 0 AND 26)).swift testdirectly, so I traced the fix end-to-end through theOperator.infix/.wrap/.prefixhelpers, the~=overloads, andSchema.swift'sdefinition()/check()to confirm the generated string exactly matches the new test expectations (including that non-optional/optional variants and the standalone table-levelcheck()path are unaffected). Would appreciate CI running the updated suite.