Skip to content

Commit 7c2e3b6

Browse files
PostgreSQL: support ORDER BY USING via dialect capability
1 parent bd7f70e commit 7c2e3b6

File tree

11 files changed

+222
-7
lines changed

11 files changed

+222
-7
lines changed

src/ast/query.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2860,6 +2860,8 @@ impl fmt::Display for OrderBy {
28602860
pub struct OrderByExpr {
28612861
/// The expression to order by.
28622862
pub expr: Expr,
2863+
/// Optional PostgreSQL `USING <operator>` clause.
2864+
pub using_operator: Option<ObjectName>,
28632865
/// Ordering options such as `ASC`/`DESC` and `NULLS` behavior.
28642866
pub options: OrderByOptions,
28652867
/// Optional `WITH FILL` clause (ClickHouse extension) which specifies how to fill gaps.
@@ -2870,6 +2872,7 @@ impl From<Ident> for OrderByExpr {
28702872
fn from(ident: Ident) -> Self {
28712873
OrderByExpr {
28722874
expr: Expr::Identifier(ident),
2875+
using_operator: None,
28732876
options: OrderByOptions::default(),
28742877
with_fill: None,
28752878
}
@@ -2878,7 +2881,15 @@ impl From<Ident> for OrderByExpr {
28782881

28792882
impl fmt::Display for OrderByExpr {
28802883
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2881-
write!(f, "{}{}", self.expr, self.options)?;
2884+
write!(f, "{}", self.expr)?;
2885+
if let Some(using_operator) = &self.using_operator {
2886+
if using_operator.0.len() > 1 {
2887+
write!(f, " USING OPERATOR({using_operator})")?;
2888+
} else {
2889+
write!(f, " USING {using_operator}")?;
2890+
}
2891+
}
2892+
write!(f, "{}", self.options)?;
28822893
if let Some(ref with_fill) = self.with_fill {
28832894
write!(f, " {with_fill}")?
28842895
}

src/ast/spans.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2087,6 +2087,7 @@ impl Spanned for OrderByExpr {
20872087
fn span(&self) -> Span {
20882088
let OrderByExpr {
20892089
expr,
2090+
using_operator: _,
20902091
options: _,
20912092
with_fill,
20922093
} = self;

src/dialect/mod.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1333,6 +1333,14 @@ pub trait Dialect: Debug + Any {
13331333
false
13341334
}
13351335

1336+
/// Returns true if the dialect supports PostgreSQL-style ordering operators:
1337+
/// `ORDER BY expr USING <operator>`.
1338+
///
1339+
/// For example: `SELECT * FROM t ORDER BY a USING <`.
1340+
fn supports_order_by_using_operator(&self) -> bool {
1341+
false
1342+
}
1343+
13361344
/// Returns true if the dialect supports `SET NAMES <charset_name> [COLLATE <collation_name>]`.
13371345
///
13381346
/// - [MySQL](https://dev.mysql.com/doc/refman/8.4/en/set-names.html)

src/dialect/postgresql.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,10 @@ impl Dialect for PostgreSqlDialect {
278278
true
279279
}
280280

281+
fn supports_order_by_using_operator(&self) -> bool {
282+
true
283+
}
284+
281285
fn supports_set_names(&self) -> bool {
282286
true
283287
}

src/parser/mod.rs

Lines changed: 69 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18083,7 +18083,32 @@ impl<'a> Parser<'a> {
1808318083
None
1808418084
};
1808518085

18086-
let options = self.parse_order_by_options()?;
18086+
let using_operator = if !with_operator_class
18087+
&& self.dialect.supports_order_by_using_operator()
18088+
&& self.parse_keyword(Keyword::USING)
18089+
{
18090+
Some(self.parse_order_by_using_operator()?)
18091+
} else {
18092+
None
18093+
};
18094+
18095+
let options = if using_operator.is_some() {
18096+
if self
18097+
.peek_one_of_keywords(&[Keyword::ASC, Keyword::DESC])
18098+
.is_some()
18099+
{
18100+
return parser_err!(
18101+
"ASC/DESC cannot be used together with USING in ORDER BY".to_string(),
18102+
self.peek_token_ref().span.start
18103+
);
18104+
}
18105+
OrderByOptions {
18106+
asc: None,
18107+
nulls_first: self.parse_order_by_nulls_first_last(),
18108+
}
18109+
} else {
18110+
self.parse_order_by_options()?
18111+
};
1808718112

1808818113
let with_fill = if self.dialect.supports_with_fill()
1808918114
&& self.parse_keywords(&[Keyword::WITH, Keyword::FILL])
@@ -18096,23 +18121,61 @@ impl<'a> Parser<'a> {
1809618121
Ok((
1809718122
OrderByExpr {
1809818123
expr,
18124+
using_operator,
1809918125
options,
1810018126
with_fill,
1810118127
},
1810218128
operator_class,
1810318129
))
1810418130
}
1810518131

18106-
fn parse_order_by_options(&mut self) -> Result<OrderByOptions, ParserError> {
18107-
let asc = self.parse_asc_desc();
18132+
fn parse_order_by_using_operator(&mut self) -> Result<ObjectName, ParserError> {
18133+
let dialect = self.dialect;
1810818134

18109-
let nulls_first = if self.parse_keywords(&[Keyword::NULLS, Keyword::FIRST]) {
18135+
if self.parse_keyword(Keyword::OPERATOR) {
18136+
self.expect_token(&Token::LParen)?;
18137+
let operator_name = self.parse_operator_name()?;
18138+
let Some(last_part) = operator_name.0.last() else {
18139+
return self.expected_ref("an operator name", self.peek_token_ref());
18140+
};
18141+
let operator = last_part.to_string();
18142+
if operator.is_empty()
18143+
|| !operator
18144+
.chars()
18145+
.all(|ch| dialect.is_custom_operator_part(ch))
18146+
{
18147+
return self.expected_ref("an operator name", self.peek_token_ref());
18148+
}
18149+
self.expect_token(&Token::RParen)?;
18150+
return Ok(operator_name);
18151+
}
18152+
18153+
let token = self.next_token();
18154+
let operator = token.token.to_string();
18155+
if !operator.is_empty()
18156+
&& operator
18157+
.chars()
18158+
.all(|ch| dialect.is_custom_operator_part(ch))
18159+
{
18160+
Ok(ObjectName::from(vec![Ident::new(operator)]))
18161+
} else {
18162+
self.expected_ref("an ordering operator after USING", &token)
18163+
}
18164+
}
18165+
18166+
fn parse_order_by_nulls_first_last(&mut self) -> Option<bool> {
18167+
if self.parse_keywords(&[Keyword::NULLS, Keyword::FIRST]) {
1811018168
Some(true)
1811118169
} else if self.parse_keywords(&[Keyword::NULLS, Keyword::LAST]) {
1811218170
Some(false)
1811318171
} else {
1811418172
None
18115-
};
18173+
}
18174+
}
18175+
18176+
fn parse_order_by_options(&mut self) -> Result<OrderByOptions, ParserError> {
18177+
let asc = self.parse_asc_desc();
18178+
let nulls_first = self.parse_order_by_nulls_first_last();
1811618179

1811718180
Ok(OrderByOptions { asc, nulls_first })
1811818181
}
@@ -20309,6 +20372,7 @@ mod tests {
2030920372
asc: None,
2031020373
nulls_first: None,
2031120374
},
20375+
using_operator: None,
2031220376
with_fill: None,
2031320377
},
2031420378
operator_class: None,

tests/sqlparser_bigquery.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2721,6 +2721,7 @@ fn test_export_data() {
27212721
asc: None,
27222722
nulls_first: None,
27232723
},
2724+
using_operator: None,
27242725
with_fill: None,
27252726
},]),
27262727
interpolate: None,
@@ -2827,6 +2828,7 @@ fn test_export_data() {
28272828
asc: None,
28282829
nulls_first: None,
28292830
},
2831+
using_operator: None,
28302832
with_fill: None,
28312833
},]),
28322834
interpolate: None,

tests/sqlparser_clickhouse.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,7 @@ fn parse_alter_table_add_projection() {
334334
asc: None,
335335
nulls_first: None,
336336
},
337+
using_operator: None,
337338
with_fill: None,
338339
}]),
339340
interpolate: None,
@@ -1162,6 +1163,7 @@ fn parse_select_order_by_with_fill_interpolate() {
11621163
asc: Some(true),
11631164
nulls_first: Some(true),
11641165
},
1166+
using_operator: None,
11651167
with_fill: Some(WithFill {
11661168
from: Some(Expr::value(number("10"))),
11671169
to: Some(Expr::value(number("20"))),
@@ -1174,6 +1176,7 @@ fn parse_select_order_by_with_fill_interpolate() {
11741176
asc: Some(false),
11751177
nulls_first: Some(false),
11761178
},
1179+
using_operator: None,
11771180
with_fill: Some(WithFill {
11781181
from: Some(Expr::value(number("30"))),
11791182
to: Some(Expr::value(number("40"))),

tests/sqlparser_common.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2575,6 +2575,7 @@ fn parse_select_order_by() {
25752575
asc: Some(true),
25762576
nulls_first: None,
25772577
},
2578+
using_operator: None,
25782579
with_fill: None,
25792580
},
25802581
OrderByExpr {
@@ -2583,6 +2584,7 @@ fn parse_select_order_by() {
25832584
asc: Some(false),
25842585
nulls_first: None,
25852586
},
2587+
using_operator: None,
25862588
with_fill: None,
25872589
},
25882590
OrderByExpr {
@@ -2591,6 +2593,7 @@ fn parse_select_order_by() {
25912593
asc: None,
25922594
nulls_first: None,
25932595
},
2596+
using_operator: None,
25942597
with_fill: None,
25952598
},
25962599
]),
@@ -2616,6 +2619,7 @@ fn parse_select_order_by_limit() {
26162619
asc: Some(true),
26172620
nulls_first: None,
26182621
},
2622+
using_operator: None,
26192623
with_fill: None,
26202624
},
26212625
OrderByExpr {
@@ -2624,6 +2628,7 @@ fn parse_select_order_by_limit() {
26242628
asc: Some(false),
26252629
nulls_first: None,
26262630
},
2631+
using_operator: None,
26272632
with_fill: None,
26282633
},
26292634
]),
@@ -2737,6 +2742,7 @@ fn parse_select_order_by_not_support_all() {
27372742
asc: None,
27382743
nulls_first: None,
27392744
},
2745+
using_operator: None,
27402746
with_fill: None,
27412747
}]),
27422748
),
@@ -2748,6 +2754,7 @@ fn parse_select_order_by_not_support_all() {
27482754
asc: Some(true),
27492755
nulls_first: Some(true),
27502756
},
2757+
using_operator: None,
27512758
with_fill: None,
27522759
}]),
27532760
),
@@ -2759,6 +2766,7 @@ fn parse_select_order_by_not_support_all() {
27592766
asc: Some(false),
27602767
nulls_first: Some(false),
27612768
},
2769+
using_operator: None,
27622770
with_fill: None,
27632771
}]),
27642772
),
@@ -2782,6 +2790,7 @@ fn parse_select_order_by_nulls_order() {
27822790
asc: Some(true),
27832791
nulls_first: Some(true),
27842792
},
2793+
using_operator: None,
27852794
with_fill: None,
27862795
},
27872796
OrderByExpr {
@@ -2790,6 +2799,7 @@ fn parse_select_order_by_nulls_order() {
27902799
asc: Some(false),
27912800
nulls_first: Some(false),
27922801
},
2802+
using_operator: None,
27932803
with_fill: None,
27942804
},
27952805
]),
@@ -3012,6 +3022,7 @@ fn parse_select_qualify() {
30123022
asc: None,
30133023
nulls_first: None,
30143024
},
3025+
using_operator: None,
30153026
with_fill: None,
30163027
}],
30173028
window_frame: None,
@@ -3457,6 +3468,7 @@ fn parse_listagg() {
34573468
asc: None,
34583469
nulls_first: None,
34593470
},
3471+
using_operator: None,
34603472
with_fill: None,
34613473
},
34623474
OrderByExpr {
@@ -3469,6 +3481,7 @@ fn parse_listagg() {
34693481
asc: None,
34703482
nulls_first: None,
34713483
},
3484+
using_operator: None,
34723485
with_fill: None,
34733486
},
34743487
]
@@ -5728,6 +5741,7 @@ fn parse_window_functions() {
57285741
asc: Some(false),
57295742
nulls_first: None,
57305743
},
5744+
using_operator: None,
57315745
with_fill: None,
57325746
}],
57335747
window_frame: None,
@@ -5954,6 +5968,7 @@ fn test_parse_named_window() {
59545968
asc: None,
59555969
nulls_first: None,
59565970
},
5971+
using_operator: None,
59575972
with_fill: None,
59585973
}],
59595974
window_frame: None,
@@ -9415,6 +9430,7 @@ fn parse_create_index() {
94159430
operator_class: None,
94169431
column: OrderByExpr {
94179432
expr: Expr::Identifier(Ident::new("name")),
9433+
using_operator: None,
94189434
with_fill: None,
94199435
options: OrderByOptions {
94209436
asc: None,
@@ -9426,6 +9442,7 @@ fn parse_create_index() {
94269442
operator_class: None,
94279443
column: OrderByExpr {
94289444
expr: Expr::Identifier(Ident::new("age")),
9445+
using_operator: None,
94299446
with_fill: None,
94309447
options: OrderByOptions {
94319448
asc: Some(false),
@@ -9461,6 +9478,7 @@ fn test_create_index_with_using_function() {
94619478
operator_class: None,
94629479
column: OrderByExpr {
94639480
expr: Expr::Identifier(Ident::new("name")),
9481+
using_operator: None,
94649482
with_fill: None,
94659483
options: OrderByOptions {
94669484
asc: None,
@@ -9472,6 +9490,7 @@ fn test_create_index_with_using_function() {
94729490
operator_class: None,
94739491
column: OrderByExpr {
94749492
expr: Expr::Identifier(Ident::new("age")),
9493+
using_operator: None,
94759494
with_fill: None,
94769495
options: OrderByOptions {
94779496
asc: Some(false),
@@ -9522,6 +9541,7 @@ fn test_create_index_with_with_clause() {
95229541
asc: None,
95239542
nulls_first: None,
95249543
},
9544+
using_operator: None,
95259545
with_fill: None,
95269546
},
95279547
operator_class: None,
@@ -13148,6 +13168,7 @@ fn test_match_recognize() {
1314813168
asc: None,
1314913169
nulls_first: None,
1315013170
},
13171+
using_operator: None,
1315113172
with_fill: None,
1315213173
}],
1315313174
measures: vec![

tests/sqlparser_hive.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ fn create_table_with_clustered_by() {
174174
asc: Some(true),
175175
nulls_first: None,
176176
},
177+
using_operator: None,
177178
with_fill: None,
178179
},
179180
OrderByExpr {
@@ -182,6 +183,7 @@ fn create_table_with_clustered_by() {
182183
asc: Some(false),
183184
nulls_first: None,
184185
},
186+
using_operator: None,
185187
with_fill: None,
186188
},
187189
]),

0 commit comments

Comments
 (0)