Skip to content

Commit 32d3200

Browse files
committed
refactor(WIP)!: querybuilder now is made of AST types for query generation
1 parent 039dd8c commit 32d3200

19 files changed

Lines changed: 898 additions & 934 deletions

File tree

canyon_core/src/query/querybuilder/contracts/mod.rs

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,21 @@ use std::error::Error;
55
use crate::query::bounds::{FieldIdentifier, FieldValueIdentifier, TableMetadata};
66
use crate::query::operators::Comp;
77
use crate::query::parameters::QueryParameter;
8+
use crate::query::querybuilder::syntax::column::ColumnRef;
89

910
pub trait DeleteQueryBuilderOps<'a>: QueryBuilderOps<'a> {}
1011

1112
pub trait UpdateQueryBuilderOps<'a>: QueryBuilderOps<'a> {
1213
/// Creates an SQL `SET` clause by specifying the columns that must be updated in the sentence,
13-
/// but without adding any [`QueryParameter`] value to the internal querybuilder
14-
fn set(self, columns: &'a [String]) -> Result<Self, Box<dyn Error + Send + Sync + 'a>>
14+
/// but without adding any [`QueryParameter`] value to the internal querybuilder.
15+
///
16+
/// Is it the responsibility of the callee to pass the query values that will match the generated
17+
/// sql placeholders
18+
///
19+
/// Note: If there's values already on the querybuilder, and the only placeholders api is called,
20+
/// UB (provisionally) will occur, since we're refactoring the API's and these are subject to change
21+
/// at any time while in the v0.x.x
22+
fn set<I: Into<ColumnRef<'a>>>(self, columns: Vec<I>) -> Result<Self, Box<dyn Error + Send + Sync + 'a>>
1523
where Self: Sized;
1624

1725
/// Similar to [`Self::set`] but storing the underlying update values for each column in the
@@ -26,7 +34,7 @@ pub trait UpdateQueryBuilderOps<'a>: QueryBuilderOps<'a> {
2634
pub trait SelectQueryBuilderOps<'a>: QueryBuilderOps<'a> {
2735
/// Adds the column names that must be added to the query in order to retrieve the correct mapped fields
2836
/// If this method isn't invoked, the querybuilder will create a SELECT * FROM query
29-
fn with_columns(self, columns: &'a [String]) -> Self;
37+
fn with_columns<I: Into<ColumnRef<'a>>>(self, columns: Vec<I>) -> Self;
3038

3139
/// Adds a *LEFT JOIN* SQL statement to the underlying
3240
/// `Sql Statement` held by the [`QueryBuilder`], where:
@@ -121,16 +129,16 @@ pub trait SelectQueryBuilderOps<'a>: QueryBuilderOps<'a> {
121129
pub trait QueryBuilderOps<'a> {
122130
/// Returns a read-only reference to the underlying SQL sentence,
123131
/// with the same lifetime as self
124-
fn read_sql(&'a self) -> &'a str;
125-
126-
/// Public interface for append the content of a slice to the end of
127-
/// the underlying SQL sentence.
128-
///
129-
/// This mutator will allow the user to wire SQL code to the already
130-
/// generated one
131-
///
132-
/// * `sql` - The [`&str`] to be wired in the SQL
133-
fn push_sql(self, sql: &str);
132+
// fn read_sql(&'a self) -> &'a str;
133+
134+
// /// Public interface for append the content of a slice to the end of
135+
// /// the underlying SQL sentence.
136+
// ///
137+
// /// This mutator will allow the user to wire SQL code to the already
138+
// /// generated one
139+
// ///
140+
// /// * `sql` - The [`&str`] to be wired in the SQL
141+
// fn push_sql(self, sql: &str);
134142

135143
/// Generates a `WHERE` SQL clause for constraint the query.
136144
///
Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
pub mod contracts;
22
pub mod types;
33
pub mod syntax;
4-
mod ast;
54

65
pub use self::{contracts::*, types::*};
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
use crate::query::querybuilder::syntax::emitter::{AstProcessor, EmitFrom, EmitKind};
2+
use crate::query::querybuilder::syntax::table_metadata::TableMetadata;
3+
use crate::query::querybuilder::syntax::tokens::{SqlToken, ToSqlTokens};
4+
5+
pub struct DeleteAst {}
6+
7+
impl AstProcessor for DeleteAst {}
8+
9+
impl<'a> EmitKind<'a> for DeleteAst {
10+
fn emit_kind(&self, out: &mut Vec<SqlToken<'a>>) {
11+
out.push(SqlToken::new_keyword("DELETE"));
12+
}
13+
}
14+
impl<'a> EmitFrom<'a> for DeleteAst {
15+
fn emit_from<'b>(&self, meta: &TableMetadata<'b>, out: &mut Vec<SqlToken<'b>>) {
16+
out.push(SqlToken::new_keyword("FROM"));
17+
meta.to_tokens(out);
18+
}
19+
}
20+
21+
impl Default for DeleteAst {
22+
fn default() -> Self {
23+
Self::new()
24+
}
25+
}
26+
27+
impl DeleteAst {
28+
pub fn new() -> Self { Self{} }
29+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
use std::borrow::Cow;
2+
use crate::query::parameters::QueryParameter;
3+
use crate::query::querybuilder::syntax::emitter::{EmitBody, EmitFrom, EmitKind};
4+
use crate::query::querybuilder::syntax::table_metadata::TableMetadata;
5+
use crate::query::querybuilder::syntax::tokens::{SqlToken, Symbol, ToSqlTokens};
6+
use crate::query::querybuilder::syntax::tokens::Symbol::{Comma, LParen, RParen};
7+
8+
pub struct InsertAst<'a> {
9+
pub columns: Vec<&'a str>,
10+
pub values: Vec<&'a dyn QueryParameter>,
11+
}
12+
13+
impl<'a> EmitKind<'a> for InsertAst<'a> {
14+
fn emit_kind(&self, out: &mut Vec<SqlToken<'a>>) {
15+
out.push(SqlToken::new_keyword("INSERT"));
16+
}
17+
}
18+
impl<'a> EmitFrom<'a> for InsertAst<'a> {
19+
fn emit_from<'b>(&self, meta: &TableMetadata<'b>, out: &mut Vec<SqlToken<'b>>) {
20+
if !self.columns.is_empty() { // this can't be emitted here
21+
// TODO: can we create like a pre-validator trait a-la-emit-kind but for checking invariants?
22+
out.push(SqlToken::new_ident("INTO"));
23+
meta.to_tokens(out);
24+
out.push(SqlToken::Symbol(LParen));
25+
for (i, c) in self.columns.iter().enumerate() {
26+
if i > 0 { out.push(SqlToken::Symbol(Symbol::Comma)); }
27+
//out.push(SqlToken::new_ident(c));
28+
}
29+
out.push(SqlToken::Symbol(Symbol::RParen));
30+
}
31+
}
32+
}
33+
impl<'a> EmitBody<'a> for InsertAst<'a> {
34+
fn emit_body(&self, out: &mut Vec<SqlToken<'a>>) {
35+
out.push(SqlToken::Keyword(Cow::Borrowed("VALUES")));
36+
out.push(SqlToken::Symbol(LParen));
37+
// for i in 0..self.values_count {
38+
// if i > 0 { out.push(SqlToken::Symbol(Comma)); }
39+
// // out.push(SqlToken::PlaceholderNext); self as the real type and call a custom impl
40+
// }
41+
out.push(SqlToken::Symbol(RParen));
42+
}
43+
}
44+
45+
impl<'a> InsertAst<'a> {
46+
pub fn new() -> Self {
47+
Self { columns: Vec::new(), values: Vec::new() }
48+
}
49+
}
Lines changed: 6 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,22 @@
1-
pub mod select;
1+
pub(crate) mod select;
2+
pub(crate) mod insert;
3+
pub(crate) mod update;
4+
pub(crate) mod delete;
25

3-
use crate::connection::database_type::DatabaseType;
4-
use crate::query::parameters::QueryParameter;
56
use crate::query::querybuilder::syntax::clause::ConditionClause;
67
use crate::query::querybuilder::syntax::table_metadata::TableMetadata;
7-
use crate::query::querybuilder::syntax::query_kind::QueryKind;
88

99
/// Base AST for common parts
1010
#[derive(Default)] pub struct BaseAst<'a> {
11-
pub kind: QueryKind,
1211
pub table: TableMetadata<'a>,
1312
pub conditions: Vec<ConditionClause<'a>>,
1413
}
1514

1615
impl<'a> BaseAst<'a> {
17-
pub fn new(kind: QueryKind, table: TableMetadata<'a>, database_type: DatabaseType) -> Self {
16+
pub fn new(table: impl Into<TableMetadata<'a>>) -> Self {
1817
Self {
19-
kind,
20-
table,
18+
table: table.into(),
2119
conditions: Vec::new()
2220
}
2321
}
2422
}
25-
26-
/// Select AST
27-
28-
29-
pub struct InsertAst<'a> {
30-
pub base: BaseAst<'a>,
31-
pub columns: Vec<&'a str>,
32-
pub values: Vec<&'a dyn QueryParameter>,
33-
}
34-
35-
impl<'a> InsertAst<'a> {
36-
pub fn new(table: TableMetadata<'a>, db: DatabaseType) -> Self {
37-
Self { base: BaseAst::new(QueryKind::Insert, table, db), columns: Vec::new(), values: Vec::new() }
38-
}
39-
}
40-
41-
pub struct UpdateAst<'a> {
42-
pub base: BaseAst<'a>,
43-
pub set_clauses: Vec<(&'a str, &'a dyn QueryParameter)>,
44-
}
45-
46-
impl<'a> UpdateAst<'a> {
47-
pub fn new(table: TableMetadata<'a>, db: DatabaseType) -> Self {
48-
Self { base: BaseAst::new(QueryKind::Update, table, db), set_clauses: Vec::new() }
49-
}
50-
}
51-
52-
pub struct DeleteAst<'a> {
53-
pub base: BaseAst<'a>,
54-
}
55-
56-
impl<'a> DeleteAst<'a> {
57-
pub fn new(table: TableMetadata<'a>, db: DatabaseType) -> Self {
58-
Self { base: BaseAst::new(QueryKind::Delete, table, db) }
59-
}
60-
}

canyon_core/src/query/querybuilder/syntax/ast/select.rs

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,14 @@
11
use std::borrow::Cow;
2-
use crate::connection::database_type::DatabaseType;
3-
use crate::query::querybuilder::syntax::ast::BaseAst;
42
use crate::query::querybuilder::syntax::column::ColumnRef;
53
use crate::query::querybuilder::syntax::emitter::{AstProcessor, EmitBody, EmitFrom, EmitKind};
64
use crate::query::querybuilder::syntax::having::HavingClause;
75
use crate::query::querybuilder::syntax::join::JoinClause;
86
use crate::query::querybuilder::syntax::order::OrderByClause;
9-
use crate::query::querybuilder::syntax::query_kind::QueryKind;
107
use crate::query::querybuilder::syntax::table_metadata::TableMetadata;
118
use crate::query::querybuilder::syntax::tokens::{SqlToken, Symbol, ToSqlTokens};
129

1310
#[derive(Default)]
1411
pub struct SelectAst<'a> {
15-
pub base: BaseAst<'a>,
1612
pub columns: Vec<ColumnRef<'a>>,
1713
pub joins: Vec<JoinClause<'a>>,
1814
pub group_by: Vec<&'a str>,
@@ -23,9 +19,8 @@ pub struct SelectAst<'a> {
2319
}
2420

2521
impl<'a> SelectAst<'a> {
26-
pub fn new(table: TableMetadata<'a>, db: DatabaseType) -> Self {
22+
pub fn new() -> Self {
2723
Self {
28-
base: BaseAst::new(QueryKind::Select, table, db),
2924
columns: Vec::new(),
3025
joins: Vec::new(),
3126
group_by: Vec::new(),
@@ -46,7 +41,7 @@ impl<'a> EmitKind<'a> for SelectAst<'a> {
4641
}
4742

4843
impl<'a> EmitFrom<'a> for SelectAst<'a> {
49-
fn emit_from<'b: 'a>(&'b self, meta: &'b TableMetadata<'b>, out: &mut Vec<SqlToken<'b>>) {
44+
fn emit_from<'b>(&self, meta: &TableMetadata<'b>, out: &mut Vec<SqlToken<'b>>) {
5045
// columns
5146
if self.columns.is_empty() {
5247
out.push(SqlToken::Symbol(Symbol::Asterisk));
@@ -59,7 +54,7 @@ impl<'a> EmitFrom<'a> for SelectAst<'a> {
5954
// FROM
6055
out.push(SqlToken::new_keyword("FROM"));
6156
meta.to_tokens(out);
62-
57+
6358
// joins (simplified)
6459
for j in &self.joins {
6560
out.push(SqlToken::new_keyword("LEFT JOIN")); // TODO: actual placeholder until
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
use crate::query::querybuilder::syntax::column::ColumnRef;
2+
use crate::query::querybuilder::syntax::emitter::{AstProcessor, EmitBody, EmitKind};
3+
use crate::query::querybuilder::syntax::tokens::SqlToken;
4+
5+
pub struct UpdateAst<'a> {
6+
pub columns: Vec<ColumnRef<'a>>,
7+
}
8+
9+
impl<'a> AstProcessor for UpdateAst<'a> {}
10+
11+
impl<'a> EmitKind<'a> for UpdateAst<'a> {
12+
fn emit_kind(&self, out: &mut Vec<SqlToken<'a>>) {
13+
out.push(SqlToken::new_keyword("UPDATE"));
14+
}
15+
}
16+
17+
impl<'a> EmitBody<'a> for UpdateAst<'a> {
18+
fn emit_body(&self, out: &mut Vec<SqlToken<'a>>) {
19+
out.push(SqlToken::new_keyword("SET"));
20+
21+
// for (i, (col, _val)) in self.columns.iter().enumerate() {
22+
// if i > 0 { out.push(SqlToken::Symbol(",")); }
23+
// out.push(SqlToken::Ident(col));
24+
// out.push(SqlToken::Symbol("="));
25+
// out.push(SqlToken::PlaceholderNext);
26+
// }
27+
}
28+
}
29+
30+
impl<'a> Default for UpdateAst<'a> {
31+
fn default() -> Self {
32+
Self::new()
33+
}
34+
}
35+
36+
impl<'a> UpdateAst<'a> {
37+
pub fn new() -> Self {
38+
Self { columns: Vec::new() }
39+
}
40+
}
Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,37 @@
11
#[derive(Debug, Clone)]
22
pub struct ColumnRef<'a> {
3-
pub name: &'a str,
3+
pub column: &'a str,
4+
pub table: Option<&'a str>,
45
// TODO: we need the table <table.column> no?
56
// TODO: so we need a ColumnRefBuilder, to avoid have 70 new different new methods?
67
pub alias: Option<&'a str>,
78
}
89

10+
impl<'a> From<&'a str> for ColumnRef<'a> {
11+
fn from(value: &'a str) -> Self {
12+
Self::new(value)
13+
}
14+
}
15+
916
impl<'a> ColumnRef<'a> {
10-
pub fn new(name: &'a str) -> Self {
11-
Self { name, alias: None }
17+
pub fn new(column_name: &'a str) -> Self {
18+
Self { column: column_name, table: None, alias: None }
1219
}
13-
pub fn aliased(name: &'a str, alias: &'a str) -> Self {
14-
Self { name, alias: Some(alias) }
20+
21+
/// mutator that allows to modify a [`ColumnRef`] to have a <table>.<column> format
22+
///
23+
/// Ex: SELECT * FROM <table>.<column> as <alias>
24+
pub fn table(mut self, table: &'a str) -> Self {
25+
self.table = Some(table);
26+
self
27+
}
28+
29+
/// mutator that allows to modify a [`ColumnRef`] to have a AS clause for
30+
/// specify a SQL alias
31+
///
32+
/// Ex: SELECT * FROM <table>.<column> as <alias>
33+
pub fn alias(mut self, alias: &'a str) -> Self {
34+
self.alias = Some(alias);
35+
self
1536
}
1637
}

0 commit comments

Comments
 (0)