Skip to content

bug: rowid is not selectable in a projection — resolves in WHERE, unknown in the result list #708

Description

@dpsiderius

Description

rowid resolves in a WHERE clause but not in a result list:

SELECT rowid, v FROM t          -> cannot compile statement: unknown column "rowid"
UPDATE t SET v = ? WHERE rowid = 1   -> works
DELETE FROM t WHERE rowid = 1        -> works

So the compiler knows what rowid means in one expression position and not in
another. is_rowid_reference (src/codegen/stmt/update.rs, and the
equivalent in delete.rs) special-cases it for the seek path, but
Scope::resolve — which every projection column goes through — has no
knowledge of it and reports it as an unknown column.

_rowid_ and oid, SQLite's other two spellings, are presumably in the same
position.

Why it matters

Reported by the SQE consumer. It was the obvious workaround for the
bind-ordering defect (fixed separately): read the rowid, then address the row
by it with a single parameter. Not being able to select it closed that door.

More generally a consumer cannot round-trip a row it just inserted —
last_insert_rowid() gives the value, but nothing can read it back alongside
the row's columns, so "insert then fetch what I inserted" needs a unique key
the table may not have.

Scope

  • Scope::resolve learns rowid/_rowid_/oid as a pseudo-column on a
    rowid table, compiling to the cursor's rowid rather than a record field.
  • The SQLite shadowing rule applies and must be respected: if the table
    declares a real column with that name, the declared column wins. INTEGER PRIMARY KEY is the reverse case — the declared column is the rowid, which
    the schema layer already tracks as a rowid alias.
  • WITHOUT ROWID tables must report the same error stock sqlite3 does, not
    silently produce a number.

Non-goals

  • Writing to rowid in an UPDATE ... SET rowid = ... (SQLite allows it;
    nothing has asked and it interacts with index maintenance).
  • SELECT * expanding to include rowid — it does not in SQLite either.

Acceptance Criteria

  • SELECT rowid, <cols> FROM t matches the pinned 3.53.4 oracle row for
    row, including after deletes have left gaps in the sequence
  • _rowid_ and oid behave identically
  • A table with a declared column named rowid resolves to the declared
    column, matching the oracle
  • On an INTEGER PRIMARY KEY table, rowid and the alias column agree
  • On a WITHOUT ROWID table the error matches the oracle's
  • rowid works in WHERE, ORDER BY and a projection in the same
    statement

Complexity

Estimate: small-medium
Reasoning: One resolver gains one pseudo-column, and the cursor already
knows its rowid, so there is no new opcode. The care is entirely in the
shadowing rules — declared column wins, INTEGER PRIMARY KEY is the alias,
WITHOUT ROWID has none — and each of those is an oracle-diffable case rather
than a judgement call.

Refs: 013/Req-3, #705

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