close
Skip to content

feat(sqlite): compile-time validation of INSERT NOT NULL constraints#4260

Open
Zelys-DFKH wants to merge 5 commits intolaunchbadge:mainfrom
Zelys-DFKH:fix/insert-not-null-validation
Open

feat(sqlite): compile-time validation of INSERT NOT NULL constraints#4260
Zelys-DFKH wants to merge 5 commits intolaunchbadge:mainfrom
Zelys-DFKH:fix/insert-not-null-validation

Conversation

@Zelys-DFKH
Copy link
Copy Markdown

Support compile-time validation of INSERT statements for NOT NULL constraints

Fixes #4206

The Problem

Right now, if you write an INSERT statement that forgets a NOT NULL column without a default, the sqlx macros happily compile — and then you get a runtime error when you first try to execute it.

conn.query_as!(
    SessionGroup,
    "INSERT INTO session_group (prop_a, prop_b) VALUES (?, ?)"  // missing prop_c
)

That's a runtime surprise that breaks the whole point of compile-time verification.

The Solution

The fix leverages SQLite's PRAGMA table_info() to inspect the schema at compile time. When describing an INSERT statement, we now:

  1. Parse the INSERT to extract the table name and any explicit column list
  2. Query the schema for the table's columns and NOT NULL constraints
  3. Cross-check: are all NOT NULL columns (without defaults) being inserted?
  4. Error at compile time if any are missing

The approach is graceful: If we can't parse the INSERT (complex cases like INSERT...SELECT), or if the table doesn't exist yet, validation silently skips. The whole thing degrades beautifully — edge cases still compile.

Implementation Details

Added to sqlx-sqlite/src/connection/describe.rs:

  • TableColumnInfo struct to hold parsed column metadata
  • is_insert_statement() to detect INSERT queries
  • extract_insert_info() to parse table name and column list (handles backticks, quotes, brackets)
  • get_table_columns() to run PRAGMA and fetch NOT NULL/default info
  • validate_insert_statement() to cross-check columns
  • Modified describe() to call validation before the normal flow

Tests

Added 10 regression tests covering:

All existing tests pass.

Trade-offs

What this does catch:

  • Missing NOT NULL columns in explicit INSERT statements → compile error ✓

What it doesn't (by design):

  • INSERT...SELECT (can't statically know what columns are returned)
  • INSERT...DEFAULT VALUES (no columns to check)
  • Schema-qualified names like INSERT INTO schema.table (parsing isn't exhaustive)

For those cases, validation is skipped and SQLite's runtime validation takes over. That's the right choice — catching 80% of cases at compile time is huge, and trying to be perfect would make the code fragile.

Why This Matters

This is a quality-of-life fix for anyone using sqlx macros with SQLite. It moves a class of errors from "find it in testing" to "catch it in CI," which is where they belong.

Thanks for considering this.

Zelys-DFKH added 3 commits May 7, 2026 18:46
Add PRAGMA table_info introspection to validate INSERT statements against
NOT NULL column requirements at compile time, catching errors before runtime.

Fixes launchbadge#4206
- Reformat function signature to single line (rustfmt requirement)
- Fix Error::Configuration formatting with proper indentation
- Add .into_sql_str() conversions to all test describe() calls
- Consolidate multi-line assert! macros to single lines

Fixes CI failures in PR launchbadge#4260 (Format and SQLite test checks)
…s correctly

Two issues prevented INSERT validation from working:
1. PRAGMA table_info() requires quoted table names to find tables reliably
2. SQLite returns empty strings for columns without defaults, which were being
   treated as 'has a default value', causing all columns to pass validation

Fixes all 9 INSERT validation tests.
@Zelys-DFKH Zelys-DFKH force-pushed the fix/insert-not-null-validation branch from c27a0e5 to 2ac4d83 Compare May 8, 2026 01:17
Zelys-DFKH and others added 2 commits May 7, 2026 22:14
SQLite auto-generates values for INTEGER PRIMARY KEY columns, so they do
not require explicit defaults. Update validation logic to check if a NOT
NULL column is an INTEGER PRIMARY KEY and skip validation for such columns.

This fixes the false positive where INSERT statements omitting an INTEGER
PRIMARY KEY column were flagged as missing required columns, even though
SQLite handles auto-generation for these columns.

All 39 describe tests now pass across async-global-executor, tokio, and
smol runtimes.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SQLite query macros don't reject INSERTs that omit NOT NULL columns without defaults

1 participant