Skip to content

Commit c78ad10

Browse files
committed
sql_parser: parsing indexed columns in upsert clause
1 parent a0376a1 commit c78ad10

File tree

6 files changed

+96
-43
lines changed

6 files changed

+96
-43
lines changed

src/ast/expression.rs

+11-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ pub enum Expression {
3232
Cast(Box<Expression>, DataType),
3333

3434
/// A collate expression (e.g. expression COLLATE collation_name)
35-
CollateExpression(Box<Expression>, String),
35+
CollateExpression(CollateExpressionStatement),
3636

3737
/// A binary matching expressions (e.g. $expr1 NOT MATCH $expr2)
3838
BinaryMatchingExpression(Box<Expression>, BinaryMatchingExpression),
@@ -489,6 +489,16 @@ pub struct CaseExpression {
489489
pub else_expression: Option<Box<Expression>>,
490490
}
491491

492+
/// A collate expression
493+
#[derive(Debug, PartialEq, Clone)]
494+
pub struct CollateExpressionStatement {
495+
/// The expression
496+
pub expression: Box<Expression>,
497+
498+
/// The collation name
499+
pub collation_name: String,
500+
}
501+
492502
/// A when expression
493503
#[derive(Debug, PartialEq, Clone)]
494504
pub struct WhenExpression {

src/ast/insert.rs

+11-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use super::{
2-
ConflictClause, Expression, Identifier, QualifiedTableName, ReturningClause, SelectStatement,
3-
SetClause,
2+
ConflictClause, Expression, Identifier, Ordering, QualifiedTableName, ReturningClause,
3+
SelectStatement, SetClause,
44
};
55

66
/// An AST for [INSERT](https://www.sqlite.org/lang_insert.html) SQL statement.
@@ -45,10 +45,18 @@ pub struct UpsertClause {
4545
/// to update the table with the new values.
4646
#[derive(Debug, PartialEq)]
4747
pub struct UpsertConflictTarget {
48-
pub columns: Vec<Identifier>,
48+
pub columns: Vec<IndexedColumn>,
4949
pub where_clause: Option<Box<Expression>>,
5050
}
5151

52+
/// An indexed column
53+
#[derive(Debug, PartialEq)]
54+
pub struct IndexedColumn {
55+
pub column: Expression,
56+
57+
pub ordering: Option<Ordering>,
58+
}
59+
5260
/// The [action](https://www.sqlite.org/lang_upsert.html#upsert_action) to take when a conflict occurs.
5361
#[derive(Debug, PartialEq)]
5462
pub enum UpsertAction {

src/parser/expression/collate_expr.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::{Expression, Keyword, Parser, ParsingError};
1+
use crate::{CollateExpressionStatement, Expression, Keyword, Parser, ParsingError};
22

33
pub trait CollateExpressionParser {
44
/// Parse a COLLATE expression, i.e. an expression followed by the COLLATE keyword and a string literal
@@ -18,10 +18,10 @@ impl<'a> CollateExpressionParser for Parser<'a> {
1818
let name = self.peek_as_string()?;
1919
self.consume_token()?;
2020

21-
Ok(Expression::CollateExpression(
22-
Box::new(expression),
23-
name.to_string(),
24-
))
21+
Ok(Expression::CollateExpression(CollateExpressionStatement {
22+
expression: Box::new(expression),
23+
collation_name: name.to_string(),
24+
}))
2525
}
2626
}
2727

src/parser/expression/mod.rs

+7-3
Original file line numberDiff line numberDiff line change
@@ -395,8 +395,9 @@ impl<'a> ExpressionParser for Parser<'a> {
395395
pub(crate) mod test_utils {
396396
use crate::ast::{Expression, SelectItem};
397397
use crate::{
398-
BinaryOp, DataType, ExistsStatement, Function, FunctionArg, Identifier, LiteralValue,
399-
OverClause, Parser, RaiseFunction, SelectStatement, Statement, UnaryOp,
398+
BinaryOp, CollateExpressionStatement, DataType, ExistsStatement, Function, FunctionArg,
399+
Identifier, LiteralValue, OverClause, Parser, RaiseFunction, SelectStatement, Statement,
400+
UnaryOp,
400401
};
401402

402403
pub fn run_sunny_day_test_with_multiple_expressions(
@@ -516,7 +517,10 @@ pub(crate) mod test_utils {
516517
}
517518

518519
pub fn collate_expression(expression: Expression, name: String) -> Expression {
519-
Expression::CollateExpression(Box::new(expression), name)
520+
Expression::CollateExpression(CollateExpressionStatement {
521+
expression: Box::new(expression),
522+
collation_name: name,
523+
})
520524
}
521525

522526
pub fn expression_list(expressions: Vec<Expression>) -> Expression {

src/parser/insert/mod.rs

+48-19
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::{
2-
parser::select::SelectStatementParser, ConflictClause, Identifier, InsertStatement,
3-
InsertValues, Keyword, QualifiedTableName, TokenType, UpsertAction, UpsertClause,
4-
UpsertConflictTarget, UpsertUpdate,
2+
parser::select::SelectStatementParser, ConflictClause, Identifier, IndexedColumn,
3+
InsertStatement, InsertValues, Keyword, Ordering, QualifiedTableName, TokenType, UpsertAction,
4+
UpsertClause, UpsertConflictTarget, UpsertUpdate,
55
};
66

77
use super::{
@@ -29,6 +29,10 @@ pub trait InsertStatementParser {
2929
) -> Result<Option<UpsertConflictTarget>, ParsingError>;
3030

3131
fn parse_upsert_action(&mut self) -> Result<UpsertAction, ParsingError>;
32+
33+
fn parse_indexed_column(&mut self) -> Result<IndexedColumn, ParsingError>;
34+
35+
fn parse_ordering(&mut self) -> Result<Option<Ordering>, ParsingError>;
3236
}
3337

3438
impl<'a> InsertStatementParser for Parser<'a> {
@@ -154,7 +158,7 @@ impl<'a> InsertStatementParser for Parser<'a> {
154158
if self.consume_as(TokenType::LeftParen).is_ok() {
155159
let mut columns = vec![];
156160
loop {
157-
columns.push(self.parse_identifier()?);
161+
columns.push(self.parse_indexed_column()?);
158162
if self.consume_as(TokenType::Comma).is_err() {
159163
break;
160164
}
@@ -194,6 +198,21 @@ impl<'a> InsertStatementParser for Parser<'a> {
194198
self.peek_token()?.to_string(),
195199
))
196200
}
201+
202+
fn parse_indexed_column(&mut self) -> Result<IndexedColumn, ParsingError> {
203+
let column = self.parse_expression()?;
204+
let ordering = self.parse_ordering()?;
205+
Ok(IndexedColumn { column, ordering })
206+
}
207+
208+
fn parse_ordering(&mut self) -> Result<Option<Ordering>, ParsingError> {
209+
if self.consume_as_keyword(Keyword::Asc).is_ok() {
210+
return Ok(Some(Ordering::Asc));
211+
} else if self.consume_as_keyword(Keyword::Desc).is_ok() {
212+
return Ok(Some(Ordering::Desc));
213+
}
214+
Ok(None)
215+
}
197216
}
198217

199218
#[cfg(test)]
@@ -212,29 +231,23 @@ mod test_utils {
212231
returning_clause: vec![],
213232
}
214233
}
215-
216-
// pub fn insert_statement_with_values(values: InsertValues) -> InsertStatement {
217-
// let mut statement = insert_statement();
218-
// statement.values = values;
219-
// statement
220-
// }
221234
}
222235

223236
#[cfg(test)]
224237
mod tests_insert_statement {
225238
use super::test_utils::*;
226239
use crate::{
227240
expression::test_utils::{
228-
binary_op_expression, expression_list, identifier_expression,
241+
binary_op_expression, collate_expression, expression_list, identifier_expression,
229242
numeric_literal_expression,
230243
},
231244
parser::{
232245
cte::test_utils::cte_expression, select::test_utils::select_from,
233246
test_utils::run_sunny_day_test,
234247
},
235-
BinaryOp, ConflictClause, CteExpression, FromClause, Identifier, InsertValues,
236-
QualifiedTableName, ReturningClause, SetClause, Statement, UpsertAction, UpsertClause,
237-
UpsertConflictTarget, UpsertUpdate, WithCteStatement,
248+
BinaryOp, ConflictClause, CteExpression, FromClause, Identifier, IndexedColumn,
249+
InsertValues, QualifiedTableName, ReturningClause, SetClause, Statement, UpsertAction,
250+
UpsertClause, UpsertConflictTarget, UpsertUpdate, WithCteStatement,
238251
};
239252

240253
#[test]
@@ -299,6 +312,7 @@ mod tests_insert_statement {
299312
Identifier::Single("col1".to_string()),
300313
Identifier::Single("col2".to_string()),
301314
];
315+
302316
statement.values = InsertValues::Values(vec![
303317
vec![
304318
numeric_literal_expression("1"),
@@ -352,7 +366,7 @@ mod tests_insert_statement {
352366
let sql = r#"
353367
INSERT INTO table1 DEFAULT VALUES
354368
ON CONFLICT (col1) DO NOTHING
355-
ON CONFLICT (col2)
369+
ON CONFLICT (col2 COLLATE utf8)
356370
WHERE col2 > 10
357371
DO UPDATE SET col2 = col2 + 1
358372
ON CONFLICT (col3, col4)
@@ -365,14 +379,23 @@ mod tests_insert_statement {
365379
expected_statement.upsert_clause = Some(vec![
366380
UpsertClause {
367381
conflict_target: Some(UpsertConflictTarget {
368-
columns: vec![Identifier::Single("col1".to_string())],
382+
columns: vec![IndexedColumn {
383+
column: identifier_expression(&["col1"]),
384+
ordering: None,
385+
}],
369386
where_clause: None,
370387
}),
371388
action: UpsertAction::Nothing,
372389
},
373390
UpsertClause {
374391
conflict_target: Some(UpsertConflictTarget {
375-
columns: vec![Identifier::Single("col2".to_string())],
392+
columns: vec![IndexedColumn {
393+
column: collate_expression(
394+
identifier_expression(&["col2"]),
395+
"utf8".to_string(),
396+
),
397+
ordering: None,
398+
}],
376399
where_clause: Some(Box::new(binary_op_expression(
377400
BinaryOp::GreaterThan,
378401
identifier_expression(&["col2"]),
@@ -394,8 +417,14 @@ mod tests_insert_statement {
394417
UpsertClause {
395418
conflict_target: Some(UpsertConflictTarget {
396419
columns: vec![
397-
Identifier::Single("col3".to_string()),
398-
Identifier::Single("col4".to_string()),
420+
IndexedColumn {
421+
column: identifier_expression(&["col3"]),
422+
ordering: None,
423+
},
424+
IndexedColumn {
425+
column: identifier_expression(&["col4"]),
426+
ordering: None,
427+
},
399428
],
400429
where_clause: Some(Box::new(binary_op_expression(
401430
BinaryOp::EqualsEquals,

src/parser/select/mod.rs

+14-12
Original file line numberDiff line numberDiff line change
@@ -1489,12 +1489,14 @@ mod test_select_having_clause {
14891489
#[cfg(test)]
14901490
mod test_select_window_clause {
14911491
use super::test_utils::select_statement_with_window_clause;
1492-
use crate::expression::test_utils::{identifier_expression, numeric_literal_expression};
1492+
use crate::expression::test_utils::{
1493+
collate_expression, identifier_expression, numeric_literal_expression,
1494+
};
14931495
use crate::parser::test_utils::*;
14941496
use crate::{
1495-
BetweenFrameSpec, BetweenFrameSpecType, Expression, FrameSpec, FrameSpecExclude,
1496-
FrameSpecType, FrameType, NamedWindowDefinition, NullsOrdering, Ordering, OrderingTerm,
1497-
Statement, WindowDefinition,
1497+
BetweenFrameSpec, BetweenFrameSpecType, FrameSpec, FrameSpecExclude, FrameSpecType,
1498+
FrameType, NamedWindowDefinition, NullsOrdering, Ordering, OrderingTerm, Statement,
1499+
WindowDefinition,
14981500
};
14991501

15001502
#[test]
@@ -1526,8 +1528,8 @@ mod test_select_window_clause {
15261528
nulls_ordering: None,
15271529
},
15281530
OrderingTerm {
1529-
expression: Box::new(Expression::CollateExpression(
1530-
Box::new(identifier_expression(&["col4"])),
1531+
expression: Box::new(collate_expression(
1532+
identifier_expression(&["col4"]),
15311533
"binary".to_string(),
15321534
)),
15331535
ordering: Some(Ordering::Asc),
@@ -1623,9 +1625,9 @@ mod test_select_union_clause {
16231625
#[cfg(test)]
16241626
mod test_select_order_by_clause {
16251627
use super::test_utils::select_statement_with_order_by_clause;
1626-
use crate::expression::test_utils::identifier_expression;
1628+
use crate::expression::test_utils::{collate_expression, identifier_expression};
16271629
use crate::parser::test_utils::*;
1628-
use crate::{Expression, NullsOrdering, Ordering, OrderingTerm, Statement};
1630+
use crate::{NullsOrdering, Ordering, OrderingTerm, Statement};
16291631

16301632
#[test]
16311633
fn test_select_order_by_clause() {
@@ -1680,16 +1682,16 @@ mod test_select_order_by_clause {
16801682
fn test_select_order_by_clause_with_collation_and_nulls_ordering() {
16811683
let expected_statement = select_statement_with_order_by_clause(vec![
16821684
OrderingTerm {
1683-
expression: Box::new(Expression::CollateExpression(
1684-
Box::new(identifier_expression(&["col1"])),
1685+
expression: Box::new(collate_expression(
1686+
identifier_expression(&["col1"]),
16851687
"binary".to_string(),
16861688
)),
16871689
ordering: Some(Ordering::Asc),
16881690
nulls_ordering: Some(NullsOrdering::Last),
16891691
},
16901692
OrderingTerm {
1691-
expression: Box::new(Expression::CollateExpression(
1692-
Box::new(identifier_expression(&["col2"])),
1693+
expression: Box::new(collate_expression(
1694+
identifier_expression(&["col2"]),
16931695
"utf8".to_string(),
16941696
)),
16951697
ordering: Some(Ordering::Desc),

0 commit comments

Comments
 (0)