Skip to content

fix(jdbc): resolve table metadata for aliases, joins, subqueries, RETURNING, and views - #70

Merged
magnusp merged 5 commits into
mainfrom
fix/37-table-metadata
Oct 21, 2026
Merged

magnusp merged 5 commits into
mainfrom
fix/37-table-metadata

Conversation

@magnusp

@magnusp magnusp commented Oct 21, 2026

Copy link
Copy Markdown
Owner

Closes #37. Part of #54.

What changed

  • Replaced the single-FROM parser in SqlTableReference with a resolver that
    returns an origin table for every result column position. It handles simple
    selects, aliases, explicit and comma joins, FROM subqueries, scalar
    subqueries, CTEs, the leftmost branch of a compound SELECT, INSERT /
    UPDATE / DELETE ... RETURNING, and views.
  • Views resolve through their definition to the base table that supplies each
    column, including nested views and explicit view column lists.
  • LibsqlResultSetMetaData now keeps a per-column origin table instead of one
    table context. getTableName, isNullable, and isAutoIncrement read that
    column's origin and load each distinct table or view once.
  • Table definitions are loaded in pragma_table_info column order so *
    expansion and view columns keep their declared order.
  • Documented the supported and unsupported shapes in the SqlTableReference
    class Javadoc.
  • Added behavior tests per shape: alias, join, subquery, RETURNING (including
    an aliased RETURNING column), and view. Pinned the unsupported expression and
    literal shapes to an empty table name and columnNullableUnknown.
  • Split the shape coverage into one commit per shape.

Contract decision

Match xerial 3.53.4.0. getTableName reports the origin table that SQLite's
sqlite3_column_table_name would report, so aliases, joins, subqueries,
RETURNING, and views all keep their base table. A view reports its base table,
not the view name. Nullability follows xerial's rule: look up the result label
in the origin table and report columnNoNulls / columnNullable; an alias
whose label is not a column of the origin table is columnNullable.

getTableName no longer requires the output label to be a column of the
resolved table. That requirement was why select a as z from w returned "".

Evidence

Failing before the fix (new tests on unmodified origin/main):

$ ./mvnw -pl libsql-jdbc -am -Dtest=LibsqlResultSetMetaDataTest \
    -Dsurefire.failIfNoSpecifiedTests=false test
[ERROR] Tests run: 22, Failures: 6, Errors: 0, Skipped: 0
[ERROR]   resolvesAliasTableNameAndNullability:333 expected: "w" but was: ""
[ERROR]   resolvesAliasedReturningTableNameAndNullability:381 expected: "w" but was: ""
[ERROR]   resolvesJoinTableNamesAndNullability:344 expected: "w" but was: ""
[ERROR]   resolvesReturningTableNameAndNullability:368 expected: "w" but was: ""
[ERROR]   resolvesSubqueryTableNameAndNullability:357 expected: "w" but was: ""
[ERROR]   resolvesViewTableNameAndNullability:392 expected: "w" but was: "v"

Passing after the fix:

$ ./mvnw -pl libsql-jdbc -am test
Tests run: 103, Failures: 0, Errors: 0, Skipped: 0
BUILD SUCCESS
$ ./mvnw -pl libsql-conformance -am verify
Tests run: 117, Failures: 0, Errors: 0, Skipped: 1
BUILD SUCCESS

Includes the vendored xerial RSMetaDataTest (13 tests) against live sqld.

$ DOCKER_CONFIG=/tmp/docker-config ./mvnw -pl libsql-itests -am verify
Tests run: 18, Failures: 0, Errors: 0, Skipped: 0
BUILD SUCCESS

I also ran a temporary live-sqld probe (removed before committing) asserting
getTableName and isNullable for alias, join, subquery, CTE, UNION,
RETURNING, view, aliased view column, and expression shapes. All matched the
expected xerial values.

$ ./mvnw spotless:apply
BUILD SUCCESS (no formatting changes)

Deviations

Supported shapes match xerial 3.53.4.0.

For unsupported shapes (expressions and literals) this driver reports
getTableName == "" and isNullable == columnNullableUnknown. xerial reports
the same empty table name but columnNullable for those columns because it
treats missing SQLite column metadata as nullable. The issue explicitly asks
unsupported shapes to return documented empty or unknown values, and an
expression has no table column whose nullability could be known, so this PR
keeps the honest unknown value.

Resolve the origin table of each result column from the statement shape
instead of assuming a single simple FROM target. Aliases keep the origin
table and follow xerial's nullability rule: a label that is not a column
of the origin table reports columnNullable.

Expressions and literals remain unresolved and report an empty table name
with columnNullableUnknown.
Assert per-column origins and nullability for explicit and comma joins,
source aliases, and star expansion across joined tables.
Assert origins for FROM subqueries, scalar subqueries, common table
expressions, and the leftmost branch of a compound SELECT.
Assert INSERT, UPDATE, and DELETE RETURNING columns resolve to the target
table, including aliased RETURNING columns.
Assert a view reports its base table, including aliased view columns,
matching xerial 3.53.4.0.
@magnusp
magnusp force-pushed the fix/37-table-metadata branch from b1a2e90 to 1f3bf6f Compare October 21, 2026 22:57
@magnusp
magnusp merged commit cb38f7f into main Oct 21, 2026
5 checks passed
@magnusp
magnusp deleted the fix/37-table-metadata branch October 21, 2026 23:01
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.

Table metadata fails for aliases, joins, subqueries, RETURNING, and views

1 participant