Skip to content

Commit 4cdaa40

Browse files
authored
Support IN () syntax of SQLite, alternate proposal (#1028)
1 parent 2f0c99c commit 4cdaa40

File tree

4 files changed

+68
-1
lines changed

4 files changed

+68
-1
lines changed

src/dialect/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,10 @@ pub trait Dialect: Debug + Any {
124124
fn supports_substring_from_for_expr(&self) -> bool {
125125
true
126126
}
127+
/// Returns true if the dialect supports `(NOT) IN ()` expressions
128+
fn supports_in_empty_list(&self) -> bool {
129+
false
130+
}
127131
/// Dialect-specific prefix parser override
128132
fn parse_prefix(&self, _parser: &mut Parser) -> Option<Result<Expr, ParserError>> {
129133
// return None to fall back to the default behavior

src/dialect/sqlite.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,8 @@ impl Dialect for SQLiteDialect {
5252
None
5353
}
5454
}
55+
56+
fn supports_in_empty_list(&self) -> bool {
57+
true
58+
}
5559
}

src/parser/mod.rs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2344,7 +2344,11 @@ impl<'a> Parser<'a> {
23442344
} else {
23452345
Expr::InList {
23462346
expr: Box::new(expr),
2347-
list: self.parse_comma_separated(Parser::parse_expr)?,
2347+
list: if self.dialect.supports_in_empty_list() {
2348+
self.parse_comma_separated0(Parser::parse_expr)?
2349+
} else {
2350+
self.parse_comma_separated(Parser::parse_expr)?
2351+
},
23482352
negated,
23492353
}
23502354
};
@@ -2710,6 +2714,27 @@ impl<'a> Parser<'a> {
27102714
Ok(values)
27112715
}
27122716

2717+
/// Parse a comma-separated list of 0+ items accepted by `F`
2718+
pub fn parse_comma_separated0<T, F>(&mut self, f: F) -> Result<Vec<T>, ParserError>
2719+
where
2720+
F: FnMut(&mut Parser<'a>) -> Result<T, ParserError>,
2721+
{
2722+
// ()
2723+
if matches!(self.peek_token().token, Token::RParen) {
2724+
return Ok(vec![]);
2725+
}
2726+
// (,)
2727+
if self.options.trailing_commas
2728+
&& matches!(self.peek_nth_token(0).token, Token::Comma)
2729+
&& matches!(self.peek_nth_token(1).token, Token::RParen)
2730+
{
2731+
let _ = self.consume_token(&Token::Comma);
2732+
return Ok(vec![]);
2733+
}
2734+
2735+
self.parse_comma_separated(f)
2736+
}
2737+
27132738
/// Run a parser method `f`, reverting back to the current position
27142739
/// if unsuccessful.
27152740
#[must_use]

tests/sqlparser_sqlite.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use test_utils::*;
2222
use sqlparser::ast::SelectItem::UnnamedExpr;
2323
use sqlparser::ast::*;
2424
use sqlparser::dialect::{GenericDialect, SQLiteDialect};
25+
use sqlparser::parser::ParserOptions;
2526
use sqlparser::tokenizer::Token;
2627

2728
#[test]
@@ -392,13 +393,46 @@ fn parse_attach_database() {
392393
}
393394
}
394395

396+
#[test]
397+
fn parse_where_in_empty_list() {
398+
let sql = "SELECT * FROM t1 WHERE a IN ()";
399+
let select = sqlite().verified_only_select(sql);
400+
if let Expr::InList { list, .. } = select.selection.as_ref().unwrap() {
401+
assert_eq!(list.len(), 0);
402+
} else {
403+
unreachable!()
404+
}
405+
406+
sqlite_with_options(ParserOptions::new().with_trailing_commas(true)).one_statement_parses_to(
407+
"SELECT * FROM t1 WHERE a IN (,)",
408+
"SELECT * FROM t1 WHERE a IN ()",
409+
);
410+
}
411+
412+
#[test]
413+
fn invalid_empty_list() {
414+
let sql = "SELECT * FROM t1 WHERE a IN (,,)";
415+
let sqlite = sqlite_with_options(ParserOptions::new().with_trailing_commas(true));
416+
assert_eq!(
417+
"sql parser error: Expected an expression:, found: ,",
418+
sqlite.parse_sql_statements(sql).unwrap_err().to_string()
419+
);
420+
}
421+
395422
fn sqlite() -> TestedDialects {
396423
TestedDialects {
397424
dialects: vec![Box::new(SQLiteDialect {})],
398425
options: None,
399426
}
400427
}
401428

429+
fn sqlite_with_options(options: ParserOptions) -> TestedDialects {
430+
TestedDialects {
431+
dialects: vec![Box::new(SQLiteDialect {})],
432+
options: Some(options),
433+
}
434+
}
435+
402436
fn sqlite_and_generic() -> TestedDialects {
403437
TestedDialects {
404438
// we don't have a separate SQLite dialect, so test only the generic dialect for now

0 commit comments

Comments
 (0)