Skip to content

Commit a0a64cd

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

15 files changed

Lines changed: 253 additions & 164 deletions

File tree

canyon_core/src/connection/clients/mysql.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ impl DbConnection for MySQLConnector {
8282
params: &[&'_ dyn QueryParameter],
8383
) -> Result<u64, Box<dyn Error + Send + Sync>> {
8484
let mysql_connection = self.0.get_conn().await?;
85-
let mysql_stmt = generate_mysql_stmt(stmt.as_ref(), params)?;
85+
let mysql_stmt = generate_mysql_stmt(stmt, params)?;
8686

8787
Ok(mysql_stmt.run(mysql_connection).await?.affected_rows())
8888
}

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

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
use crate::query::querybuilder::syntax::emitter::{AstProcessor, EmitFrom, EmitKind};
1+
use crate::query::querybuilder::syntax::emitter::{AsEmitBody, AsEmitFrom, AsEmitKind, AstProcessor, EmitBody, EmitFrom, EmitKind, ToSql};
22
use crate::query::querybuilder::syntax::table_metadata::TableMetadata;
33
use crate::query::querybuilder::syntax::tokens::{SqlToken, ToSqlTokens};
44

55
pub struct DeleteAst {}
66

77
impl AstProcessor for DeleteAst {}
8+
impl<'a> ToSql<'a> for DeleteAst {}
89

910
impl<'a> EmitKind<'a> for DeleteAst {
1011
fn emit_kind(&self, out: &mut Vec<SqlToken<'a>>) {
@@ -27,3 +28,13 @@ impl Default for DeleteAst {
2728
impl DeleteAst {
2829
pub fn new() -> Self { Self{} }
2930
}
31+
32+
impl<'a> AsEmitKind<'a> for DeleteAst {
33+
fn as_emit_kind(&self) -> Option<&dyn EmitKind<'a>> { Some(self as &dyn EmitKind<'a>) }
34+
}
35+
impl<'a> AsEmitFrom<'a> for DeleteAst {
36+
fn as_emit_from(&self) -> Option<&dyn EmitFrom<'a>> { Some(self as &dyn EmitFrom<'a>) }
37+
}
38+
impl<'a> AsEmitBody<'a> for DeleteAst {
39+
fn as_emit_body(&self) -> Option<&dyn EmitBody<'a>> { None }
40+
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use crate::query::parameters::QueryParameter;
33
use crate::query::querybuilder::syntax::emitter::{EmitBody, EmitFrom, EmitKind};
44
use crate::query::querybuilder::syntax::table_metadata::TableMetadata;
55
use crate::query::querybuilder::syntax::tokens::{SqlToken, Symbol, ToSqlTokens};
6-
use crate::query::querybuilder::syntax::tokens::Symbol::{Comma, LParen, RParen};
6+
use crate::query::querybuilder::syntax::tokens::Symbol::{LParen, RParen};
77

88
pub struct InsertAst<'a> {
99
pub columns: Vec<&'a str>,

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

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::borrow::Cow;
22
use crate::query::querybuilder::syntax::column::ColumnRef;
3-
use crate::query::querybuilder::syntax::emitter::{AstProcessor, EmitBody, EmitFrom, EmitKind};
3+
use crate::query::querybuilder::syntax::emitter::{AsEmitBody, AsEmitFrom, AsEmitKind, AstProcessor, EmitBody, EmitFrom, EmitKind, ToSql};
44
use crate::query::querybuilder::syntax::having::HavingClause;
55
use crate::query::querybuilder::syntax::join::JoinClause;
66
use crate::query::querybuilder::syntax::order::OrderByClause;
@@ -32,6 +32,8 @@ impl<'a> SelectAst<'a> {
3232
}
3333
}
3434

35+
impl<'a> ToSql<'a> for SelectAst<'a> {}
36+
3537
impl<'a> AstProcessor for SelectAst<'a> {}
3638

3739
impl<'a> EmitKind<'a> for SelectAst<'a> {
@@ -41,30 +43,49 @@ impl<'a> EmitKind<'a> for SelectAst<'a> {
4143
}
4244

4345
impl<'a> EmitFrom<'a> for SelectAst<'a> {
44-
fn emit_from<'b>(&self, meta: &TableMetadata<'b>, out: &mut Vec<SqlToken<'b>>) {
46+
fn emit_from(&self, meta: &TableMetadata<'a>, out: &mut Vec<SqlToken<'a>>) {
4547
// columns
4648
if self.columns.is_empty() {
4749
out.push(SqlToken::Symbol(Symbol::Asterisk));
4850
} else {
49-
for (i, c) in self.columns.iter().enumerate() {
50-
if i > 0 { out.push(SqlToken::Symbol(Symbol::Comma)); }
51-
// TODO: out.push(SqlToken::new_ident(c));
51+
for (i, col) in self.columns.iter().enumerate() {
52+
if i > 0 {
53+
out.push(SqlToken::Symbol(Symbol::Comma));
54+
}
55+
col.to_tokens(out);
56+
// out.push(SqlToken::new_ident(col));
5257
}
5358
}
5459
// FROM
5560
out.push(SqlToken::new_keyword("FROM"));
5661
meta.to_tokens(out);
5762

58-
// joins (simplified)
59-
for j in &self.joins {
60-
out.push(SqlToken::new_keyword("LEFT JOIN")); // TODO: actual placeholder until
61-
// we bring the JoinClauses
62-
// TODO: out.push(SqlToken::new_ident(*j));
63-
}
6463
}
6564
}
6665
impl<'a> EmitBody<'a> for SelectAst<'a> {
6766
fn emit_body(&self, _out: &mut Vec<SqlToken<'a>>) {
6867
// optional: ORDER BY, LIMIT, etc. left for child builder
68+
69+
// joins (simplified)
70+
// for j in &self.joins {
71+
// _out.push(SqlToken::new_keyword("LEFT JOIN")); // TODO: actual placeholder until
72+
// // we bring the JoinClauses
73+
// // TODO: out.push(SqlToken::new_ident(*j));
74+
// }
6975
}
7076
}
77+
78+
79+
// tell the system that SelectAst supports these phases:
80+
impl<'a> AsEmitKind<'a> for SelectAst<'a> {
81+
fn as_emit_kind(&self) -> Option<&dyn EmitKind<'a>> { Some(self as &dyn EmitKind<'a>) }
82+
}
83+
impl<'a> AsEmitFrom<'a> for SelectAst<'a> {
84+
fn as_emit_from(&self) -> Option<&dyn EmitFrom<'a>> { Some(self as &dyn EmitFrom<'a>) }
85+
}
86+
impl<'a> AsEmitBody<'a> for SelectAst<'a> {
87+
fn as_emit_body(&self) -> Option<&dyn EmitBody<'a>> { Some(self as &dyn EmitBody<'a>) }
88+
}
89+
// impl<'a> AsEmitConditions<'a> for SelectAst<'a> {
90+
// fn as_emit_conditions(&self) -> Option<&dyn EmitConditions<'a>> { Some(self as &dyn EmitConditions<'a>) }
91+
// }

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

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
use crate::query::querybuilder::syntax::column::ColumnRef;
2-
use crate::query::querybuilder::syntax::emitter::{AstProcessor, EmitBody, EmitKind};
2+
use crate::query::querybuilder::syntax::emitter::{AsEmitBody, AsEmitFrom, AsEmitKind, AstProcessor, EmitBody, EmitFrom, EmitKind, ToSql};
33
use crate::query::querybuilder::syntax::tokens::SqlToken;
44

55
pub struct UpdateAst<'a> {
66
pub columns: Vec<ColumnRef<'a>>,
77
}
88

99
impl<'a> AstProcessor for UpdateAst<'a> {}
10+
impl<'a> ToSql<'a> for UpdateAst<'a> {}
1011

1112
impl<'a> EmitKind<'a> for UpdateAst<'a> {
1213
fn emit_kind(&self, out: &mut Vec<SqlToken<'a>>) {
@@ -38,3 +39,13 @@ impl<'a> UpdateAst<'a> {
3839
Self { columns: Vec::new() }
3940
}
4041
}
42+
43+
impl<'a> AsEmitKind<'a> for UpdateAst<'a> {
44+
fn as_emit_kind(&self) -> Option<&dyn EmitKind<'a>> { Some(self as &dyn EmitKind<'a>) }
45+
}
46+
impl<'a> AsEmitFrom<'a> for UpdateAst<'a> {
47+
fn as_emit_from(&self) -> Option<&dyn EmitFrom<'a>> { None }
48+
}
49+
impl<'a> AsEmitBody<'a> for UpdateAst<'a> {
50+
fn as_emit_body(&self) -> Option<&dyn EmitBody<'a>> { Some(self as &dyn EmitBody<'a>) }
51+
}

canyon_core/src/query/querybuilder/syntax/column.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
use crate::query::querybuilder::syntax::tokens::{SqlToken, ToSqlTokens};
2+
use crate::query::querybuilder::syntax::tokens::Symbol::Dot;
3+
14
#[derive(Debug, Clone)]
25
pub struct ColumnRef<'a> {
36
pub column: &'a str,
@@ -13,6 +16,22 @@ impl<'a> From<&'a str> for ColumnRef<'a> {
1316
}
1417
}
1518

19+
impl<'a> ToSqlTokens<'a> for ColumnRef<'a> {
20+
fn to_tokens(&self, out: &mut Vec<SqlToken<'a>>) {
21+
if let Some(table_ref) = self.table {
22+
out.push(SqlToken::new_ident(table_ref));
23+
out.push(SqlToken::Symbol(Dot))
24+
}
25+
26+
out.push(SqlToken::new_ident(self.column));
27+
28+
if let Some(alias) = self.alias {
29+
out.push(SqlToken::new_keyword("AS"));
30+
out.push(SqlToken::new_ident(alias));
31+
}
32+
}
33+
}
34+
1635
impl<'a> ColumnRef<'a> {
1736
pub fn new(column_name: &'a str) -> Self {
1837
Self { column: column_name, table: None, alias: None }
Lines changed: 109 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1,95 +1,126 @@
1-
use std::borrow::Cow;
2-
use crate::query::querybuilder::syntax::ast::delete::DeleteAst;
3-
use crate::query::querybuilder::syntax::ast::insert::InsertAst;
4-
use crate::query::querybuilder::syntax::ast::select::SelectAst;
5-
use crate::query::querybuilder::syntax::ast::update::UpdateAst;
6-
use crate::query::querybuilder::syntax::clause::ConditionClause;
7-
use crate::query::querybuilder::syntax::query_kind::QueryKind;
81
use crate::query::querybuilder::syntax::table_metadata::TableMetadata;
9-
use crate::query::querybuilder::syntax::tokens::{SqlToken, Symbol, ToSqlTokens};
10-
use crate::query::querybuilder::syntax::tokens::Symbol::{LParen, RParen};
2+
use crate::query::querybuilder::syntax::tokens::SqlToken;
113

12-
// ---------- AST Processor marker trait ----------
13-
pub trait AstProcessor: Default {
14-
// TODO: get base? as mut ref for convenience?
15-
} // TODO: maybe this and the other one are visitor related?
16-
17-
// ---------- Emit traits ----------
18-
pub trait EmitKind<'a> {
19-
fn emit_kind(&self, out: &mut Vec<SqlToken<'a>>);
4+
// default impl returns None; concrete ASTs override to return Some(&self as &dyn ...)
5+
pub trait AsEmitKind<'a> {
6+
fn as_emit_kind(&self) -> Option<&dyn EmitKind<'a>> { None }
207
}
21-
pub trait EmitFrom<'a> {
22-
fn emit_from<'b>(&self, meta: &TableMetadata<'b>, out: &mut Vec<SqlToken<'b>>) ;
8+
pub trait AsEmitFrom<'a> {
9+
fn as_emit_from(&self) -> Option<&dyn EmitFrom<'a>> { None }
2310
}
24-
pub trait EmitBody<'a> {
25-
fn emit_body(&self, out: &mut Vec<SqlToken<'a>>);
11+
pub trait AsEmitBody<'a> {
12+
fn as_emit_body(&self) -> Option<&dyn EmitBody<'a>> { None }
2613
}
27-
28-
29-
// ---------- QueryEmitter enum ----------
30-
pub enum QueryEmitter<'a> { // Isn't this almost queryKind?
31-
// Raw(BaseAst<'a>),
32-
Select(SelectAst<'a>),
33-
Insert(InsertAst<'a>),
34-
Update(UpdateAst<'a>),
35-
Delete(DeleteAst),
36-
}
37-
// ---------- QueryEmitter enum ----------
38-
// pub struct QueryEmitter<'a, P: AstProcessor> { // Isn't this almost queryKind?
39-
// kind: QueryKind,
40-
// ast: P,
41-
// _p: &'a str,
14+
// pub trait AsEmitConditions<'a> {
15+
// fn as_emit_conditions(&self) -> Option<&dyn EmitConditions<'a>> { None }
4216
// }
4317

44-
impl<'a> QueryEmitter<'a> {
45-
/// façade: executes all phases in logical order: KIND -> FROM -> BODY -> CONDITIONS
46-
pub fn emit_all_phases(
18+
// The façade trait: ToSql groups phases.
19+
// Require AsEmit* so we can call as_emit_* on any AST type P.
20+
pub trait ToSql<'a>: AsEmitKind<'a> + AsEmitFrom<'a> + AsEmitBody<'a> /*+ AsEmitConditions<'a> */ {
21+
fn emit_all(
4722
&self,
4823
meta: &TableMetadata<'a>,
49-
conditions: &[ConditionClause<'a>],
24+
//conditions: &[ConditionClause<'a>],
5025
out: &mut Vec<SqlToken<'a>>
5126
) {
52-
// 1. kind
53-
self.emit_kind(out);
54-
// 2. from (if any)
55-
self.emit_from(meta, out);
56-
// 3. body (set/values/insert columns...)
57-
self.emit_body(out); // TODO: swap body and conditions
58-
// 4. conditions (WHERE / AND / OR)
59-
for cond in conditions {
60-
// cond.to_tokens(out);
61-
}
62-
}
63-
}
27+
// KIND
28+
if let Some(k) = self.as_emit_kind() { k.emit_kind(out); }
29+
30+
// FROM
31+
if let Some(f) = self.as_emit_from() { f.emit_from(meta, out); }
6432

65-
impl<'a> EmitKind<'a> for QueryEmitter<'a> {
66-
fn emit_kind(&self, out: &mut Vec<SqlToken<'a>>) {
67-
match self {
68-
Self::Select(ast) => ast.emit_kind(out),
69-
Self::Insert(ast) => ast.emit_kind(out),
70-
Self::Update(ast) => ast.emit_kind(out),
71-
Self::Delete(ast) => ast.emit_kind(out),
72-
}
33+
// BODY
34+
if let Some(b) = self.as_emit_body() { b.emit_body(out); }
35+
36+
// CONDITIONS (if AST wants to override condition emission)
37+
// if let Some(c) = self.as_emit_conditions() {
38+
// c.emit_conditions(conditions, out);
39+
// } else {
40+
// // fallback generic emission of base conditions (if base handles it)
41+
// for cond in conditions {
42+
// cond.to_tokens(out); // your ConditionClause -> SqlToken
43+
// }
44+
// }
7345
}
7446
}
7547

76-
impl<'a> EmitFrom<'a> for QueryEmitter<'a> {
77-
fn emit_from<'b>(&self, meta: &TableMetadata<'b>, out: &mut Vec<SqlToken<'b>>) {
78-
match self {
79-
Self::Select(ast) => ast.emit_from(meta, out),
80-
Self::Insert(ast) => ast.emit_from(meta, out),
81-
Self::Update(_) => { },
82-
Self::Delete(ast) => ast.emit_from(meta, out)
83-
}
84-
}
48+
49+
// ---------- AST Processor marker trait ----------
50+
pub trait AstProcessor: Default {
51+
// TODO: get base? as mut ref for convenience?
52+
} // TODO: maybe this and the other one are visitor related?
53+
54+
// ---------- Emit traits ----------
55+
pub trait EmitKind<'a> {
56+
fn emit_kind(&self, out: &mut Vec<SqlToken<'a>>);
8557
}
86-
impl<'a> EmitBody<'a> for QueryEmitter<'a> {
87-
fn emit_body(&self, out: &mut Vec<SqlToken<'a>>) {
88-
match self {
89-
Self::Select(ast) => ast.emit_body(out),
90-
Self::Insert(ast) => ast.emit_body(out),
91-
Self::Update(ast) => ast.emit_body(out),
92-
Self::Delete(_) => { /* delete has no body */ }
93-
}
94-
}
58+
pub trait EmitFrom<'a> {
59+
fn emit_from(&self, meta: &TableMetadata<'a>, out: &mut Vec<SqlToken<'a>>) ;
9560
}
61+
pub trait EmitBody<'a> {
62+
fn emit_body(&self, out: &mut Vec<SqlToken<'a>>);
63+
}
64+
//
65+
//
66+
// // ---------- QueryEmitter enum ----------
67+
// pub enum QueryEmitter<'a> { // Isn't this almost queryKind?
68+
// // Raw(BaseAst<'a>),
69+
// Select(SelectAst<'a>),
70+
// Insert(InsertAst<'a>),
71+
// Update(UpdateAst<'a>),
72+
// Delete(DeleteAst),
73+
// }
74+
//
75+
// impl<'a> QueryEmitter<'a> {
76+
// /// façade: executes all phases in logical order: KIND -> FROM -> BODY -> CONDITIONS
77+
// pub fn emit_all_phases(
78+
// &self,
79+
// meta: &TableMetadata<'a>,
80+
// conditions: &[ConditionClause<'a>],
81+
// out: &mut Vec<SqlToken<'a>>
82+
// ) {
83+
// // 1. kind
84+
// self.emit_kind(out);
85+
// // 2. from (if any)
86+
// self.emit_from(meta, out);
87+
// // 3. body (set/values/insert columns...)
88+
// self.emit_body(out); // TODO: swap body and conditions
89+
// // 4. conditions (WHERE / AND / OR)
90+
// for cond in conditions {
91+
// // cond.to_tokens(out);
92+
// }
93+
// }
94+
// }
95+
//
96+
// impl<'a> EmitKind<'a> for QueryEmitter<'a> {
97+
// fn emit_kind(&self, out: &mut Vec<SqlToken<'a>>) {
98+
// match self {
99+
// Self::Select(ast) => ast.emit_kind(out),
100+
// Self::Insert(ast) => ast.emit_kind(out),
101+
// Self::Update(ast) => ast.emit_kind(out),
102+
// Self::Delete(ast) => ast.emit_kind(out),
103+
// }
104+
// }
105+
// }
106+
//
107+
// impl<'a> EmitFrom<'a> for QueryEmitter<'a> {
108+
// fn emit_from(&self, meta: &TableMetadata<'a>, out: &mut Vec<SqlToken<'a>>) {
109+
// match self {
110+
// Self::Select(ast) => ast.emit_from(meta, out),
111+
// Self::Insert(ast) => ast.emit_from(meta, out),
112+
// Self::Update(_) => { },
113+
// Self::Delete(ast) => ast.emit_from(meta, out)
114+
// }
115+
// }
116+
// }
117+
// impl<'a> EmitBody<'a> for QueryEmitter<'a> {
118+
// fn emit_body(&self, out: &mut Vec<SqlToken<'a>>) {
119+
// match self {
120+
// Self::Select(ast) => ast.emit_body(out),
121+
// Self::Insert(ast) => ast.emit_body(out),
122+
// Self::Update(ast) => ast.emit_body(out),
123+
// Self::Delete(_) => { /* delete has no body */ }
124+
// }
125+
// }
126+
// }

canyon_core/src/query/querybuilder/syntax/query_kind.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
use std::borrow::Cow;
2-
use crate::query::querybuilder::syntax::tokens::{SqlToken, ToSqlTokens};
31

42
#[derive(Default)]
53
pub enum QueryKind {

0 commit comments

Comments
 (0)