Skip to content

Commit f08af3d

Browse files
committed
perf(quaint): support insert common table expressions
Signed-off-by: Alexey Orlenko's AI Agent <robot@aqrln.net>
1 parent 9b25ed3 commit f08af3d

7 files changed

Lines changed: 71 additions & 22 deletions

File tree

quaint/src/ast.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ pub use column::{Column, DefaultValue, TypeDataLength, TypeFamily};
3434
pub use compare::{Comparable, Compare, JsonCompare, JsonType};
3535
pub use conditions::ConditionTree;
3636
pub use conjunctive::Conjunctive;
37+
pub(crate) use cte::CommonTableExpressionBody;
3738
pub use cte::{CommonTableExpression, IntoCommonTableExpression};
3839
pub use delete::Delete;
3940
pub use enums::{EnumName, EnumVariant};

quaint/src/ast/cte.rs

Lines changed: 43 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::borrow::Cow;
22

3-
use super::SelectQuery;
3+
use super::{Insert, Select, SelectQuery, Union};
44

55
/// A builder for a common table expression (CTE) statement, to be used in the
66
/// `WITH` block of a `SELECT` statement.
@@ -12,7 +12,13 @@ use super::SelectQuery;
1212
pub struct CommonTableExpression<'a> {
1313
pub(crate) identifier: Cow<'a, str>,
1414
pub(crate) columns: Vec<Cow<'a, str>>,
15-
pub(crate) selection: SelectQuery<'a>,
15+
pub(crate) body: CommonTableExpressionBody<'a>,
16+
}
17+
18+
#[derive(Debug, PartialEq, Clone)]
19+
pub(crate) enum CommonTableExpressionBody<'a> {
20+
Selection(SelectQuery<'a>),
21+
Insert(Insert<'a>),
1622
}
1723

1824
impl<'a> CommonTableExpression<'a> {
@@ -30,14 +36,40 @@ impl<'a> CommonTableExpression<'a> {
3036
///
3137
/// [`Select#with`]: struct.Select.html#method.with
3238
pub trait IntoCommonTableExpression<'a> {
33-
fn into_cte(self, identifier: impl Into<Cow<'a, str>>) -> CommonTableExpression<'a>
34-
where
35-
Self: Into<SelectQuery<'a>>,
36-
{
37-
CommonTableExpression {
38-
identifier: identifier.into(),
39-
columns: Vec::new(),
40-
selection: self.into(),
41-
}
39+
fn into_cte(self, identifier: impl Into<Cow<'a, str>>) -> CommonTableExpression<'a>;
40+
}
41+
42+
fn common_table_expression<'a>(
43+
identifier: impl Into<Cow<'a, str>>,
44+
body: CommonTableExpressionBody<'a>,
45+
) -> CommonTableExpression<'a> {
46+
CommonTableExpression {
47+
identifier: identifier.into(),
48+
columns: Vec::new(),
49+
body,
50+
}
51+
}
52+
53+
impl<'a> IntoCommonTableExpression<'a> for Select<'a> {
54+
fn into_cte(self, identifier: impl Into<Cow<'a, str>>) -> CommonTableExpression<'a> {
55+
common_table_expression(identifier, CommonTableExpressionBody::Selection(self.into()))
56+
}
57+
}
58+
59+
impl<'a> IntoCommonTableExpression<'a> for Union<'a> {
60+
fn into_cte(self, identifier: impl Into<Cow<'a, str>>) -> CommonTableExpression<'a> {
61+
common_table_expression(identifier, CommonTableExpressionBody::Selection(self.into()))
62+
}
63+
}
64+
65+
impl<'a> IntoCommonTableExpression<'a> for SelectQuery<'a> {
66+
fn into_cte(self, identifier: impl Into<Cow<'a, str>>) -> CommonTableExpression<'a> {
67+
common_table_expression(identifier, CommonTableExpressionBody::Selection(self))
68+
}
69+
}
70+
71+
impl<'a> IntoCommonTableExpression<'a> for Insert<'a> {
72+
fn into_cte(self, identifier: impl Into<Cow<'a, str>>) -> CommonTableExpression<'a> {
73+
common_table_expression(identifier, CommonTableExpressionBody::Insert(self))
4274
}
4375
}

quaint/src/ast/query.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
use crate::ast::{Delete, Insert, Merge, Select, Union, Update};
22
use std::borrow::Cow;
33

4-
use super::IntoCommonTableExpression;
5-
64
/// A database query
75
#[derive(Debug, PartialEq)]
86
pub enum Query<'a> {
@@ -107,5 +105,3 @@ impl<'a> From<SelectQuery<'a>> for Query<'a> {
107105
}
108106
}
109107
}
110-
111-
impl<'a> IntoCommonTableExpression<'a> for SelectQuery<'a> {}

quaint/src/ast/select.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -722,8 +722,6 @@ impl<'a> Select<'a> {
722722
}
723723
}
724724

725-
impl<'a> IntoCommonTableExpression<'a> for Select<'a> {}
726-
727725
impl<'a> Extend<Expression<'a>> for Select<'a> {
728726
fn extend<T: IntoIterator<Item = Expression<'a>>>(&mut self, iter: T) {
729727
self.columns.extend(iter);

quaint/src/ast/union.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ use crate::ast::{Expression, Query, Select};
22
use std::{collections::BTreeSet, fmt};
33

44
use super::CommonTableExpression;
5-
use super::IntoCommonTableExpression;
65

76
#[derive(Debug, PartialEq, Clone, Copy)]
87
pub(crate) enum UnionType {
@@ -145,5 +144,3 @@ impl<'a> Union<'a> {
145144
}
146145
}
147146
}
148-
149-
impl<'a> IntoCommonTableExpression<'a> for Union<'a> {}

quaint/src/visitor.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1350,8 +1350,11 @@ pub trait Visitor<'a> {
13501350

13511351
self.write(" AS ")?;
13521352

1353-
let selection = cte.selection;
1354-
self.surround_with("(", ")", |ref mut s| s.visit_selection(selection))
1353+
let body = cte.body;
1354+
self.surround_with("(", ")", |ref mut s| match body {
1355+
CommonTableExpressionBody::Selection(selection) => s.visit_selection(selection),
1356+
CommonTableExpressionBody::Insert(insert) => s.visit_insert(insert),
1357+
})
13551358
}
13561359

13571360
fn visit_comment(&mut self, comment: Cow<'a, str>) -> Result {

quaint/src/visitor/postgres.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -934,6 +934,28 @@ mod tests {
934934
assert_eq!(expected.1, params);
935935
}
936936

937+
#[test]
938+
#[cfg(feature = "postgresql")]
939+
fn test_insert_common_table_expression() {
940+
let expected = expected_values(
941+
"WITH \"inserted\" AS (INSERT INTO \"relations\" (\"parent_id\",\"child_id\") SELECT \"parent_id\", \"child_id\" FROM \"children\" WHERE \"child_id\" = $1 ON CONFLICT DO NOTHING RETURNING \"child_id\") SELECT \"child_id\" FROM \"inserted\"",
942+
vec![10],
943+
);
944+
let selection = Select::from_table("children")
945+
.columns(vec!["parent_id", "child_id"])
946+
.so_that("child_id".equals(10));
947+
let insert = Insert::expression_into("relations", vec!["parent_id", "child_id"], selection)
948+
.on_conflict(OnConflict::DoNothing)
949+
.returning(vec!["child_id"]);
950+
let query = Select::from_table("inserted")
951+
.column("child_id")
952+
.with(insert.into_cte("inserted"));
953+
let (sql, params) = Postgres::build(query).unwrap();
954+
955+
assert_eq!(expected.0, sql);
956+
assert_eq!(expected.1, params);
957+
}
958+
937959
#[test]
938960
#[cfg(feature = "postgresql")]
939961
fn test_insert_on_conflict_update() {

0 commit comments

Comments
 (0)