Skip to content

Commit da6a17b

Browse files
committed
add support for postgres composite types
Signed-off-by: password <rbalajis25@gmail.com>
1 parent d035784 commit da6a17b

File tree

3 files changed

+77
-1
lines changed

3 files changed

+77
-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
@@ -553,6 +558,9 @@ impl fmt::Display for Expr {
553558
} => {
554559
write!(f, "{} {} {}", left, operator, right)
555560
}
561+
Expr::CompositeAccess { expr, key } => {
562+
write!(f, "{}.{}", expr, key)
563+
}
556564
}
557565
}
558566
}

src/parser.rs

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

tests/sqlparser_postgres.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1301,6 +1301,63 @@ fn test_json() {
13011301
);
13021302
}
13031303

1304+
#[test]
1305+
fn test_composite_value() {
1306+
let sql = "SELECT (on_hand.item).name FROM on_hand WHERE (on_hand.item).price > 9.99";
1307+
let select = pg().verified_only_select(sql);
1308+
assert_eq!(
1309+
SelectItem::UnnamedExpr(Expr::CompositeAccess {
1310+
key: Ident::new("name"),
1311+
expr: Box::new(Expr::Nested(Box::new(Expr::CompoundIdentifier(vec![
1312+
Ident::new("on_hand"),
1313+
Ident::new("item")
1314+
]))))
1315+
}),
1316+
select.projection[0]
1317+
);
1318+
1319+
assert_eq!(
1320+
select.selection,
1321+
Some(Expr::BinaryOp {
1322+
left: Box::new(Expr::CompositeAccess {
1323+
key: Ident::new("price"),
1324+
expr: Box::new(Expr::Nested(Box::new(Expr::CompoundIdentifier(vec![
1325+
Ident::new("on_hand"),
1326+
Ident::new("item")
1327+
]))))
1328+
}),
1329+
op: BinaryOperator::Gt,
1330+
right: Box::new(Expr::Value(Value::Number("9.99".to_string(), false)))
1331+
})
1332+
);
1333+
1334+
let sql = "SELECT (information_schema._pg_expandarray(ARRAY['i', 'i'])).n";
1335+
let select = pg().verified_only_select(sql);
1336+
assert_eq!(
1337+
SelectItem::UnnamedExpr(Expr::CompositeAccess {
1338+
key: Ident::new("n"),
1339+
expr: Box::new(Expr::Nested(Box::new(Expr::Function(Function {
1340+
name: ObjectName(vec![
1341+
Ident::new("information_schema"),
1342+
Ident::new("_pg_expandarray")
1343+
]),
1344+
args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Array(
1345+
Array {
1346+
elem: vec![
1347+
Expr::Value(Value::SingleQuotedString("i".to_string())),
1348+
Expr::Value(Value::SingleQuotedString("i".to_string())),
1349+
],
1350+
named: true
1351+
}
1352+
)))],
1353+
over: None,
1354+
distinct: false,
1355+
}))))
1356+
}),
1357+
select.projection[0]
1358+
);
1359+
}
1360+
13041361
#[test]
13051362
fn parse_comments() {
13061363
match pg().verified_stmt("COMMENT ON COLUMN tab.name IS 'comment'") {

0 commit comments

Comments
 (0)