Skip to content

fix: CREATE TABLE does not emit sqlite_autoindex_* for declared composite keys (oracle calls the file malformed) #687

Description

@dpsiderius

Description

The other half of #685. That ticket fixed adopting a stock-created
file: an existing sqlite_autoindex_* is now recovered and maintained.
This one fixes producing one.

CREATE TABLE with a declared composite PRIMARY KEY or UNIQUE
constraint currently creates no sqlite_autoindex_* b-tree and no
sqlite_master row for it. Stock SQLite then reads the DDL, expects the
index, does not find it, and reports "database disk image is malformed
(11)"
on any write or integrity_check.

So a table this crate creates with a declared composite key is not a
valid SQLite database, which is a direct hit on the crate's headline
byte-compatibility claim.

Recorded today in src/codegen/stmt/insert.rs's module doc:

A composite PRIMARY KEY(...)/UNIQUE(...) table constraint with no
backing CREATE INDEX/on-disk index (this codebase doesn't
auto-create sqlite_autoindex_* entries yet) has no real index to seek
against, so it still isn't enforced — that's a CREATE TABLE-side gap,
not an INSERT-codegen one.

#685 closed the INSERT-codegen half. This is the CREATE TABLE half.

Scope

src/codegen/ddl/CREATE TABLE must, for each constraint that
SQLite would give an autoindex:

  • allocate an index b-tree root page;
  • write a sqlite_master row with type = 'index',
    name = sqlite_autoindex_<table>_<n>, tbl_name = <table>, the new
    root page, and sql = NULL;
  • number them by the rule fix: writes silently corrupt tables carrying a sqlite_autoindex_* (composite PRIMARY KEY / UNIQUE) #685 derived from the oracle and now
    implements in ddl_reader::autoindex_key_lists — declaration order,
    column-level constraints included, rowid-alias and WITHOUT ROWID
    primary keys skipped and consuming no number, redundant constraints
    collapsed.

The rule is already written and tested; this ticket consumes it rather
than re-deriving it.

Why it is separate from #685

Different blast radius. #685 touches the schema reader and refuses
unsafe writes; this touches DDL codegen and the freelist/page
allocator. #685 is what unblocks the SQE integration, because SQE's
warehouse files are created by C SQLite and only read and written by
this crate — it never creates them.

Complexity

Estimate: medium
Reasoning: The numbering rule and its tests already exist from
#685. The work is DDL codegen: allocating a root page per autoindex,
emitting the sqlite_master rows with a NULL sql, and keeping the
whole thing inside one transaction so a partial CREATE TABLE cannot
leave a table with some of its indexes. Populating the index is trivial
for a fresh empty table.

Acceptance Criteria

  • A table created here with a composite PRIMARY KEY passes the
    oracle's PRAGMA integrity_check and accepts oracle writes
  • No "database disk image is malformed (11)" from the oracle on any
    created shape
  • sqlite_master rows match the oracle's byte-for-byte, including
    sql IS NULL
  • Round trip: create here, write with the oracle, read back here
  • Rowid-alias and WITHOUT ROWID tables gain no autoindex
  • A failed CREATE TABLE leaves no partial index behind

Refs: 010/Req-8, #685

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions