Skip to content

Commit a7abdbf

Browse files
committed
duckdb: support #N positional column references
Parse DuckDB's #1, #2 positional column references as Value::Placeholder. DuckDB tokenizes # as Token::Sharp followed by a number.
1 parent b51dfc5 commit a7abdbf

File tree

3 files changed

+34
-4
lines changed

3 files changed

+34
-4
lines changed

corpus-fixes/003-expected-an-expression-found-token.md

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ SELECT untuple((* EXCEPT (v2, v3),)) FROM kv;
3636

3737
## databricks (3 files)
3838

39-
- [ ] `sqlglot_databricks/select/0b4fcd7e5337.sql`
39+
- [x] `sqlglot_databricks/select/0b4fcd7e5337.sql`
4040

4141
**Error:** `Expected an expression:, found: ]`
4242

@@ -47,7 +47,9 @@ SELECT c1:item[*].price FROM VALUES ('{ "item": [ { "model" : "basic", "price" :
4747

4848
```
4949

50-
- [ ] `sqlglot_databricks/select/67b14a6de0de.sql`
50+
**Fix:** Added `Token::Mul` handling in `parse_snowflake_json_path` to emit `*` into the path string buffer, enabling `[*]` wildcard array subscript syntax in JSON paths.
51+
52+
- [x] `sqlglot_databricks/select/67b14a6de0de.sql`
5153

5254
**Error:** `Expected an expression:, found: ]`
5355

@@ -58,7 +60,9 @@ SELECT INLINE(FROM_JSON(c1:item[*], 'ARRAY<STRUCT<model STRING, price DOUBLE>>')
5860

5961
```
6062

61-
- [ ] `sqlglot_databricks/select/680db20c65dc.sql`
63+
**Fix:** Same fix as above — `[*]` in JSON path now handled by `Token::Mul` in `parse_snowflake_json_path`.
64+
65+
- [x] `sqlglot_databricks/select/680db20c65dc.sql`
6266

6367
**Error:** `Expected an expression:, found: ]`
6468

@@ -69,9 +73,11 @@ SELECT FROM_JSON(c1:item[*].price, 'ARRAY<DOUBLE>')[0] FROM VALUES ('{ "item": [
6973

7074
```
7175

76+
**Fix:** Same fix as above — `[*]` in JSON path now handled by `Token::Mul` in `parse_snowflake_json_path`.
77+
7278
## duckdb (7 files)
7379

74-
- [ ] `sqlglot_duckdb/select/02b06713af25.sql`
80+
- [x] `sqlglot_duckdb/select/02b06713af25.sql`
7581

7682
**Error:** `Expected an expression:, found: *`
7783

@@ -82,6 +88,8 @@ SELECT UNNEST([*COLUMNS('alias_.*')]) AS column_name
8288

8389
```
8490

91+
**TOO HARD** — DuckDB's `*COLUMNS(...)` star replacement operator requires adding `Expr::Wildcard`/`Expr::StarReplacement` AST variants, since wildcards currently only exist in `FunctionArgExpr` and `SelectItem`, not in the general `Expr` enum. Only 1 corpus file affected.
92+
8593
- [ ] `sqlglot_duckdb/select/1c2a1084cc5d.sql`
8694

8795
**Error:** `Expected an expression:, found: #`

src/parser/mod.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -984,6 +984,19 @@ impl<'a> Parser<'a> {
984984
expr: Box::new(self.parse_subexpr(Self::MUL_DIV_MOD_OP_PREC)?),
985985
})
986986
}
987+
Token::Sharp if dialect_of!(self is DuckDbDialect) => {
988+
// DuckDB positional reference: #1, #2, etc.
989+
match self.peek_token().token {
990+
Token::Number(ref s, _) => {
991+
let s = format!("#{}", s);
992+
self.next_token();
993+
Ok(Expr::Value(Value::Placeholder(s)))
994+
}
995+
_ => {
996+
self.expected("a number after #", self.peek_token())?
997+
}
998+
}
999+
}
9871000
Token::Tilde => {
9881001
Ok(Expr::UnaryOp {
9891002
op: UnaryOperator::PGBitwiseNot,

tests/sqlparser_duckdb.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,3 +402,12 @@ fn test_trim_with_characters() {
402402
// DuckDB supports TRIM(expr, characters) syntax
403403
duckdb().verified_stmt("SELECT TRIM('***apple***', '*') AS result");
404404
}
405+
406+
#[test]
407+
fn test_positional_reference() {
408+
// DuckDB supports #N positional references
409+
// Note: GenericDialect tokenizes #2 as identifier (since # is identifier_start),
410+
// DuckDB tokenizes as Sharp + Number, so we only test DuckDB dialect.
411+
duckdb().verified_stmt("SELECT #2 AS a, #1 AS b FROM (VALUES (1, 'foo'))");
412+
duckdb().verified_stmt("SELECT #2, #1 FROM (VALUES (1, 'foo'))");
413+
}

0 commit comments

Comments
 (0)