Skip to content

Commit 75a1bb3

Browse files
Resolve view query sources by lexical scope
1 parent cd9e574 commit 75a1bb3

46 files changed

Lines changed: 350 additions & 2885 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎diesel_cli/src/infer_schema_internals/inference.rs‎

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -394,15 +394,21 @@ pub fn load_view_data(
394394
{
395395
tracing::debug!(view = %name, ?data, "Inferred data");
396396
if data.field_count() == column_data.len() {
397-
for (column_data, is_nullable) in column_data
398-
.iter_mut()
399-
.zip(data.infer_nullability(resolver)?)
400-
{
401-
tracing::debug!(view = %name, field = %column_data.rust_name, ?is_nullable, "Correct field nullablility");
402-
match is_nullable {
403-
IsNull::IsNullable => column_data.ty.is_nullable = true,
404-
IsNull::NotNullable => column_data.ty.is_nullable = false,
405-
IsNull::Unknown => {}
397+
match data.infer_nullability(resolver) {
398+
Ok(nullability) => {
399+
for (column_data, is_nullable) in
400+
column_data.iter_mut().zip(nullability)
401+
{
402+
tracing::debug!(view = %name, field = %column_data.rust_name, ?is_nullable, "Correct field nullablility");
403+
match is_nullable {
404+
IsNull::IsNullable => column_data.ty.is_nullable = true,
405+
IsNull::NotNullable => column_data.ty.is_nullable = false,
406+
IsNull::Unknown => {}
407+
}
408+
}
409+
}
410+
Err(e) => {
411+
tracing::warn!(view = %name, error = %e, "Failed to infer nullability for view fields");
406412
}
407413
}
408414
} else {

‎diesel_cli/tests/print_schema.rs‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -568,6 +568,15 @@ fn print_schema_view_infer_nullable_simple() {
568568
)
569569
}
570570

571+
#[test]
572+
#[cfg(feature = "sqlite")]
573+
fn print_schema_view_infer_nullable_resolution_failure() {
574+
test_print_schema(
575+
"print_schema_view_infer_nullable_resolution_failure",
576+
vec!["--include-views", "--experimental-infer-nullable-for-views"],
577+
);
578+
}
579+
571580
#[test]
572581
fn print_schema_view_infer_nullable_from_table() {
573582
test_print_schema(
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[print_schema]
2+
file = "src/schema.rs"
3+
with_docs = false
4+
include_views = true
5+
experimental_infer_nullable_for_views = true
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
source: diesel_cli/tests/print_schema.rs
3+
description: "Test: print_schema_view_infer_nullable_resolution_failure"
4+
---
5+
// @generated automatically by Diesel CLI.
6+
7+
diesel::view! {
8+
v {
9+
a -> Nullable<Binary>,
10+
}
11+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
CREATE VIEW v AS SELECT s."1 + 2" AS a FROM (SELECT 1 + 2) s;

‎diesel_infer_query/Cargo.toml‎

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,6 @@ autotests = false
1111
sqlparser = { version = "0.63.0", features = ["visitor"] }
1212
thiserror = "2"
1313

14-
[dev-dependencies]
15-
insta = "1.43"
16-
1714
[[test]]
1815
path = "tests/lib.rs"
1916
name = "integration_tests"

‎diesel_infer_query/src/expression.rs‎

Lines changed: 39 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use crate::error::{Error, Result};
66
use crate::query_source::QuerySource;
77
use crate::select::{CaseCondition, Expression};
8+
use crate::views::ParseContext;
89
use sqlparser::ast::{
910
Expr, FunctionArg, FunctionArgExpr, FunctionArguments, ObjectNamePart, Value,
1011
};
@@ -13,6 +14,7 @@ use std::collections::HashMap;
1314
pub(crate) fn infer_expr(
1415
expr: &sqlparser::ast::Expr,
1516
query_source_lookup: &HashMap<Option<&str>, QuerySource>,
17+
context: &mut ParseContext,
1618
) -> Result<Expression> {
1719
match expr {
1820
Expr::Value(v) => Ok(Expression::Literal {
@@ -29,6 +31,7 @@ pub(crate) fn infer_expr(
2931
schema: table.schema.map(|s| s.to_owned()),
3032
query_source: table.name.map(|s| s.to_owned()),
3133
field_name: id.value.clone(),
34+
source_id: table.source_id,
3235
// no joins here so we should be fine
3336
via_left_join: false,
3437
})
@@ -53,6 +56,7 @@ pub(crate) fn infer_expr(
5356
schema: table.schema.map(|s| s.to_owned()),
5457
query_source: table.name.map(|s| s.to_owned()),
5558
field_name: field.value.clone(),
59+
source_id: table.source_id,
5660
via_left_join,
5761
})
5862
}
@@ -65,15 +69,15 @@ pub(crate) fn infer_expr(
6569
Expr::Cast {
6670
expr, data_type, ..
6771
} => {
68-
let inner = infer_expr(expr, query_source_lookup)?;
72+
let inner = infer_expr(expr, query_source_lookup, context)?;
6973
Ok(Expression::Cast {
7074
inner: Box::new(inner),
7175
tpe: data_type.to_string(),
7276
})
7377
}
7478
Expr::BinaryOp { left, op, right } => {
75-
let left = Box::new(infer_expr(left, query_source_lookup)?);
76-
let right = Box::new(infer_expr(right, query_source_lookup)?);
79+
let left = Box::new(infer_expr(left, query_source_lookup, context)?);
80+
let right = Box::new(infer_expr(right, query_source_lookup, context)?);
7781
Ok(Expression::BinaryOp {
7882
left,
7983
right,
@@ -82,22 +86,22 @@ pub(crate) fn infer_expr(
8286
})
8387
}
8488
Expr::IsNull(e) => {
85-
let inner = Box::new(infer_expr(e, query_source_lookup)?);
89+
let inner = Box::new(infer_expr(e, query_source_lookup, context)?);
8690
Ok(Expression::PostfixOp {
8791
expr: inner,
8892
op: String::from("IS NULL"),
8993
statically_not_null: true,
9094
})
9195
}
9296
Expr::IsNotNull(e) => {
93-
let inner = Box::new(infer_expr(e, query_source_lookup)?);
97+
let inner = Box::new(infer_expr(e, query_source_lookup, context)?);
9498
Ok(Expression::PostfixOp {
9599
expr: inner,
96100
op: String::from("IS NOT NULL"),
97101
statically_not_null: true,
98102
})
99103
}
100-
Expr::Function(f) => infer_functions(f, query_source_lookup),
104+
Expr::Function(f) => infer_functions(f, query_source_lookup, context),
101105
Expr::Like {
102106
negated,
103107
any,
@@ -107,8 +111,8 @@ pub(crate) fn infer_expr(
107111
} if !*any && escape_char.is_none() => {
108112
let op = if *negated { "NOT LIKE" } else { "LIKE" };
109113
Ok(Expression::BinaryOp {
110-
left: Box::new(infer_expr(expr, query_source_lookup)?),
111-
right: Box::new(infer_expr(pattern, query_source_lookup)?),
114+
left: Box::new(infer_expr(expr, query_source_lookup, context)?),
115+
right: Box::new(infer_expr(pattern, query_source_lookup, context)?),
112116
op: String::from(op),
113117
statically_not_null: false,
114118
})
@@ -122,21 +126,21 @@ pub(crate) fn infer_expr(
122126
} if !*any && escape_char.is_none() => {
123127
let op = if *negated { "NOT ILIKE" } else { "ILIKE" };
124128
Ok(Expression::BinaryOp {
125-
left: Box::new(infer_expr(expr, query_source_lookup)?),
126-
right: Box::new(infer_expr(pattern, query_source_lookup)?),
129+
left: Box::new(infer_expr(expr, query_source_lookup, context)?),
130+
right: Box::new(infer_expr(pattern, query_source_lookup, context)?),
127131
op: String::from(op),
128132
statically_not_null: false,
129133
})
130134
}
131135
Expr::IsDistinctFrom(a, b) => Ok(Expression::BinaryOp {
132-
left: Box::new(infer_expr(a, query_source_lookup)?),
133-
right: Box::new(infer_expr(b, query_source_lookup)?),
136+
left: Box::new(infer_expr(a, query_source_lookup, context)?),
137+
right: Box::new(infer_expr(b, query_source_lookup, context)?),
134138
op: String::from("IS DISTINCT FROM"),
135139
statically_not_null: true,
136140
}),
137141
Expr::IsNotDistinctFrom(a, b) => Ok(Expression::BinaryOp {
138-
left: Box::new(infer_expr(a, query_source_lookup)?),
139-
right: Box::new(infer_expr(b, query_source_lookup)?),
142+
left: Box::new(infer_expr(a, query_source_lookup, context)?),
143+
right: Box::new(infer_expr(b, query_source_lookup, context)?),
140144
op: String::from("IS NOT DISTINCT FROM"),
141145
statically_not_null: true,
142146
}),
@@ -146,10 +150,10 @@ pub(crate) fn infer_expr(
146150
low,
147151
high,
148152
} => Ok(Expression::Between {
149-
left: Box::new(infer_expr(expr, query_source_lookup)?),
153+
left: Box::new(infer_expr(expr, query_source_lookup, context)?),
150154
negated: *negated,
151-
low: Box::new(infer_expr(low, query_source_lookup)?),
152-
high: Box::new(infer_expr(high, query_source_lookup)?),
155+
low: Box::new(infer_expr(low, query_source_lookup, context)?),
156+
high: Box::new(infer_expr(high, query_source_lookup, context)?),
153157
}),
154158
Expr::SimilarTo {
155159
negated,
@@ -163,8 +167,8 @@ pub(crate) fn infer_expr(
163167
"SIMILAR TO"
164168
};
165169
Ok(Expression::BinaryOp {
166-
left: Box::new(infer_expr(expr, query_source_lookup)?),
167-
right: Box::new(infer_expr(pattern, query_source_lookup)?),
170+
left: Box::new(infer_expr(expr, query_source_lookup, context)?),
171+
right: Box::new(infer_expr(pattern, query_source_lookup, context)?),
168172
op: String::from(op),
169173
statically_not_null: false,
170174
})
@@ -183,8 +187,8 @@ pub(crate) fn infer_expr(
183187
"RLIKE"
184188
};
185189
Ok(Expression::BinaryOp {
186-
left: Box::new(infer_expr(expr, query_source_lookup)?),
187-
right: Box::new(infer_expr(pattern, query_source_lookup)?),
190+
left: Box::new(infer_expr(expr, query_source_lookup, context)?),
191+
right: Box::new(infer_expr(pattern, query_source_lookup, context)?),
188192
op: String::from(op),
189193
statically_not_null: false,
190194
})
@@ -197,19 +201,19 @@ pub(crate) fn infer_expr(
197201
} => {
198202
let operand = operand
199203
.as_ref()
200-
.map(|o| infer_expr(o, query_source_lookup))
204+
.map(|o| infer_expr(o, query_source_lookup, context))
201205
.transpose()?
202206
.map(Box::new);
203207
let else_clause = else_result
204208
.as_ref()
205-
.map(|e| infer_expr(e, query_source_lookup))
209+
.map(|e| infer_expr(e, query_source_lookup, context))
206210
.transpose()?
207211
.map(Box::new);
208212
let conditions = conditions
209213
.iter()
210214
.map(|c| -> std::result::Result<_, _> {
211-
let condition = infer_expr(&c.condition, query_source_lookup)?;
212-
let result = infer_expr(&c.result, query_source_lookup)?;
215+
let condition = infer_expr(&c.condition, query_source_lookup, context)?;
216+
let result = infer_expr(&c.result, query_source_lookup, context)?;
213217
Ok(CaseCondition { condition, result })
214218
})
215219
.collect::<Result<Vec<_>>>()?;
@@ -224,28 +228,28 @@ pub(crate) fn infer_expr(
224228
list,
225229
negated,
226230
} => Ok(Expression::In {
227-
left: Box::new(infer_expr(expr, query_source_lookup)?),
231+
left: Box::new(infer_expr(expr, query_source_lookup, context)?),
228232
negated: *negated,
229233
list: list
230234
.iter()
231-
.map(|e| infer_expr(e, query_source_lookup))
235+
.map(|e| infer_expr(e, query_source_lookup, context))
232236
.collect::<Result<Vec<_>, _>>()?,
233237
}),
234-
Expr::Nested(n) => infer_expr(n, query_source_lookup)
238+
Expr::Nested(n) => infer_expr(n, query_source_lookup, context)
235239
.map(Box::new)
236240
.map(Expression::Grouped),
237241
Expr::Subquery(query) => {
238-
let results = crate::select::parse_query(query, Some(query_source_lookup))?;
242+
let results = crate::select::parse_query(query, Some(query_source_lookup), context)?;
239243
Ok(Expression::Subquery { selection: results })
240244
}
241245
Expr::InSubquery {
242246
expr,
243247
subquery,
244248
negated,
245249
} => {
246-
let results = crate::select::parse_query(subquery, Some(query_source_lookup))?;
250+
let results = crate::select::parse_query(subquery, Some(query_source_lookup), context)?;
247251
Ok(Expression::InSubQuery {
248-
left: Box::new(infer_expr(expr, query_source_lookup)?),
252+
left: Box::new(infer_expr(expr, query_source_lookup, context)?),
249253
negated: *negated,
250254
subquery: results,
251255
})
@@ -304,6 +308,7 @@ pub(crate) fn infer_expr(
304308
fn infer_functions(
305309
f: &sqlparser::ast::Function,
306310
query_source_lookup: &HashMap<Option<&str>, QuerySource<'_>>,
311+
context: &mut ParseContext,
307312
) -> Result<Expression> {
308313
let (name, schema) = match f.name.0.as_slice() {
309314
[ObjectNamePart::Identifier(name)] => (&name.value, None),
@@ -321,7 +326,7 @@ fn infer_functions(
321326
FunctionArg::Named { arg, .. }
322327
| FunctionArg::ExprNamed { arg, .. }
323328
| FunctionArg::Unnamed(arg) => match arg {
324-
FunctionArgExpr::Expr(expr) => infer_expr(expr, query_source_lookup),
329+
FunctionArgExpr::Expr(expr) => infer_expr(expr, query_source_lookup, context),
325330
FunctionArgExpr::QualifiedWildcard(object_name) => {
326331
if let Some(item) = object_name
327332
.0
@@ -334,6 +339,7 @@ fn infer_functions(
334339
Ok(Expression::Wildcard {
335340
is_left_joined,
336341
relation: item.name.map(|s| s.to_owned()),
342+
source_id: item.source_id,
337343
schema: item.schema.map(|t| t.to_owned()),
338344
})
339345
} else {
@@ -349,6 +355,7 @@ fn infer_functions(
349355
is_left_joined: false,
350356
relation: query_source_lookup.name.map(|s| s.to_owned()),
351357
schema: query_source_lookup.schema.map(|t| t.to_owned()),
358+
source_id: query_source_lookup.source_id,
352359
})
353360
}
354361
FunctionArgExpr::WildcardWithOptions(_) | FunctionArgExpr::Wildcard => {

‎diesel_infer_query/src/query_source.rs‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ pub(crate) struct QuerySource<'a> {
5656
pub(crate) schema: Option<&'a str>,
5757
/// The name of the query source
5858
pub(crate) name: Option<&'a str>,
59+
pub(crate) source_id: Option<usize>,
5960
/// The alias that is used to refer to this query source in the query
6061
#[expect(dead_code, reason = "its there for later")]
6162
pub(crate) alias: Option<&'a str>,
@@ -160,6 +161,7 @@ impl<'a> QuerySource<'a> {
160161
schema,
161162
name: Some(name),
162163
alias,
164+
source_id: None,
163165
join: None,
164166
},
165167
);
@@ -177,6 +179,7 @@ impl<'a> QuerySource<'a> {
177179
schema: None,
178180
name: Some(&alias.name.value),
179181
alias: Some(&alias.name.value),
182+
source_id: None,
180183
join: None
181184
});
182185
Ok(())
@@ -186,6 +189,7 @@ impl<'a> QuerySource<'a> {
186189
schema: None,
187190
name: None,
188191
alias: None,
192+
source_id: None,
189193
join: None
190194
});
191195
Ok(())

0 commit comments

Comments
 (0)