Skip to content

Commit 82c97aa

Browse files
committed
Support querying by rowid
1 parent 88e7279 commit 82c97aa

11 files changed

Lines changed: 129 additions & 12 deletions

File tree

Sources/Compiler/Environment.swift

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,9 +184,19 @@ struct Environment {
184184
isOptional: Bool,
185185
qualifiedAccessOnly: Bool = false
186186
) {
187+
var additionalColumns: Columns = [:]
188+
189+
if table.kind == .fts5 {
190+
additionalColumns.append(Column(type: .real), for: "rank")
191+
}
192+
193+
if (table.kind == .normal || table.kind == .fts5), !table.isWithoutRowid {
194+
additionalColumns.append(Column(type: .integer), for: "rowid")
195+
}
196+
187197
let importedTable = ImportedTable(
188198
table: isOptional ? table.mapTypes { $0.coerceToOptional() } : table,
189-
additionalColumns: table.kind == .fts5 ? ["rank": Column(type: .real)] : nil,
199+
additionalColumns: additionalColumns.count == 0 ? nil : additionalColumns,
190200
isOptional: isOptional
191201
)
192202

Sources/Compiler/Parse/Parsers.swift

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1250,7 +1250,17 @@ enum Parsers {
12501250
switch state.current.kind {
12511251
case .without:
12521252
state.skip()
1253-
state.skip(.rowid)
1253+
if case let .identifier(value) = state.current.kind,
1254+
value.uppercased() == "ROWID"
1255+
{
1256+
state.skip()
1257+
} else {
1258+
state.diagnostics.add(.unexpectedToken(
1259+
of: state.current.kind,
1260+
expected: .rowid,
1261+
at: state.current.location
1262+
))
1263+
}
12541264
options = options.union(.withoutRowId)
12551265
case .strict:
12561266
state.skip()

Sources/Compiler/Parse/Token.swift

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,6 @@ struct Token {
131131
.right,
132132
.rollback,
133133
.row,
134-
.rowid,
135134
.rows,
136135
.savepoint,
137136
.select,
@@ -166,6 +165,7 @@ struct Token {
166165
static let `true`: Kind = .identifier("TRUE")
167166
static let `false`: Kind = .identifier("FALSE")
168167
static let unindexed: Kind = .identifier("UNINDEXED")
168+
static let rowid: Kind = .identifier("ROWID")
169169

170170
case identifier(Substring)
171171
case string(Substring)
@@ -295,7 +295,6 @@ struct Token {
295295
case right
296296
case rollback
297297
case row
298-
case rowid
299298
case rows
300299
case savepoint
301300
case select
@@ -505,7 +504,6 @@ struct Token {
505504
case .right: "RIGHT"
506505
case .rollback: "ROLLBACK"
507506
case .row: "ROW"
508-
case .rowid: "ROWID"
509507
case .rows: "ROWS"
510508
case .savepoint: "SAVEPOINT"
511509
case .select: "SELECT"

Sources/Compiler/Sema/StmtTypeChecker.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1181,9 +1181,10 @@ extension StmtTypeChecker {
11811181
name: tableName,
11821182
columns: columns,
11831183
primaryKey: primaryKey(of: createTable, columns: columns),
1184-
kind: .normal
1184+
kind: .normal,
1185+
isWithoutRowid: options.kind.contains(.withoutRowId)
11851186
)
1186-
1187+
11871188
if pragmas.contains(.requireStrictTables),
11881189
!options.kind.contains(.strict)
11891190
{

Sources/Compiler/Table.swift

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,25 +15,29 @@ public struct Table: Sendable, Equatable {
1515
public let primaryKey: [Substring]
1616
/// What kind of table it is (FTS/CTE...)
1717
public let kind: Kind
18-
18+
/// Whether the table was declared `WITHOUT ROWID`
19+
public let isWithoutRowid: Bool
20+
1921
public enum Kind: Sendable {
2022
case normal
2123
case view
2224
case fts5
2325
case cte
2426
case subquery
2527
}
26-
28+
2729
init(
2830
name: QualifiedName,
2931
columns: Columns,
3032
primaryKey: [Substring] = [],
31-
kind: Kind
33+
kind: Kind,
34+
isWithoutRowid: Bool = false
3235
) {
3336
self.name = name
3437
self.columns = columns
3538
self.primaryKey = primaryKey
3639
self.kind = kind
40+
self.isWithoutRowid = isWithoutRowid
3741
}
3842

3943
var type: Type {

Tests/CompilerTests/Compiler/CompileFTS5.sql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
-- CHECK-SCHEMA: KEY quz
77
-- CHECK-SCHEMA: VALUE INTEGER
88
-- CHECK-SCHEMA: KIND fts5
9+
-- CHECK-SCHEMA: IS_WITHOUT_ROWID false
910
-- CHECK-ERROR: Missing column type
1011
CREATE VIRTUAL TABLE foo USING fts5 (
1112
bar TEXT,
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
CREATE TABLE foo (id INTEGER PRIMARY KEY, name TEXT NOT NULL);
2+
CREATE TABLE no_rowid (a TEXT NOT NULL, b TEXT NOT NULL, PRIMARY KEY (a)) WITHOUT ROWID;
3+
4+
-- `rowid` is not part of the generated output of `SELECT *`.
5+
-- CHECK: SIGNATURE
6+
-- CHECK: OUTPUT_CHUNKS
7+
-- CHECK: CHUNK
8+
-- CHECK: OUTPUT
9+
-- CHECK: id INTEGER
10+
-- CHECK: name TEXT
11+
-- CHECK: OUTPUT_TABLE foo
12+
-- CHECK: TABLES
13+
-- CHECK: foo
14+
SELECT * FROM foo;
15+
16+
-- `rowid` is available at query time, in both the result and the filter.
17+
-- CHECK: SIGNATURE
18+
-- CHECK: PARAMETERS
19+
-- CHECK: PARAMETER
20+
-- CHECK: TYPE INTEGER
21+
-- CHECK: INDEX 1
22+
-- CHECK: NAME rid
23+
-- CHECK: OUTPUT_CHUNKS
24+
-- CHECK: CHUNK
25+
-- CHECK: OUTPUT
26+
-- CHECK: rowid INTEGER
27+
-- CHECK: name TEXT
28+
-- CHECK: TABLES
29+
-- CHECK: foo
30+
SELECT rowid, name FROM foo WHERE rowid = :rid;
31+
32+
-- WITHOUT ROWID tables have no `rowid` column.
33+
-- CHECK: SIGNATURE
34+
-- CHECK: OUTPUT_CHUNKS
35+
-- CHECK: CHUNK
36+
-- CHECK: OUTPUT
37+
-- CHECK: rowid <<error>>
38+
-- CHECK: TABLES
39+
-- CHECK: no_rowid
40+
-- CHECK-ERROR: Column 'rowid' does not exist
41+
SELECT rowid FROM no_rowid;

Tests/CompilerTests/Compiler/CompileTableSchema.sql

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
-- CHECK: PRIMARY_KEY
1111
-- CHECK: bar
1212
-- CHECK: KIND normal
13+
-- CHECK: IS_WITHOUT_ROWID false
1314
CREATE TABLE foo (
1415
bar INTEGER PRIMARY KEY,
1516
baz TEXT CHECK (baz = 'baz')
@@ -28,6 +29,7 @@ ALTER TABLE foo ADD COLUMN qux TEXT;
2829
-- CHECK: foo
2930
-- CHECK: baz
3031
-- CHECK: KIND normal
32+
-- CHECK: IS_WITHOUT_ROWID false
3133
CREATE TABLE bar (
3234
foo TEXT,
3335
baz TEXT,
@@ -44,6 +46,7 @@ PRAGMA puresql_require_strict_tables = TRUE;
4446
-- CHECK: PRIMARY_KEY
4547
-- CHECK: foo
4648
-- CHECK: KIND normal
49+
-- CHECK: IS_WITHOUT_ROWID false
4750
-- CHECK-ERROR: Invalid type 'DECIMAL'
4851
-- CHECK-ERROR: Column 'bar' does not exist
4952
-- CHECK-ERROR: Missing STRICT table option
@@ -62,6 +65,7 @@ CREATE TABLE baz (
6265
-- CHECK: PRIMARY_KEY
6366
-- CHECK: bar
6467
-- CHECK: KIND normal
68+
-- CHECK: IS_WITHOUT_ROWID false
6569
-- CHECK-ERROR: Table 'qux' already has a primary key
6670
CREATE TABLE qux (
6771
foo TEXT PRIMARY KEY ON CONFLICT REPLACE REFERENCES qux (foo) ON DELETE CASCADE,
@@ -75,6 +79,7 @@ CREATE TABLE qux (
7579
-- CHECK: KEY TABLE
7680
-- CHECK: VALUE KEY?
7781
-- CHECK: KIND normal
82+
-- CHECK: IS_WITHOUT_ROWID false
7883
-- CHECK-ERROR: Invalid type 'KEY'
7984
CREATE TABLE "PRIMARY" (
8085
"TABLE" INTEGER,
@@ -95,6 +100,7 @@ CREATE TABLE "PRIMARY" (
95100
-- CHECK: KEY any
96101
-- CHECK: VALUE ANY?
97102
-- CHECK: KIND normal
103+
-- CHECK: IS_WITHOUT_ROWID false
98104
CREATE TABLE allValidTypes (
99105
int INT,
100106
integer INTEGER,
@@ -115,6 +121,7 @@ CREATE TABLE allValidTypes (
115121
-- CHECK: KEY ref
116122
-- CHECK: VALUE INTEGER?
117123
-- CHECK: KIND normal
124+
-- CHECK: IS_WITHOUT_ROWID false
118125
-- CHECK-ERROR: Column 'qux' does not exist
119126
-- CHECK-ERROR: Table 'dne' does not exist
120127
CREATE TABLE hasGenerated (
@@ -132,6 +139,7 @@ CREATE TABLE hasGenerated (
132139
-- CHECK: KEY bar
133140
-- CHECK: VALUE INTEGER?
134141
-- CHECK: KIND normal
142+
-- CHECK: IS_WITHOUT_ROWID false
135143
-- CHECK-ERROR: Column 'foooooo' does not exist
136144
-- CHECK-ERROR: Column 'typo' does not exist
137145
-- CHECK-ERROR: Table 'doesNotExist' does not exist
@@ -154,6 +162,7 @@ CREATE TABLE hasTableCheck (
154162
-- CHECK: KEY qux
155163
-- CHECK: VALUE TEXT?
156164
-- CHECK: KIND normal
165+
-- CHECK: IS_WITHOUT_ROWID false
157166
CREATE TABLE fromSelect AS SELECT * FROM foo;
158167

159168
CREATE TABLE wontShow (bar INTEGER) STRICT;

Tests/CompilerTests/Compiler/CompileView.sql

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ CREATE TABLE foo (bar INTEGER, baz TEXT);
1010
-- CHECK-SCHEMA: KEY baz
1111
-- CHECK-SCHEMA: VALUE TEXT?
1212
-- CHECK-SCHEMA: KIND view
13+
-- CHECK-SCHEMA: IS_WITHOUT_ROWID false
1314
CREATE VIEW view1 AS SELECT * FROM foo;
1415

1516
-- CHECK-SCHEMA: TABLE
@@ -20,6 +21,7 @@ CREATE VIEW view1 AS SELECT * FROM foo;
2021
-- CHECK-SCHEMA: KEY col2
2122
-- CHECK-SCHEMA: VALUE TEXT?
2223
-- CHECK-SCHEMA: KIND view
24+
-- CHECK-SCHEMA: IS_WITHOUT_ROWID false
2325
CREATE VIEW view2 (col1, col2) AS SELECT * FROM foo;
2426

2527
-- CHECK-SCHEMA: TABLE
@@ -28,6 +30,7 @@ CREATE VIEW view2 (col1, col2) AS SELECT * FROM foo;
2830
-- CHECK-SCHEMA: KEY col1
2931
-- CHECK-SCHEMA: VALUE INTEGER?
3032
-- CHECK-SCHEMA: KIND view
33+
-- CHECK-SCHEMA: IS_WITHOUT_ROWID false
3134
-- CHECK-ERROR: SELECT returns 2 columns but only have 1 names defined
3235
CREATE VIEW view3 (col1) AS SELECT * FROM foo;
3336

Tests/CompilerTests/CompilerTests.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ class CompilerTests: XCTestCase {
4848
try checkQueries(compile: "CompileFTS5", prefix: "CHECK-QUERIES")
4949
}
5050

51+
func testRowid() throws {
52+
try checkQueries(compile: "CompileRowid")
53+
}
54+
5155
func testSpecialNames() throws {
5256
try checkQueries(compile: "CompileSpecialNames")
5357
}

0 commit comments

Comments
 (0)