Skip to content

Commit 30ccc3b

Browse files
committed
refactor(WIP)!: querybuilder now is made of AST types for query generation
1 parent 044efe7 commit 30ccc3b

10 files changed

Lines changed: 262 additions & 122 deletions

File tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
pub mod contracts;
22
pub mod types;
3+
pub mod syntax;
34

45
pub use self::{contracts::*, types::*};
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
use crate::query::operators::Comp;
2+
use crate::query::parameters::QueryParameter;
3+
use crate::query::querybuilder::syntax::tokens::SqlToken;
4+
5+
pub struct ConditionClause<'a> {
6+
// TODO: where are missing complex where usages, like in joins, so we should consider to add the table
7+
// to the column like where table.column = ...
8+
pub(crate) kind: ConditionClauseKind,
9+
pub(crate) column_name: SqlToken<'a>,
10+
pub(crate) operator: Comp,
11+
pub(crate) value: &'a dyn QueryParameter
12+
}
13+
#[derive(Eq, PartialEq)]
14+
pub enum ConditionClauseKind {
15+
Where,
16+
And,
17+
Or,
18+
In // TODO: should this one be a Comp instead?
19+
}
20+
21+
impl<'a> AsRef<str> for ConditionClauseKind {
22+
fn as_ref(&self) -> &str {
23+
match self {
24+
ConditionClauseKind::Where => "WHERE",
25+
ConditionClauseKind::And => "AND",
26+
ConditionClauseKind::In => "IN",
27+
ConditionClauseKind::Or => "OR",
28+
}
29+
}
30+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
pub(crate) mod query_kind;
2+
pub(crate) mod table_metadata;
3+
pub(crate) mod tokens;
4+
pub(crate) mod clause;
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
use std::borrow::Cow;
2+
use crate::query::querybuilder::syntax::tokens::{SqlToken, ToSqlTokens};
3+
4+
pub enum QueryKind {
5+
Select,
6+
Update,
7+
Delete,
8+
}
9+
10+
impl<'a> ToSqlTokens<'a> for QueryKind {
11+
fn to_tokens(&self) -> SqlToken<'a> {
12+
match self {
13+
QueryKind::Select => SqlToken::Keyword(Cow::from("SELECT")),
14+
QueryKind::Update => SqlToken::Keyword(Cow::from("UPDATE")),
15+
QueryKind::Delete => SqlToken::Keyword(Cow::from("DELETE")),
16+
}
17+
}
18+
}
19+
impl AsRef<str> for QueryKind {
20+
fn as_ref(&self) -> &str {
21+
match self {
22+
QueryKind::Select => { "SELECT" }
23+
QueryKind::Update => { "UPDATE " }
24+
QueryKind::Delete => { "DELETE " }
25+
}
26+
}
27+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
use std::fmt::{Display, Formatter};
2+
use crate::query::querybuilder::syntax::tokens::{SqlToken, ToSqlTokens};
3+
4+
#[derive(Clone, Default, Debug)]
5+
pub struct TableMetadata {
6+
pub schema: Option<String>,
7+
pub name: String,
8+
} // TODO: we can have those fields as Cow<'_> for max performance
9+
10+
impl<'a> ToSqlTokens<'a> for TableMetadata {
11+
fn to_tokens(&self) -> SqlToken<'a> {
12+
match &self.schema {
13+
Some(s) => {
14+
out.push(SqlToken::Ident(s));
15+
out.push(SqlToken::Symbol('.'));
16+
out.push(SqlToken::Ident(&self.name));
17+
}
18+
None => {
19+
out.push(SqlToken::Ident(&self.name));
20+
}
21+
}
22+
}
23+
}
24+
impl From<&str> for TableMetadata {
25+
/// Creates a new [`TableMetadata`] from a string slice.
26+
///
27+
/// If the slice contains a dot, we assume that is a schema.table_name format, otherwise,
28+
/// we assume that the client is just creating a [`Self`] from the passed in string
29+
fn from(value: &str) -> Self {
30+
if let Some((schema, table)) = value.split_once('.') {
31+
TableMetadata {
32+
schema: Some(schema.to_string()),
33+
name: table.to_string(),
34+
}
35+
} else {
36+
TableMetadata {
37+
schema: None,
38+
name: value.to_string(),
39+
}
40+
}
41+
}
42+
}
43+
44+
45+
impl<'a> TableMetadata {
46+
pub fn new(schema: &'a str, name: &'a str) -> Self {
47+
Self { schema: Some(schema.to_string()), name: name.to_string() }
48+
}
49+
pub fn schema(&mut self, schema: String) { self.schema = Some(schema); }
50+
pub fn table_name(&mut self, table_name: String) { self.name = table_name }
51+
52+
/// Returns an already formatted version of the schema and table of a target database table
53+
/// ready to be used in a SQL statement.
54+
///
55+
/// This method allocates a new string, so it returns an owned one to the callee.
56+
/// Just take it in consideration if someday someone uses it outside the macro generation
57+
/// and there's some heavy callee procedure
58+
pub fn sql(&self) -> String {
59+
match &self.schema {
60+
Some(schema_name) => {format!("{}.{}", schema_name, self.name)}
61+
None => self.name.to_string()
62+
}
63+
}
64+
}
65+
66+
impl Display for TableMetadata {
67+
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
68+
match &self.schema {
69+
Some(schema_name) => {write!(f, "{}.{}", schema_name, self.name)}
70+
None => {write!(f, "{}", self.name)}
71+
}
72+
}
73+
}
74+
75+
impl AsRef<str> for TableMetadata {
76+
fn as_ref(&self) -> &str {
77+
self.schema.as_ref().unwrap()
78+
}
79+
}
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 std::fmt::Write;
3+
use crate::connection::database_type::DatabaseType;
4+
5+
pub trait ToSqlTokens<'a> {
6+
fn to_tokens(&self) -> SqlToken<'a>;
7+
}
8+
9+
pub enum SqlToken<'a> {
10+
Keyword(Cow<'a, str>), // SELECT, WHERE, AND, OR, FROM, UPDATE, DELETE
11+
WhiteSpace,
12+
Ident(Cow<'a, str>), // table, column
13+
Symbol(char), // = , ( ) , .
14+
Placeholder(usize), // $1, ? , @P1
15+
}
16+
17+
pub struct TokenWriter<'a> {
18+
pub tokens: Vec<SqlToken<'a>>,
19+
}
20+
21+
impl<'a> TokenWriter<'a> {
22+
pub fn new() -> Self {
23+
Self { tokens: Vec::new() }
24+
}
25+
26+
pub fn push(&mut self, token: SqlToken<'a>) {
27+
self.tokens.push(token);
28+
}
29+
30+
pub fn render(self, db: DatabaseType) -> String {
31+
let mut out = String::new();
32+
33+
for t in self.tokens {
34+
match t {
35+
SqlToken::Keyword(k) => write!(out, " {}", k).unwrap(),
36+
SqlToken::Ident(i) => write!(out, " {}", i).unwrap(),
37+
SqlToken::Symbol(c) => write!(out, " {}", c).unwrap(),
38+
SqlToken::Placeholder(p) => match db { // TODO: invent something like placeholder kind, so we can avoid to match it on every pplaceholder?
39+
DatabaseType::PostgreSql => write!(out, " ${}", p).unwrap(),
40+
DatabaseType::SqlServer => write!(out, " @P{}", p).unwrap(),
41+
DatabaseType::MySQL => write!(out, " ?").unwrap(),
42+
DatabaseType::Deferred => write!(out, " ?").unwrap(),
43+
},
44+
}
45+
}
46+
47+
out.trim_start().to_string()
48+
}
49+
}

canyon_core/src/query/querybuilder/types/delete.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
use crate::connection::database_type::DatabaseType;
22
use crate::query::bounds::{FieldIdentifier, FieldValueIdentifier};
3-
use crate::query::operators::{Comp, Operator};
3+
use crate::query::operators::Comp;
44
use crate::query::parameters::QueryParameter;
55
use crate::query::query::Query;
66
use crate::query::querybuilder::types::TableMetadata;
7-
use crate::query::querybuilder::{DeleteQueryBuilderOps, QueryBuilder, QueryBuilderOps, QueryKind};
7+
use crate::query::querybuilder::{DeleteQueryBuilderOps, QueryBuilder, QueryBuilderOps};
8+
use crate::query::querybuilder::syntax::query_kind::QueryKind;
89
use std::error::Error;
910

1011
/// Contains the specific database operations associated with the

0 commit comments

Comments
 (0)