Skip to content

Commit 662f17f

Browse files
committed
Moved to own files
1 parent 923ca86 commit 662f17f

13 files changed

Lines changed: 163 additions & 139 deletions

Sources/Compiler/Columns.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//
2+
// Columns.swift
3+
// Feather
4+
//
5+
// Created by Wes Wickwire on 6/2/25.
6+
//
7+
8+
import OrderedCollections
9+
10+
public typealias Columns = OrderedDictionary<Substring, Type>
11+
12+
extension Columns {
13+
/// Initializes the columns with their default names that SQLite gives to them.
14+
init(withDefaultNames types: [Type]) {
15+
self = types.enumerated()
16+
.reduce(into: [:]) { c, v in c["column\(v.offset + 1)"] = v.element }
17+
}
18+
}

Sources/Compiler/Environment.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
/// type checked against as well as any other static analysis.
1010
struct Environment {
1111
/// Any tables that are imported into the environment.
12-
var importedTables: DuplicateDictionary<QualifiedTableName, ImportedTable> = [:]
12+
var importedTables: DuplicateDictionary<QualifiedName, ImportedTable> = [:]
1313
/// Detached values are all of the columns but detached from under their
1414
/// table. Also any defined column that is inserted without a table is
1515
/// in here as well. As well as the table themselves which allows
@@ -296,18 +296,18 @@ extension Environment {
296296
/// precedence orders to make sure the correc table is returned.
297297
private func resolveImported(table: Substring) -> LookupResult<ImportedTable> {
298298
// No schema means it was a CTE or aliased subquery which should take precedence
299-
let noSchemaEntries = importedTables[QualifiedTableName(name: table, schema: nil)]
299+
let noSchemaEntries = importedTables[QualifiedName(name: table, schema: nil)]
300300
if let importedTable = noSchemaEntries.first {
301301
return LookupResult(importedTable, isAmbiguous: noSchemaEntries.count > 1)
302302
}
303303

304304
// SQLite actually lets `temp` take precedence over `main` so we need to check it first.
305-
let tempEntries = importedTables[QualifiedTableName(name: table, schema: .temp)]
305+
let tempEntries = importedTables[QualifiedName(name: table, schema: .temp)]
306306
if let importedTable = tempEntries.first {
307307
return LookupResult(importedTable, isAmbiguous: tempEntries.count > 1)
308308
}
309309

310-
let mainEntries = importedTables[QualifiedTableName(name: table, schema: .main)]
310+
let mainEntries = importedTables[QualifiedName(name: table, schema: .main)]
311311
if let importedTable = mainEntries.first {
312312
return LookupResult(importedTable, isAmbiguous: mainEntries.count > 1)
313313
}
@@ -327,7 +327,7 @@ extension Environment {
327327
return .schemaDoesNotExist(schema)
328328
}
329329

330-
let qualifiedName = QualifiedTableName(name: table, schema: schemaName)
330+
let qualifiedName = QualifiedName(name: table, schema: schemaName)
331331
let entries = importedTables[qualifiedName]
332332

333333
guard let importedTable = entries.first else {

Sources/Compiler/Index.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
//
2+
// Index.swift
3+
// Feather
4+
//
5+
// Created by Wes Wickwire on 6/2/25.
6+
//
7+
8+
/// An index created within the schema
9+
public struct Index {
10+
/// The name given too the index
11+
public let name: QualifiedName
12+
/// The name of the table the index was created for.
13+
public let table: QualifiedName
14+
}
Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
//
2-
// QualifiedTableName.swift
2+
// QualifiedName.swift
33
// Feather
44
//
55
// Created by Wes Wickwire on 6/2/25.
66
//
77

8-
public struct QualifiedTableName: Hashable, Sendable, CustomStringConvertible {
8+
/// A fully qualified name. This can be used for tables, indices or anything
9+
/// that is `schema.name` based.
10+
public struct QualifiedName: Hashable, Sendable, CustomStringConvertible {
911
/// The non qualified name.
1012
public let name: Substring
1113
/// The schema it exists in if any.

Sources/Compiler/Schema.swift

Lines changed: 6 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -8,104 +8,22 @@
88
import OrderedCollections
99

1010
public struct Schema {
11-
public var tables: OrderedDictionary<QualifiedTableName, Table> = [:]
12-
public var triggers: OrderedDictionary<QualifiedTableName, Trigger> = [:]
13-
public var indices: OrderedDictionary<QualifiedTableName, Index> = [:]
11+
public var tables: OrderedDictionary<QualifiedName, Table> = [:]
12+
public var triggers: OrderedDictionary<QualifiedName, Trigger> = [:]
13+
public var indices: OrderedDictionary<QualifiedName, Index> = [:]
1414

15-
public subscript(tableName: QualifiedTableName) -> Table? {
15+
public subscript(tableName: QualifiedName) -> Table? {
1616
_read { yield tables[tableName] }
1717
_modify { yield &tables[tableName] }
1818
}
1919

20-
public subscript(trigger triggerName: QualifiedTableName) -> Trigger? {
20+
public subscript(trigger triggerName: QualifiedName) -> Trigger? {
2121
_read { yield triggers[triggerName] }
2222
_modify { yield &triggers[triggerName] }
2323
}
2424

25-
public subscript(index indexName: QualifiedTableName) -> Index? {
25+
public subscript(index indexName: QualifiedName) -> Index? {
2626
_read { yield indices[indexName] }
2727
_modify { yield &indices[indexName] }
2828
}
2929
}
30-
31-
// TODO: An ordered dictionary may not be the best representation of the
32-
// TODO: columns. Since this is used even in selects, the user could
33-
// TODO: technically do `SELECT foo, foo FROM bar;` which have the same
34-
// TODO: name which the ordered dictionary wouldnt catch. Or just error?
35-
public typealias Columns = OrderedDictionary<Substring, Type>
36-
37-
extension Columns {
38-
/// Initializes the columns with their default names that SQLite gives to them.
39-
init(withDefaultNames types: [Type]) {
40-
self = types.enumerated()
41-
.reduce(into: [:]) { c, v in c["column\(v.offset + 1)"] = v.element }
42-
}
43-
}
44-
45-
/// A table within the database schema
46-
public struct Table: Sendable, Equatable {
47-
/// The name of the table
48-
public var name: QualifiedTableName
49-
/// The columns of the table
50-
public var columns: Columns
51-
/// The columns that make up the primary key
52-
public let primaryKey: [Substring]
53-
/// What kind of table it is (FTS/CTE...)
54-
public let kind: Kind
55-
56-
public enum Kind: Sendable {
57-
case normal
58-
case view
59-
case fts5
60-
case cte
61-
case subquery
62-
}
63-
64-
var type: Type {
65-
return .row(.fixed(columns.map(\.value)))
66-
}
67-
68-
/// A table to be returned incase of an error in type checking
69-
static let error = Table(
70-
name: QualifiedTableName(name: "<<error>>", schema: nil),
71-
columns: [:],
72-
primaryKey: [],
73-
kind: .normal
74-
)
75-
76-
/// The table but with the name of the alias.
77-
/// Used in `FROM foo AS bar`
78-
func aliased(to alias: Substring) -> Table {
79-
var copy = self
80-
// Alias erases schema on purpose.
81-
// main.foo AS bar does not equal main.bar
82-
copy.name = QualifiedTableName(name: alias, schema: nil)
83-
return copy
84-
}
85-
86-
/// Function to map over the column types and perform any
87-
/// transformations needed
88-
func mapTypes(_ transform: (Type) -> Type) -> Table {
89-
var copy = self
90-
copy.columns = columns.mapValues(transform)
91-
return copy
92-
}
93-
}
94-
95-
/// A trigger to be run on certain SQL operations
96-
public struct Trigger {
97-
/// The name of the trigger
98-
public let name: QualifiedTableName
99-
/// The table the trigger is watching
100-
public let targetTable: QualifiedTableName
101-
/// Any table accessed in the `BEGIN/END`
102-
public let usedTables: Set<Substring>
103-
}
104-
105-
/// An index created within the schema
106-
public struct Index {
107-
/// The name given too the index
108-
public let name: QualifiedTableName
109-
/// The name of the table the index was created for.
110-
public let table: QualifiedTableName
111-
}

Sources/Compiler/Sema/CardinalityInferrer.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ struct CardinalityInferrer {
3535
private func qualifiedName(
3636
for name: IdentifierSyntax,
3737
in schema: IdentifierSyntax?
38-
) -> QualifiedTableName {
39-
return QualifiedTableName(name: name.value, schema: schema?.value)
38+
) -> QualifiedName {
39+
return QualifiedName(name: name.value, schema: schema?.value)
4040
}
4141
}
4242

Sources/Compiler/Sema/StmtTypeChecker.swift

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ struct StmtTypeChecker {
121121

122122
/// Initializes a `QualifiedName` and emits any diagnostics on a failure.
123123
/// If the schema does not exists `nil` will be returned.
124-
private mutating func qualifedName(for name: TableNameSyntax) -> QualifiedTableName {
124+
private mutating func qualifedName(for name: TableNameSyntax) -> QualifiedName {
125125
return qualifedName(for: name.name, in: name.schema)
126126
}
127127

@@ -131,9 +131,9 @@ struct StmtTypeChecker {
131131
for name: IdentifierSyntax,
132132
in schema: IdentifierSyntax?,
133133
isTemp: Bool = false
134-
) -> QualifiedTableName {
134+
) -> QualifiedName {
135135
guard let schema else {
136-
return QualifiedTableName(name: name.value, schema: isTemp ? .temp : .main)
136+
return QualifiedName(name: name.value, schema: isTemp ? .temp : .main)
137137
}
138138

139139
if isTemp {
@@ -142,10 +142,10 @@ struct StmtTypeChecker {
142142

143143
guard let schemaName = SchemaName(schema.value) else {
144144
diagnostics.add(.init("Schema '\(schema)' does not exist", at: schema.location))
145-
return QualifiedTableName(name: name.value, schema: nil)
145+
return QualifiedName(name: name.value, schema: nil)
146146
}
147147

148-
return QualifiedTableName(name: name.value, schema: schemaName)
148+
return QualifiedName(name: name.value, schema: schemaName)
149149
}
150150

151151
private mutating func value<Value>(
@@ -682,7 +682,7 @@ extension StmtTypeChecker {
682682
}
683683

684684
return Table(
685-
name: QualifiedTableName(name: cte.table.value, schema: nil),
685+
name: QualifiedName(name: cte.table.value, schema: nil),
686686
columns: columns,
687687
primaryKey: [],
688688
kind: .cte
@@ -925,7 +925,7 @@ extension StmtTypeChecker {
925925
// Insert the result of the subquery into the environment
926926
if let alias {
927927
let table = Table(
928-
name: QualifiedTableName(name: alias.identifier.value, schema: nil),
928+
name: QualifiedName(name: alias.identifier.value, schema: nil),
929929
columns: resultColumns.allColumns,
930930
primaryKey: [],
931931
kind: .subquery
@@ -1050,7 +1050,7 @@ extension StmtTypeChecker {
10501050
schema[tableName] = nil
10511051

10521052
// Update name, `table` will be inserted at end of function
1053-
tableName = QualifiedTableName(name: newTableName.value, schema: tableName.schema)
1053+
tableName = QualifiedName(name: newTableName.value, schema: tableName.schema)
10541054
table.name = tableName
10551055
case let .renameColumn(oldName, newName):
10561056
table.columns = table.columns.reduce(into: [:]) { newColumns, column in
@@ -1131,7 +1131,7 @@ extension StmtTypeChecker {
11311131
diagnostics.add(.columnDoesNotExist(foreignColumn))
11321132
}
11331133
}
1134-
} else if let table = schema[QualifiedTableName(name: fk.foreignTable.value, schema: .main)] {
1134+
} else if let table = schema[QualifiedName(name: fk.foreignTable.value, schema: .main)] {
11351135
for foreignColumn in fk.foreignColumns {
11361136
if table.columns[foreignColumn.value] == nil {
11371137
diagnostics.add(.columnDoesNotExist(foreignColumn))
@@ -1267,7 +1267,7 @@ extension StmtTypeChecker {
12671267
diagnostics.add(.columnDoesNotExist(column))
12681268
}
12691269

1270-
let foreignTable = QualifiedTableName(name: fkClause.foreignTable.value, schema: .main)
1270+
let foreignTable = QualifiedName(name: fkClause.foreignTable.value, schema: .main)
12711271

12721272
// Make sure referenced table exists
12731273
guard let foreignTable = schema[foreignTable] else {

Sources/Compiler/Table.swift

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
//
2+
// Table.swift
3+
// Feather
4+
//
5+
// Created by Wes Wickwire on 6/2/25.
6+
//
7+
8+
/// A table within the database schema
9+
public struct Table: Sendable, Equatable {
10+
/// The name of the table
11+
public var name: QualifiedName
12+
/// The columns of the table
13+
public var columns: Columns
14+
/// The columns that make up the primary key
15+
public let primaryKey: [Substring]
16+
/// What kind of table it is (FTS/CTE...)
17+
public let kind: Kind
18+
19+
public enum Kind: Sendable {
20+
case normal
21+
case view
22+
case fts5
23+
case cte
24+
case subquery
25+
}
26+
27+
var type: Type {
28+
return .row(.fixed(columns.map(\.value)))
29+
}
30+
31+
/// A table to be returned incase of an error in type checking
32+
static let error = Table(
33+
name: QualifiedName(name: "<<error>>", schema: nil),
34+
columns: [:],
35+
primaryKey: [],
36+
kind: .normal
37+
)
38+
39+
/// The table but with the name of the alias.
40+
/// Used in `FROM foo AS bar`
41+
func aliased(to alias: Substring) -> Table {
42+
var copy = self
43+
// Alias erases schema on purpose.
44+
// main.foo AS bar does not equal main.bar
45+
copy.name = QualifiedName(name: alias, schema: nil)
46+
return copy
47+
}
48+
49+
/// Function to map over the column types and perform any
50+
/// transformations needed
51+
func mapTypes(_ transform: (Type) -> Type) -> Table {
52+
var copy = self
53+
copy.columns = columns.mapValues(transform)
54+
return copy
55+
}
56+
}

Sources/Compiler/Trigger.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
//
2+
// Trigger.swift
3+
// Feather
4+
//
5+
// Created by Wes Wickwire on 6/2/25.
6+
//
7+
8+
/// A trigger to be run on certain SQL operations
9+
public struct Trigger {
10+
/// The name of the trigger
11+
public let name: QualifiedName
12+
/// The table the trigger is watching
13+
public let targetTable: QualifiedName
14+
/// Any table accessed in the `BEGIN/END`
15+
public let usedTables: Set<Substring>
16+
}

Tests/CompilerTests/Check/Check.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ struct CheckEmitter {
287287
is Float, is Double, is String, is Any.Type, is IdentifierSyntax,
288288
is LiteralExprSyntax, is TableOptionsSyntax, is TypeNameSyntax, is BindParameterSyntax,
289289
is OperatorSyntax, is OrderSyntax, is Type, is AliasSyntax, is ColumnExprSyntax.Column,
290-
is QualifiedTableName: true
290+
is QualifiedName: true
291291
case _ as Substring:
292292
true
293293
default: false

0 commit comments

Comments
 (0)