Skip to content

Commit b566c6e

Browse files
committed
template engine
1 parent e4c9680 commit b566c6e

File tree

10 files changed

+289
-123
lines changed

10 files changed

+289
-123
lines changed

packages/cubejs-schema-compiler/src/adapter/BaseQuery.js

+18-3
Original file line numberDiff line numberDiff line change
@@ -3213,25 +3213,35 @@ export class BaseQuery {
32133213
DATE: 'DATE({{ args_concat }})',
32143214
},
32153215
statements: {
3216-
select: 'SELECT {% if distinct %}DISTINCT {% endif %}' +
3216+
select: '{% if ctes %} WITH \n' +
3217+
'{{ ctes | join(\',\n\') }}\n' +
3218+
'{% endif %}' +
3219+
'SELECT {% if distinct %}DISTINCT {% endif %}' +
32173220
'{{ select_concat | map(attribute=\'aliased\') | join(\', \') }} {% if from %}\n' +
32183221
'FROM (\n' +
32193222
'{{ from | indent(2, true) }}\n' +
3220-
') AS {{ from_alias }}{% endif %}' +
3223+
') AS {{ from_alias }}{% elif from_prepared %}\n' +
3224+
'FROM {{ from_prepared }}' +
3225+
'{% endif %}' +
32213226
'{% if filter %}\nWHERE {{ filter }}{% endif %}' +
32223227
'{% if group_by %}\nGROUP BY {{ group_by }}{% endif %}' +
3228+
'{% if having %}\nHAVING {{ having }}{% endif %}' +
32233229
'{% if order_by %}\nORDER BY {{ order_by | map(attribute=\'expr\') | join(\', \') }}{% endif %}' +
32243230
'{% if limit is not none %}\nLIMIT {{ limit }}{% endif %}' +
32253231
'{% if offset is not none %}\nOFFSET {{ offset }}{% endif %}',
32263232
group_by_exprs: '{{ group_by | map(attribute=\'index\') | join(\', \') }}',
3233+
join: '{{ join_type }} JOIN {{ source }} ON {{ condition }}',
3234+
cte: '{{ alias }} AS ({{ query | indent(2, true) }})'
32273235
},
32283236
expressions: {
32293237
column_reference: '{% if table_name %}{{ table_name }}.{% endif %}{{ name }}',
32303238
column_aliased: '{{expr}} {{quoted_alias}}',
3239+
query_aliased: '{{ query }} AS {{ quoted_alias }}',
32313240
case: 'CASE{% if expr %} {{ expr }}{% endif %}{% for when, then in when_then %} WHEN {{ when }} THEN {{ then }}{% endfor %}{% if else_expr %} ELSE {{ else_expr }}{% endif %} END',
32323241
is_null: '{{ expr }} IS {% if negate %}NOT {% endif %}NULL',
32333242
binary: '({{ left }} {{ op }} {{ right }})',
32343243
sort: '{{ expr }} {% if asc %}ASC{% else %}DESC{% endif %}{% if nulls_first %} NULLS FIRST{% endif %}',
3244+
order_by: '{% if index %} {{ index }} {% else %} {{ expr }} {% endif %} {% if asc %}ASC{% else %}DESC{% endif %}{% if nulls_first %} NULLS FIRST{% endif %}',
32353245
cast: 'CAST({{ expr }} AS {{ data_type }})',
32363246
window_function: '{{ fun_call }} OVER ({% if partition_by_concat %}PARTITION BY {{ partition_by_concat }}{% if order_by_concat or window_frame %} {% endif %}{% endif %}{% if order_by_concat %}ORDER BY {{ order_by_concat }}{% if window_frame %} {% endif %}{% endif %}{% if window_frame %}{{ window_frame }}{% endif %})',
32373247
window_frame_bounds: '{{ frame_type }} BETWEEN {{ frame_start }} AND {{ frame_end }}',
@@ -3260,7 +3270,8 @@ export class BaseQuery {
32603270
gt: '{{ column }} > {{ param }}',
32613271
gte: '{{ column }} >= {{ param }}',
32623272
lt: '{{ column }} < {{ param }}',
3263-
lte: '{{ column }} <= {{ param }}'
3273+
lte: '{{ column }} <= {{ param }}',
3274+
always_true: '1 == 1'
32643275

32653276
},
32663277
quotes: {
@@ -3270,6 +3281,10 @@ export class BaseQuery {
32703281
params: {
32713282
param: '?'
32723283
},
3284+
join_types: {
3285+
inner: 'INNER',
3286+
left: 'LEFT'
3287+
},
32733288
window_frame_types: {
32743289
rows: 'ROWS',
32753290
range: 'RANGE',

packages/cubejs-schema-compiler/test/integration/postgres/sql-generation.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -538,7 +538,7 @@ describe('SQL Generation', () => {
538538
});
539539
`);
540540

541-
it('simple join', async () => {
541+
it('simple join 1', async () => {
542542
await compiler.compile();
543543

544544
console.log(joinGraph.buildJoin(['visitor_checkins', 'visitors']));

rust/cubesqlplanner/cubesqlplanner/src/plan/aggregation.rs

-16
This file was deleted.

rust/cubesqlplanner/cubesqlplanner/src/plan/filter.rs

+10-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use super::Schema;
22
use crate::planner::filter::BaseFilter;
3+
use crate::planner::sql_templates::PlanSqlTemplates;
34
use crate::planner::VisitorContext;
45
use cubenativeutils::CubeError;
56
use std::fmt;
@@ -45,6 +46,7 @@ impl fmt::Display for FilterGroupOperator {
4546
impl FilterItem {
4647
pub fn to_sql(
4748
&self,
49+
templates: &PlanSqlTemplates,
4850
context: Rc<VisitorContext>,
4951
schema: Rc<Schema>,
5052
) -> Result<String, CubeError> {
@@ -54,13 +56,14 @@ impl FilterItem {
5456
let items_sql = group
5557
.items
5658
.iter()
57-
.map(|itm| itm.to_sql(context.clone(), schema.clone()))
59+
.map(|itm| itm.to_sql(templates, context.clone(), schema.clone()))
5860
.collect::<Result<Vec<_>, _>>()?;
59-
if items_sql.is_empty() {
60-
format!("( 1 = 1 )")
61+
let result = if items_sql.is_empty() {
62+
templates.always_true()?
6163
} else {
62-
format!("({})", items_sql.join(&operator))
63-
}
64+
items_sql.join(&operator)
65+
};
66+
format!("({})", result)
6467
}
6568
FilterItem::Item(item) => {
6669
let sql = item.to_sql(context.clone(), schema)?;
@@ -74,13 +77,14 @@ impl FilterItem {
7477
impl Filter {
7578
pub fn to_sql(
7679
&self,
80+
templates: &PlanSqlTemplates,
7781
context: Rc<VisitorContext>,
7882
schema: Rc<Schema>,
7983
) -> Result<String, CubeError> {
8084
let res = self
8185
.items
8286
.iter()
83-
.map(|itm| itm.to_sql(context.clone(), schema.clone()))
87+
.map(|itm| itm.to_sql(templates, context.clone(), schema.clone()))
8488
.collect::<Result<Vec<_>, _>>()?
8589
.join(" AND ");
8690
Ok(res)

rust/cubesqlplanner/cubesqlplanner/src/plan/from.rs

+1-4
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,7 @@ impl SingleAliasedSource {
7070
) -> Result<String, CubeError> {
7171
let sql = self.source.to_sql(templates, context)?;
7272

73-
Ok(format!(
74-
"{sql} AS {}",
75-
templates.quote_identifier(&self.alias)?
76-
))
73+
templates.query_aliased(&sql, &self.alias)
7774
}
7875

7976
pub fn make_schema(&self) -> Schema {

rust/cubesqlplanner/cubesqlplanner/src/plan/join.rs

+7-21
Original file line numberDiff line numberDiff line change
@@ -68,20 +68,7 @@ impl DimensionJoinCondition {
6868
dimension,
6969
schema.clone(),
7070
)?;
71-
let null_check = if self.null_check {
72-
format!(
73-
" OR ({} AND {})",
74-
templates.is_null_expr(&left_column, false)?,
75-
templates.is_null_expr(&right_column, false)?
76-
)
77-
} else {
78-
format!("")
79-
};
80-
81-
Ok(format!(
82-
"({} = {}{})",
83-
left_column, right_column, null_check
84-
))
71+
templates.join_by_dimension_conditions(&left_column, &right_column, self.null_check)
8572
}
8673

8774
fn resolve_member_alias(
@@ -157,14 +144,13 @@ impl JoinItem {
157144
context: Rc<VisitorContext>,
158145
schema: Rc<Schema>,
159146
) -> Result<String, CubeError> {
160-
let operator = if self.is_inner { "INNER" } else { "LEFT" };
161147
let on_sql = self.on.to_sql(templates, context.clone(), schema)?;
162-
Ok(format!(
163-
"{} JOIN {} ON {}",
164-
operator,
165-
self.from.to_sql(templates, context)?,
166-
on_sql
167-
))
148+
let result = templates.join(
149+
&self.from.to_sql(templates, context)?,
150+
&on_sql,
151+
self.is_inner,
152+
)?;
153+
Ok(result)
168154
}
169155
}
170156

rust/cubesqlplanner/cubesqlplanner/src/plan/select.rs

+65-71
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ use itertools::Itertools;
22

33
use super::{Cte, Expr, Filter, From, OrderBy, Schema, SchemaColumn};
44
use crate::planner::sql_templates::PlanSqlTemplates;
5+
use crate::planner::sql_templates::{
6+
TemplateGroupByColumn, TemplateOrderByColumn, TemplateProjectionColumn,
7+
};
58
use crate::planner::VisitorContext;
69
use cubenativeutils::CubeError;
710
use std::rc::Rc;
@@ -21,12 +24,14 @@ impl AliasedExpr {
2124
templates: &PlanSqlTemplates,
2225
context: Rc<VisitorContext>,
2326
schema: Rc<Schema>,
24-
) -> Result<String, CubeError> {
25-
Ok(format!(
26-
"{} {}",
27-
self.expr.to_sql(templates, context, schema)?,
28-
templates.quote_identifier(&self.alias)?
29-
))
27+
) -> Result<TemplateProjectionColumn, CubeError> {
28+
let expr = self.expr.to_sql(templates, context, schema)?;
29+
let aliased = templates.column_aliased(&expr, &self.alias)?;
30+
Ok(TemplateProjectionColumn {
31+
expr,
32+
alias: self.alias.clone(),
33+
aliased,
34+
})
3035
}
3136
}
3237

@@ -76,90 +81,79 @@ impl Select {
7681
.iter()
7782
.map(|p| p.to_sql(templates, self.context.clone(), schema.clone()))
7883
.collect::<Result<Vec<_>, _>>()?
79-
.join(", ")
8084
} else {
81-
format!(" * ")
85+
vec![TemplateProjectionColumn {
86+
expr: format!("*"),
87+
alias: format!(""),
88+
aliased: format!("*"),
89+
}]
8290
};
8391

8492
let where_condition = if let Some(filter) = &self.filter {
85-
format!(
86-
" WHERE {}",
87-
filter.to_sql(self.context.clone(), schema.clone())?
88-
)
93+
Some(filter.to_sql(templates, self.context.clone(), schema.clone())?)
8994
} else {
90-
format!("")
95+
None
9196
};
9297

93-
let group_by = if !self.group_by.is_empty() {
94-
let str = self
95-
.group_by
96-
.iter()
97-
.enumerate()
98-
.map(|(i, _)| format!("{}", i + 1))
99-
.join(", ");
100-
format!(" GROUP BY {}", str)
101-
} else {
102-
format!("")
103-
};
98+
let group_by = self
99+
.group_by
100+
.iter()
101+
.enumerate()
102+
.map(|(i, expr)| -> Result<_, CubeError> {
103+
let expr = expr.to_sql(templates, self.context.clone(), schema.clone())?;
104+
Ok(TemplateGroupByColumn { expr, index: i + 1 })
105+
})
106+
.collect::<Result<Vec<_>, _>>()?;
104107

105108
let having = if let Some(having) = &self.having {
106-
format!(
107-
" HAVING {}",
108-
having.to_sql(self.context.clone(), schema.clone())?
109-
)
109+
Some(having.to_sql(templates, self.context.clone(), schema.clone())?)
110110
} else {
111-
format!("")
111+
None
112112
};
113113

114-
let ctes = if !self.ctes.is_empty() {
115-
let ctes_sql = self
116-
.ctes
117-
.iter()
118-
.map(|cte| -> Result<_, CubeError> {
119-
Ok(format!(
120-
" {} as ({})",
121-
cte.name(),
122-
cte.query().to_sql(templates)?
123-
))
124-
})
125-
.collect::<Result<Vec<_>, _>>()?
126-
.join(",\n");
127-
format!("WITH\n{ctes_sql}\n")
128-
} else {
129-
"".to_string()
130-
};
114+
let ctes = self
115+
.ctes
116+
.iter()
117+
.map(|cte| -> Result<_, CubeError> {
118+
templates.cte(&cte.query().to_sql(templates)?, &cte.name().clone())
119+
})
120+
.collect::<Result<Vec<_>, _>>()?;
131121

132-
let order_by = if !self.order_by.is_empty() {
133-
let order_sql = self
134-
.order_by
135-
.iter()
136-
.map(|itm| format!("{} {}", itm.pos, itm.asc_str()))
137-
.collect::<Vec<_>>()
138-
.join(", ");
139-
format!(" ORDER BY {}", order_sql)
140-
} else {
141-
format!("")
142-
};
122+
let order_by = self
123+
.order_by
124+
.iter()
125+
.map(|itm| -> Result<_, CubeError> {
126+
let expr = templates.order_by(
127+
&itm.expr
128+
.to_sql(templates, self.context.clone(), schema.clone())?,
129+
Some(itm.pos),
130+
!itm.desc,
131+
)?;
132+
Ok(TemplateOrderByColumn { expr })
133+
})
134+
.collect::<Result<Vec<_>, _>>()?;
143135

144-
let distinct = if self.is_distinct { "DISTINCT " } else { "" };
145136
let from = self.from.to_sql(templates, self.context.clone())?;
146-
let limit = if let Some(limit) = self.limit {
147-
format!(" LIMIT {limit}")
148-
} else {
149-
format!("")
150-
};
151-
let offset = if let Some(offset) = self.offset {
152-
format!(" OFFSET {offset}")
153-
} else {
154-
format!("")
155-
};
156137

157-
let res = format!(
138+
let result = templates.select(
139+
ctes,
140+
&from,
141+
projection,
142+
where_condition,
143+
group_by,
144+
having,
145+
order_by,
146+
self.limit,
147+
self.offset,
148+
self.is_distinct,
149+
)?;
150+
151+
/* let res = format!(
158152
"{ctes}SELECT\
159153
\n {distinct}{projection}\
160154
\n FROM\
161155
\n{from}{where_condition}{group_by}{having}{order_by}{limit}{offset}",
162-
);
163-
Ok(res)
156+
); */
157+
Ok(result)
164158
}
165159
}
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
pub mod filter;
22
pub mod plan;
3+
pub mod structs;
34

45
pub use filter::FilterTemplates;
56
pub use plan::PlanSqlTemplates;
7+
pub use structs::{TemplateGroupByColumn, TemplateOrderByColumn, TemplateProjectionColumn};

0 commit comments

Comments
 (0)