-
Notifications
You must be signed in to change notification settings - Fork 627
Description
Some DBs support using placeholders for IN
clauses in prepared statements. For example in DuckDB:
$ duckdb
DuckDB v1.3.0 (Ossivalis) 71c5c07cdd
Enter ".help" for usage hints.
Connected to a transient in-memory database.
Use ".open FILENAME" to reopen on a persistent database.
D PREPARE qry AS SELECT 'a' in $list;
D EXECUTE qry(list := ['a', 'b']);
┌──────────────────────┐
│ contains($list, 'a') │
│ boolean │
├──────────────────────┤
│ true │
└──────────────────────┘
However, currently the parser doesn't handle this, for example:
#[test]
fn test_parse_in_placeholder() {
let stmt = all_dialects().verified_stmt("SELECT i IN $placeholder");
dbg!(&stmt);
}
Fails w/ SELECT i IN $placeholder: ParserError("Expected: (, found: $placeholder")
.
To fix this, I think we would need to make two changes:
First off, the definition of Expr::InList
:
/// `[ NOT ] IN (val1, val2, ...)`
InList {
expr: Box<Expr>,
list: Vec<Expr>,
negated: bool,
},
The issue is that list
is always a Vec, where in this case we want list to be a Expr::Value
w/ value = Placeholder("$placeholder")
.
If InList
supports that, then in parse_in
we can do a check like this before the expect_token(LParen)
:
if let Token::Placeholder(_) = &self.peek_token_ref().token {
let placeholder = self.parse_expr()?;
return Ok(Expr::InList {
expr: Box::new(expr),
list: placeholder,
negated,
})
};
self.expect_token(&Token::LParen)?;
But, how can we cleanly support this, without too much breakage for existing consumers? Note that I considered just putting the placeholder inside the list, but that doesn't work since that would represent IN ($placeholder)
which has a very different meaning.