Skip to content

Commit 7e80c55

Browse files
committed
Allow values from parent env to be overwritten
1 parent 8f94422 commit 7e80c55

5 files changed

Lines changed: 70 additions & 6 deletions

File tree

Sources/Compiler/Environment.swift

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,37 +33,64 @@ struct Environment {
3333
/// during a query. However if they do a `SELECT *` it should
3434
/// not be included into the result columns
3535
let explicitAccessOnly: Bool
36+
/// Whether or not this value is inherited from a parent environment.
37+
/// In a subquery we inherit the values from the parent environment
38+
/// but with the caveat that they are overridable and won't be consider
39+
/// ambiguous if something was inserted with the same name.
40+
let isFromParent: Bool
3641
}
3742

3843
init() {}
44+
45+
private init(identifiers: OrderedDictionary<Substring, TypeContainer>) {
46+
self.identifiers = identifiers
47+
}
3948

4049
/// Inserts or updates the type for the given name
4150
mutating func upsert(_ name: Substring, ty: Type, explicitAccessOnly: Bool = false) {
4251
identifiers[name] = TypeContainer(
4352
type: ty,
4453
isAmbiguous: false,
45-
explicitAccessOnly: explicitAccessOnly
54+
explicitAccessOnly: explicitAccessOnly,
55+
isFromParent: false
4656
)
4757
}
4858

4959
/// Inserts the type for the given name. If the name
5060
/// already exists it will be marked as ambiguous
5161
mutating func insert(_ name: Substring, ty: Type, explicitAccessOnly: Bool = false) {
52-
if let existing = identifiers[name] {
62+
// If it already exists, mark it as ambiguous except for the case
63+
// where its from a parent environment.
64+
if let existing = identifiers[name], !existing.isFromParent {
5365
identifiers[name] = TypeContainer(
5466
type: existing.type,
5567
isAmbiguous: true,
56-
explicitAccessOnly: explicitAccessOnly
68+
explicitAccessOnly: explicitAccessOnly,
69+
isFromParent: false
5770
)
5871
} else {
5972
identifiers[name] = TypeContainer(
6073
type: ty,
6174
isAmbiguous: false,
62-
explicitAccessOnly: explicitAccessOnly
75+
explicitAccessOnly: explicitAccessOnly,
76+
isFromParent: false
6377
)
6478
}
6579
}
6680

81+
/// Returns a copy of `self` but with all identifiers in the environment
82+
/// as overridable since they are from a parent environment.
83+
func asParent() -> Environment {
84+
return Environment(identifiers: identifiers.mapValues { container in
85+
TypeContainer(
86+
type: container.type,
87+
isAmbiguous: container.isAmbiguous,
88+
explicitAccessOnly: container.explicitAccessOnly,
89+
isFromParent: true
90+
)
91+
})
92+
}
93+
6794
mutating func rename(_ key: Substring, to newValue: Substring) {
6895
guard let value = identifiers[key] else { return }
6996
identifiers[key] = nil

Sources/Compiler/Sema/Builtins.swift

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,20 @@ enum Builtins {
2525
static let regexp = TypeScheme(typeVariables: [], type: .fn(params: [.text, .text], ret: .integer))
2626
static let glob = TypeScheme(typeVariables: [], type: .fn(params: [.text, .text], ret: .integer))
2727

28-
static let functions: OrderedDictionary<Substring, TypeScheme> = [
28+
static let functions: OrderedDictionary<Substring, TypeScheme> = {
29+
// TODO: Clean this up. SQLite isnt casing dependant but we are at the moment.
30+
// TODO: So insert each function twice, under the lower and upper cased name.
31+
32+
var functions = baseFunctions
33+
34+
for (name, fn) in baseFunctions {
35+
functions[name.uppercased()[...]] = fn
36+
}
37+
38+
return functions
39+
}()
40+
41+
private static let baseFunctions: OrderedDictionary<Substring, TypeScheme> = [
2942
// Scalar functions
3043
"abs": TypeScheme(typeVariables: [.integer(0)], type: .fn(params: [.var(.integer(0))], ret: .var(.integer(0)))),
3144
"changes": TypeScheme(typeVariables: [], type: .fn(params: [], ret: .integer)),

Sources/Compiler/Sema/ExprTypeChecker.swift

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ struct ExprTypeChecker {
1818
private let schema: Schema
1919
/// Any diagnostics that are emitted during compilation
2020
private(set) var diagnostics = Diagnostics()
21+
/// Any table that is used
22+
private(set) var usedTableNames: Set<Substring> = []
2123

2224
private let pragmas: FeatherPragmas
2325

@@ -245,7 +247,12 @@ extension ExprTypeChecker: ExprSyntaxVisitor {
245247
}
246248

247249
mutating func visit(_ expr: borrowing SelectExprSyntax) -> Type {
248-
var typeChecker = StmtTypeChecker(env: env, schema: schema, inferenceState: inferenceState, pragmas: pragmas)
250+
var typeChecker = StmtTypeChecker(
251+
env: env.asParent(),
252+
schema: schema,
253+
inferenceState: inferenceState,
254+
pragmas: pragmas
255+
)
249256
let signature = typeChecker.signature(for: expr.select)
250257
let type: Type = .row(.named(signature.output.allColumns))
251258
// Make sure to update our inference state
@@ -255,6 +262,7 @@ extension ExprTypeChecker: ExprSyntaxVisitor {
255262
diagnostics.merge(typeChecker.diagnostics)
256263
// Record the result type in the state
257264
inferenceState.record(type: type, for: expr)
265+
usedTableNames = typeChecker.usedTableNames
258266
return type
259267
}
260268

Sources/Compiler/Sema/StmtTypeChecker.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ struct StmtTypeChecker {
6868
diagnostics.merge(exprTypeChecker.diagnostics)
6969
// Collect the updated inference state.
7070
inferenceState = exprTypeChecker.inferenceState
71+
usedTableNames.formUnion(exprTypeChecker.usedTableNames)
7172
return (type, name)
7273
}
7374

@@ -287,6 +288,7 @@ extension StmtTypeChecker: StmtSyntaxVisitor {
287288
}
288289

289290
for statement in stmt.statements {
291+
// Extending the current environment to include new/old
290292
_ = inNewEnvironment(extendCurrentEnv: true) { typeChecker in
291293
statement.accept(visitor: &typeChecker)
292294
}

Tests/CompilerTests/Compiler/CompileSimpleSelects.sql

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,3 +194,17 @@ SELECT id,
194194
(bar.id) + (1 - 12) * (unixepoch()) AS secondBestValue,
195195
(bar.id) + (bar.id + 1) AS thirdBestValue
196196
FROM bar;
197+
198+
-- CHECK: SIGNATURE
199+
-- CHECK: OUTPUT_CHUNKS
200+
-- CHECK: CHUNK
201+
-- CHECK: OUTPUT
202+
-- CHECK: id INTEGER
203+
-- CHECK: bar (INTEGER AS Bool)?
204+
-- CHECK: baz TEXT
205+
-- CHECK: OUTPUT_TABLE foo
206+
-- CHECK: TABLES
207+
-- CHECK: bar
208+
-- CHECK: foo
209+
SELECT * FROM foo
210+
WHERE id IN (SELECT qux FROM bar WHERE qux > foo.id);

0 commit comments

Comments
 (0)