Skip to content

Commit ed86c6d

Browse files
authored
add support for postgres composite types (#466)
* add support for postgres composite types Signed-off-by: password <rbalajis25@gmail.com> * fix composite test for bigdecimal feature Signed-off-by: password <rbalajis25@gmail.com>
1 parent 6b2fc81 commit ed86c6d

File tree

3 files changed

+81
-1
lines changed

3 files changed

+81
-1
lines changed

src/ast/mod.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,11 @@ pub enum Expr {
231231
operator: JsonOperator,
232232
right: Box<Expr>,
233233
},
234+
/// CompositeAccess (postgres) eg: SELECT (information_schema._pg_expandarray(array['i','i'])).n
235+
CompositeAccess {
236+
expr: Box<Expr>,
237+
key: Ident,
238+
},
234239
/// `IS NULL` operator
235240
IsNull(Box<Expr>),
236241
/// `IS NOT NULL` operator
@@ -565,6 +570,9 @@ impl fmt::Display for Expr {
565570
} => {
566571
write!(f, "{} {} {}", left, operator, right)
567572
}
573+
Expr::CompositeAccess { expr, key } => {
574+
write!(f, "{}.{}", expr, key)
575+
}
568576
}
569577
}
570578
}

src/parser.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -518,7 +518,18 @@ impl<'a> Parser<'a> {
518518
}
519519
};
520520
self.expect_token(&Token::RParen)?;
521-
Ok(expr)
521+
if !self.consume_token(&Token::Period) {
522+
return Ok(expr);
523+
}
524+
let tok = self.next_token();
525+
let key = match tok {
526+
Token::Word(word) => word.to_ident(),
527+
_ => return parser_err!(format!("Expected identifier, found: {}", tok)),
528+
};
529+
Ok(Expr::CompositeAccess {
530+
expr: Box::new(expr),
531+
key,
532+
})
522533
}
523534
Token::Placeholder(_) => {
524535
self.prev_token();

tests/sqlparser_postgres.rs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1334,6 +1334,67 @@ fn test_json() {
13341334
);
13351335
}
13361336

1337+
#[test]
1338+
fn test_composite_value() {
1339+
let sql = "SELECT (on_hand.item).name FROM on_hand WHERE (on_hand.item).price > 9";
1340+
let select = pg().verified_only_select(sql);
1341+
assert_eq!(
1342+
SelectItem::UnnamedExpr(Expr::CompositeAccess {
1343+
key: Ident::new("name"),
1344+
expr: Box::new(Expr::Nested(Box::new(Expr::CompoundIdentifier(vec![
1345+
Ident::new("on_hand"),
1346+
Ident::new("item")
1347+
]))))
1348+
}),
1349+
select.projection[0]
1350+
);
1351+
1352+
#[cfg(feature = "bigdecimal")]
1353+
let num: Expr = Expr::Value(Value::Number(bigdecimal::BigDecimal::from(9), false));
1354+
#[cfg(not(feature = "bigdecimal"))]
1355+
let num: Expr = Expr::Value(Value::Number("9".to_string(), false));
1356+
assert_eq!(
1357+
select.selection,
1358+
Some(Expr::BinaryOp {
1359+
left: Box::new(Expr::CompositeAccess {
1360+
key: Ident::new("price"),
1361+
expr: Box::new(Expr::Nested(Box::new(Expr::CompoundIdentifier(vec![
1362+
Ident::new("on_hand"),
1363+
Ident::new("item")
1364+
]))))
1365+
}),
1366+
op: BinaryOperator::Gt,
1367+
right: Box::new(num)
1368+
})
1369+
);
1370+
1371+
let sql = "SELECT (information_schema._pg_expandarray(ARRAY['i', 'i'])).n";
1372+
let select = pg().verified_only_select(sql);
1373+
assert_eq!(
1374+
SelectItem::UnnamedExpr(Expr::CompositeAccess {
1375+
key: Ident::new("n"),
1376+
expr: Box::new(Expr::Nested(Box::new(Expr::Function(Function {
1377+
name: ObjectName(vec![
1378+
Ident::new("information_schema"),
1379+
Ident::new("_pg_expandarray")
1380+
]),
1381+
args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Array(
1382+
Array {
1383+
elem: vec![
1384+
Expr::Value(Value::SingleQuotedString("i".to_string())),
1385+
Expr::Value(Value::SingleQuotedString("i".to_string())),
1386+
],
1387+
named: true
1388+
}
1389+
)))],
1390+
over: None,
1391+
distinct: false,
1392+
}))))
1393+
}),
1394+
select.projection[0]
1395+
);
1396+
}
1397+
13371398
#[test]
13381399
fn parse_comments() {
13391400
match pg().verified_stmt("COMMENT ON COLUMN tab.name IS 'comment'") {

0 commit comments

Comments
 (0)