Skip to content

bug: by-name column access returns column1/column2 for joins and compounds #709

Description

@dpsiderius

Description

Row::get_by_name only works on single-table selects. For a join or a
compound, result_column_names yields positional placeholders:

SELECT a.x, b.y FROM a JOIN b ON ...   -> column names: ["column1", "column2"]
SELECT x FROM a UNION SELECT y FROM b  -> column names: ["column1"]

So get_by_name("x") fails with ColumnNotFound on exactly the queries where
positional access is hardest to get right — the ones with columns from more
than one source.

Spec 013 Requirement 3 currently scopes around this ("by-name access is
scoped to single-table selects") rather than treating it as a defect, which
was the honest thing to do while shipping but is not where it should stay.

What the names should be

SQLite's rule, as .headers on prints it: the alias if there is one,
otherwise the column name for a bare column reference, otherwise the
expression text. For a compound, the names come from the left-most arm.

That is the behaviour to diff against, and it is fully specified by the oracle
rather than by preference.

Why it matters

Requirement 6's own acceptance list for the embedding API includes a UNION,
so the spec contradicts itself slightly today: the acceptance workload
contains a query shape whose columns cannot be addressed by name.

The SQE consumer's current statements are all single-table, so this is not
blocking them — but it will bite the first join they add, and "use positional
access on joins, names elsewhere" is not a rule an API should ask a consumer
to remember.

Scope

  • result_column_names derives real names for joins and compounds.
  • Qualified forms: SQLite reports x for SELECT a.x, not a.x. Match it.
  • Duplicate names are legal and must stay positional-addressable —
    SELECT a.x, b.x yields two columns both named x, and get_by_name
    should resolve to the first, as sqlite3_column_name does.

Non-goals

  • PRAGMA full_column_names / short_column_names (legacy, V7).
  • Changing get(index), which is correct today.

Acceptance Criteria

  • Column names for a two-table join, a three-table join, a UNION, a
    UNION ALL and a subquery-in-FROM all match what
    sqlite3 -header prints for the same statement
  • Aliases win over derived names; SELECT a.x AS renamed reports
    renamed
  • SELECT a.x reports x, not a.x
  • A compound takes its names from the left-most arm, matching the oracle
  • SELECT a.x, b.x yields two columns named x and get_by_name("x")
    resolves to the first
  • Spec 013 Requirement 3's scoping note is removed, not merely amended
  • tests/unit/api_statement_test.rs::joins_and_compounds_report_positional_column_names
    — which currently asserts the defect — is inverted

Complexity

Estimate: medium
Reasoning: The rule is fully specified by the oracle, so there is no design
question, and a corpus diff over a handful of shapes is the whole test. The
work is that result_column_names sits after the point where a join's
per-source schemas are still distinguishable, so the naming may need to be
derived earlier in the pipeline and threaded through rather than recovered
afterwards.

Refs: 013/Req-3, 013/Req-6, #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