Skip to content

Commit 04d0114

Browse files
SQLite: Support with a table name or table-valued function
1 parent 0cb62a1 commit 04d0114

7 files changed

Lines changed: 96 additions & 2 deletions

File tree

‎src/ast/mod.rs‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1002,6 +1002,19 @@ pub enum Expr {
10021002
/// `true` when the `NOT` modifier is present.
10031003
negated: bool,
10041004
},
1005+
/// `[ NOT ] IN table_name` or `[ NOT ] IN schema.table_name(args)` (SQLite)
1006+
///
1007+
/// See <https://www.sqlite.org/lang_expr.html>
1008+
InTable {
1009+
/// Left-hand expression to test for membership.
1010+
expr: Box<Expr>,
1011+
/// Table or table-valued function name.
1012+
table: ObjectName,
1013+
/// Table-valued function arguments, `None` for a plain table name.
1014+
args: Option<Vec<FunctionArg>>,
1015+
/// `true` when the `NOT` modifier is present.
1016+
negated: bool,
1017+
},
10051018
/// `<expr> [ NOT ] BETWEEN <low> AND <high>`
10061019
Between {
10071020
/// Expression being compared.
@@ -1823,6 +1836,24 @@ impl fmt::Display for Expr {
18231836
if *negated { "NOT " } else { "" },
18241837
array_expr
18251838
),
1839+
Expr::InTable {
1840+
expr,
1841+
table,
1842+
args,
1843+
negated,
1844+
} => {
1845+
write!(
1846+
f,
1847+
"{} {}IN {}",
1848+
expr,
1849+
if *negated { "NOT " } else { "" },
1850+
table
1851+
)?;
1852+
if let Some(args) = args {
1853+
write!(f, "({})", display_comma_separated(args))?;
1854+
}
1855+
Ok(())
1856+
}
18261857
Expr::Between {
18271858
expr,
18281859
negated,

‎src/ast/spans.rs‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1522,6 +1522,16 @@ impl Spanned for Expr {
15221522
array_expr,
15231523
negated: _,
15241524
} => expr.span().union(&array_expr.span()),
1525+
Expr::InTable {
1526+
expr,
1527+
negated: _,
1528+
table,
1529+
args,
1530+
} => union_spans(
1531+
core::iter::once(expr.span())
1532+
.chain(core::iter::once(table.span()))
1533+
.chain(args.iter().flatten().map(|a| a.span())),
1534+
),
15251535
Expr::Between {
15261536
expr,
15271537
negated: _,

‎src/dialect/mod.rs‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,12 @@ pub trait Dialect: Debug + Any {
453453
false
454454
}
455455

456+
/// Returns true if the dialect supports a table name or table-valued function
457+
/// as the right-hand side of `IN`, e.g. `x IN t`, `x IN schema.t`, or `x IN f(args)`.
458+
fn supports_in_table_expression(&self) -> bool {
459+
false
460+
}
461+
456462
/// Returns true if the dialect supports `BEGIN {DEFERRED | IMMEDIATE | EXCLUSIVE | TRY | CATCH} [TRANSACTION]` statements
457463
fn supports_start_transaction_modifier(&self) -> bool {
458464
false
@@ -2138,6 +2144,10 @@ mod tests {
21382144
self.0.supports_in_unparenthesized_expr()
21392145
}
21402146

2147+
fn supports_in_table_expression(&self) -> bool {
2148+
self.0.supports_in_table_expression()
2149+
}
2150+
21412151
fn convert_type_before_value(&self) -> bool {
21422152
self.0.convert_type_before_value()
21432153
}

‎src/dialect/sqlite.rs‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,4 +129,8 @@ impl Dialect for SQLiteDialect {
129129
fn supports_numeric_literal_underscores(&self) -> bool {
130130
true
131131
}
132+
133+
fn supports_in_table_expression(&self) -> bool {
134+
true
135+
}
132136
}

‎src/parser/mod.rs‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4427,6 +4427,24 @@ impl<'a> Parser<'a> {
44274427
negated,
44284428
});
44294429
}
4430+
if self.dialect.supports_in_table_expression()
4431+
&& matches!(self.peek_token_ref().token, Token::Word(_))
4432+
{
4433+
let table = self.parse_object_name(false)?;
4434+
let args = if self.consume_token(&Token::LParen) {
4435+
let args = self.parse_comma_separated0(Self::parse_function_args, Token::RParen)?;
4436+
self.expect_token(&Token::RParen)?;
4437+
Some(args)
4438+
} else {
4439+
None
4440+
};
4441+
return Ok(Expr::InTable {
4442+
expr: Box::new(expr),
4443+
table,
4444+
args,
4445+
negated,
4446+
});
4447+
}
44304448
if self.dialect.supports_in_unparenthesized_expr()
44314449
&& self.peek_token_ref().token != Token::LParen
44324450
{

‎tests/sqlparser_common.rs‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2605,8 +2605,10 @@ fn parse_in_error() {
26052605
// <expr> IN <expr> is no valid, except in dialects that accept an
26062606
// unparenthesized expression as the IN right-hand side (e.g. ClickHouse).
26072607
let sql = "SELECT * FROM customers WHERE segment in segment";
2608-
let res =
2609-
all_dialects_except(|d| d.supports_in_unparenthesized_expr()).parse_sql_statements(sql);
2608+
let res = all_dialects_except(|d| {
2609+
d.supports_in_unparenthesized_expr() || d.supports_in_table_expression()
2610+
})
2611+
.parse_sql_statements(sql);
26102612
assert_eq!(
26112613
ParserError::ParserError("Expected: (, found: segment".to_string()),
26122614
res.unwrap_err()

‎tests/sqlparser_sqlite.rs‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -957,6 +957,25 @@ fn parse_pattern_operators_bind_at_like_precedence() {
957957
}
958958
}
959959

960+
#[test]
961+
fn test_in_table_and_table_valued_function() {
962+
sqlite().verified_stmt("SELECT a IN t FROM t");
963+
sqlite().verified_stmt("SELECT a IN main.t FROM t");
964+
// Table-valued function with no arguments
965+
sqlite().verified_stmt("SELECT a IN f() FROM t");
966+
// NOT IN variants round-trip
967+
sqlite().verified_stmt("SELECT a NOT IN t FROM t");
968+
sqlite().verified_stmt("SELECT a NOT IN main.t FROM t");
969+
sqlite().verified_stmt("SELECT a NOT IN f() FROM t");
970+
// Table-valued function with arguments
971+
sqlite().verified_stmt("SELECT a IN f(1, 2) FROM t");
972+
973+
// GenericDialect rejects bare-identifier IN (no parentheses, not UNNEST)
974+
let res = TestedDialects::new(vec![Box::new(GenericDialect {})])
975+
.parse_sql_statements("SELECT a IN t FROM t");
976+
assert!(res.is_err(), "GenericDialect should reject IN <table>");
977+
}
978+
960979
fn sqlite() -> TestedDialects {
961980
TestedDialects::new(vec![Box::new(SQLiteDialect {})])
962981
}

0 commit comments

Comments
 (0)