mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-09-25 15:09:19 +00:00
Support IN ()
syntax of SQLite, alternate proposal (#1028)
This commit is contained in:
parent
2f0c99c405
commit
4cdaa40abe
4 changed files with 68 additions and 1 deletions
|
@ -124,6 +124,10 @@ pub trait Dialect: Debug + Any {
|
||||||
fn supports_substring_from_for_expr(&self) -> bool {
|
fn supports_substring_from_for_expr(&self) -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
/// Returns true if the dialect supports `(NOT) IN ()` expressions
|
||||||
|
fn supports_in_empty_list(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
/// Dialect-specific prefix parser override
|
/// Dialect-specific prefix parser override
|
||||||
fn parse_prefix(&self, _parser: &mut Parser) -> Option<Result<Expr, ParserError>> {
|
fn parse_prefix(&self, _parser: &mut Parser) -> Option<Result<Expr, ParserError>> {
|
||||||
// return None to fall back to the default behavior
|
// return None to fall back to the default behavior
|
||||||
|
|
|
@ -52,4 +52,8 @@ impl Dialect for SQLiteDialect {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn supports_in_empty_list(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2344,7 +2344,11 @@ impl<'a> Parser<'a> {
|
||||||
} else {
|
} else {
|
||||||
Expr::InList {
|
Expr::InList {
|
||||||
expr: Box::new(expr),
|
expr: Box::new(expr),
|
||||||
list: self.parse_comma_separated(Parser::parse_expr)?,
|
list: if self.dialect.supports_in_empty_list() {
|
||||||
|
self.parse_comma_separated0(Parser::parse_expr)?
|
||||||
|
} else {
|
||||||
|
self.parse_comma_separated(Parser::parse_expr)?
|
||||||
|
},
|
||||||
negated,
|
negated,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -2710,6 +2714,27 @@ impl<'a> Parser<'a> {
|
||||||
Ok(values)
|
Ok(values)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parse a comma-separated list of 0+ items accepted by `F`
|
||||||
|
pub fn parse_comma_separated0<T, F>(&mut self, f: F) -> Result<Vec<T>, ParserError>
|
||||||
|
where
|
||||||
|
F: FnMut(&mut Parser<'a>) -> Result<T, ParserError>,
|
||||||
|
{
|
||||||
|
// ()
|
||||||
|
if matches!(self.peek_token().token, Token::RParen) {
|
||||||
|
return Ok(vec![]);
|
||||||
|
}
|
||||||
|
// (,)
|
||||||
|
if self.options.trailing_commas
|
||||||
|
&& matches!(self.peek_nth_token(0).token, Token::Comma)
|
||||||
|
&& matches!(self.peek_nth_token(1).token, Token::RParen)
|
||||||
|
{
|
||||||
|
let _ = self.consume_token(&Token::Comma);
|
||||||
|
return Ok(vec![]);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.parse_comma_separated(f)
|
||||||
|
}
|
||||||
|
|
||||||
/// Run a parser method `f`, reverting back to the current position
|
/// Run a parser method `f`, reverting back to the current position
|
||||||
/// if unsuccessful.
|
/// if unsuccessful.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
|
|
@ -22,6 +22,7 @@ use test_utils::*;
|
||||||
use sqlparser::ast::SelectItem::UnnamedExpr;
|
use sqlparser::ast::SelectItem::UnnamedExpr;
|
||||||
use sqlparser::ast::*;
|
use sqlparser::ast::*;
|
||||||
use sqlparser::dialect::{GenericDialect, SQLiteDialect};
|
use sqlparser::dialect::{GenericDialect, SQLiteDialect};
|
||||||
|
use sqlparser::parser::ParserOptions;
|
||||||
use sqlparser::tokenizer::Token;
|
use sqlparser::tokenizer::Token;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -392,6 +393,32 @@ fn parse_attach_database() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_where_in_empty_list() {
|
||||||
|
let sql = "SELECT * FROM t1 WHERE a IN ()";
|
||||||
|
let select = sqlite().verified_only_select(sql);
|
||||||
|
if let Expr::InList { list, .. } = select.selection.as_ref().unwrap() {
|
||||||
|
assert_eq!(list.len(), 0);
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlite_with_options(ParserOptions::new().with_trailing_commas(true)).one_statement_parses_to(
|
||||||
|
"SELECT * FROM t1 WHERE a IN (,)",
|
||||||
|
"SELECT * FROM t1 WHERE a IN ()",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn invalid_empty_list() {
|
||||||
|
let sql = "SELECT * FROM t1 WHERE a IN (,,)";
|
||||||
|
let sqlite = sqlite_with_options(ParserOptions::new().with_trailing_commas(true));
|
||||||
|
assert_eq!(
|
||||||
|
"sql parser error: Expected an expression:, found: ,",
|
||||||
|
sqlite.parse_sql_statements(sql).unwrap_err().to_string()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
fn sqlite() -> TestedDialects {
|
fn sqlite() -> TestedDialects {
|
||||||
TestedDialects {
|
TestedDialects {
|
||||||
dialects: vec![Box::new(SQLiteDialect {})],
|
dialects: vec![Box::new(SQLiteDialect {})],
|
||||||
|
@ -399,6 +426,13 @@ fn sqlite() -> TestedDialects {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn sqlite_with_options(options: ParserOptions) -> TestedDialects {
|
||||||
|
TestedDialects {
|
||||||
|
dialects: vec![Box::new(SQLiteDialect {})],
|
||||||
|
options: Some(options),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn sqlite_and_generic() -> TestedDialects {
|
fn sqlite_and_generic() -> TestedDialects {
|
||||||
TestedDialects {
|
TestedDialects {
|
||||||
// we don't have a separate SQLite dialect, so test only the generic dialect for now
|
// we don't have a separate SQLite dialect, so test only the generic dialect for now
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue