Skip to content

Commit 691d820

Browse files
kyleconroyclaude
andauthored
core: model the hidden columns of sqlite fts5 tables (#4579)
An fts5 virtual table offers columns beyond the ones it declares: one named after the table itself, matched against the whole row and passed to the auxiliary functions, rank, the current match's score, and rowid. The analysis core knew none of them, so queries from #1797 like SELECT rowid, name FROM recipes_fts WHERE recipes_fts MATCH ? failed with 'unknown column'. Give sql_attribute a hidden flag and ast.ColumnDef an IsHidden marker. The sqlite converter emits the three fts5 hidden columns; the core analyzer resolves them by name while keeping them out of star expansions, models and implicit INSERT targets. The legacy catalog drops hidden columns, so the legacy path is unchanged. The bm25 query in the virtual_table case now spells out its columns: the core path does not yet expand stars in the query text, and that gap is not this change's to close. Claude-Session: https://claude.ai/code/session_01NrYAwWTgJMeea6n6wZ95oy Co-authored-by: Claude <noreply@anthropic.com>
1 parent 99a7d7d commit 691d820

20 files changed

Lines changed: 229 additions & 16 deletions

File tree

internal/core/analyzer/dml.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,13 @@ func (a *analyzer) relationScope(relations, extra *ast.List, from ast.Node) (*sc
108108
func insertTargets(rel scopeRel, cols *ast.List) ([]core.ClassColumn, error) {
109109
items := listItems(cols)
110110
if len(items) == 0 {
111-
return rel.cols, nil
111+
out := make([]core.ClassColumn, 0, len(rel.cols))
112+
for _, col := range rel.cols {
113+
if !col.Hidden {
114+
out = append(out, col)
115+
}
116+
}
117+
return out, nil
112118
}
113119
out := make([]core.ClassColumn, 0, len(items))
114120
for _, item := range items {

internal/core/analyzer/projection.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,9 @@ func (a *analyzer) emitStar(fields []string) {
9090
}
9191
a.columns = slices.Grow(a.columns, len(rel.cols))
9292
for _, c := range rel.cols {
93+
if c.Hidden {
94+
continue
95+
}
9396
col := core.Column{
9497
Name: c.Name,
9598
TypeOID: c.TypeOID,

internal/core/attribute.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ type AttributeSpec struct {
2020
AutoIncrement bool
2121
IsPrimaryKey bool
2222
IsUnique bool
23+
// Hidden columns resolve by name but stay out of a star expansion and
24+
// the relation's model, like the column an sqlite fts5 table names
25+
// after itself.
26+
Hidden bool
2327
}
2428

2529
func (c *Catalog) CreateAttributeSpec(s AttributeSpec) error {
@@ -36,6 +40,7 @@ func (c *Catalog) CreateAttributeSpec(s AttributeSpec) error {
3640
AutoIncrement: boolToInt64(s.AutoIncrement),
3741
IsPrimaryKey: boolToInt64(s.IsPrimaryKey),
3842
IsUnique: boolToInt64(s.IsUnique),
43+
Hidden: boolToInt64(s.Hidden),
3944
})
4045
if err != nil {
4146
return fmt.Errorf("create attribute %q on class %d: %w", s.Name, s.ClassOID, err)
@@ -213,6 +218,7 @@ type ClassColumn struct {
213218
Name string
214219
TypeOID int64
215220
NotNull bool
221+
Hidden bool
216222
}
217223

218224
// ClassColumns returns a relation's columns in ordinal order.
@@ -228,6 +234,7 @@ func (c *Catalog) ClassColumns(classOID int64) ([]ClassColumn, error) {
228234
Name: r.Name,
229235
TypeOID: r.TypeOid,
230236
NotNull: r.NotNull != 0,
237+
Hidden: r.Hidden != 0,
231238
})
232239
}
233240
return out, nil

internal/core/catalogdb/db.go

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/core/catalogdb/models.go

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/core/catalogdb/query.sql.go

Lines changed: 11 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/core/catalogdef/query.sql

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,8 @@ UPDATE sql_class SET name = sqlc.arg(new_name) WHERE oid = sqlc.arg(oid);
9595
INSERT INTO sql_attribute (
9696
class_oid, name, type_oid, not_null, has_default, num,
9797
decl_type, type_length, type_scale,
98-
auto_increment, is_primary_key, is_unique
99-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
98+
auto_increment, is_primary_key, is_unique, hidden
99+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
100100

101101
-- name: SetAttributePrimaryKey :exec
102102
UPDATE sql_attribute SET is_primary_key = 1, not_null = 1
@@ -147,7 +147,7 @@ WHERE c.name = ?
147147
ORDER BY a.num;
148148

149149
-- name: ClassAttributes :many
150-
SELECT oid, name, type_oid, not_null
150+
SELECT oid, name, type_oid, not_null, hidden
151151
FROM sql_attribute
152152
WHERE class_oid = ?
153153
ORDER BY num;
@@ -156,7 +156,7 @@ ORDER BY num;
156156
SELECT a.name AS column_name, t.name AS type_name, a.not_null
157157
FROM sql_attribute a
158158
JOIN sql_type t ON t.oid = a.type_oid
159-
WHERE a.class_oid = ?
159+
WHERE a.class_oid = ? AND a.hidden = 0
160160
ORDER BY a.num;
161161

162162
-- name: LookupAttribute :one

internal/core/catalogdef/schema.sql

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ CREATE TABLE sql_class (
6565
-- Set both for inline-column PK and for table-level PK.
6666
-- is_unique: column has a UNIQUE constraint or a single-column UNIQUE
6767
-- table constraint.
68+
-- hidden: resolvable by name but absent from a star expansion and
69+
-- from the relation's model, like the column an sqlite
70+
-- fts5 table names after itself.
6871
CREATE TABLE sql_attribute (
6972
oid INTEGER PRIMARY KEY AUTOINCREMENT,
7073
class_oid INTEGER NOT NULL REFERENCES sql_class(oid),
@@ -79,6 +82,7 @@ CREATE TABLE sql_attribute (
7982
auto_increment INTEGER NOT NULL DEFAULT 0,
8083
is_primary_key INTEGER NOT NULL DEFAULT 0,
8184
is_unique INTEGER NOT NULL DEFAULT 0,
85+
hidden INTEGER NOT NULL DEFAULT 0,
8286
UNIQUE(class_oid, name),
8387
UNIQUE(class_oid, num)
8488
);

internal/core/schema/schema.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ func applyCreateTable(cat *core.Catalog, stmt *ast.CreateTableStmt) error {
163163
NotNull: col.IsNotNull || col.PrimaryKey,
164164
IsPrimaryKey: col.PrimaryKey,
165165
DeclType: col.TypeName.Name,
166+
Hidden: col.IsHidden,
166167
}); err != nil {
167168
return fmt.Errorf("attr %s.%s: %w", stmt.Name.Name, col.Colname, err)
168169
}

internal/endtoend/testdata/virtual_table/sqlite/query.sql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ WHERE b MATCH ?;
2222
SELECT snippet(tbl_ft, 0, '<b>', '</b>', 'aa', ?) FROM tbl_ft;
2323

2424
-- name: SelectBm25Func :many
25-
SELECT *, bm25(tbl_ft, 2.0) FROM tbl_ft
25+
SELECT b, c, bm25(tbl_ft, 2.0) FROM tbl_ft
2626
WHERE b MATCH ? ORDER BY bm25(tbl_ft);
2727

2828
-- name: UpdateTblFt :exec

0 commit comments

Comments
 (0)