Skip to content

Commit e78dd05

Browse files
KSDaemonigorlukanin
authored andcommitted
fix(schema-compiler): Allow calling cube functions in yaml python blocks (cube-js#9473)
* fix(schema-compiler): Allow calling cube functions in yaml python blocks * regenerate antlr * exclude antlr auto generated code from coverage * add tests * Add docs and YAML examples * some fixes in tests --------- Co-authored-by: Igor Lukanin <[email protected]>
1 parent 9d5f4c8 commit e78dd05

File tree

15 files changed

+2253
-1404
lines changed

15 files changed

+2253
-1404
lines changed

docs/pages/guides/recipes/analytics/event-analytics.mdx

Lines changed: 419 additions & 17 deletions
Large diffs are not rendered by default.

docs/pages/guides/recipes/data-modeling/snapshots.mdx

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ First, we need to generate a range with all dates of interest, from the earliest
7575
to the latest. Second, we need to join the dates with the statuses and leave
7676
only the most recent statuses to date.
7777

78+
<CodeTabs>
79+
7880
```javascript
7981
cube(`status_snapshots`, {
8082
extends: statuses,
@@ -111,6 +113,39 @@ cube(`status_snapshots`, {
111113
});
112114
```
113115

116+
```yaml
117+
cubes:
118+
- name: status_snapshots
119+
extends: statuses
120+
sql: >
121+
-- Create a range from the earlist date to the latest date
122+
WITH range AS (
123+
SELECT date
124+
FROM GENERATE_SERIES(
125+
(SELECT MIN(changed_at) FROM {statuses.sql()} AS statuses),
126+
(SELECT MAX(changed_at) FROM {statuses.sql()} AS statuses),
127+
INTERVAL '1 DAY'
128+
) AS date
129+
)
130+
131+
-- Calculate snapshots for every date in the range
132+
SELECT range.date, statuses.*
133+
FROM range
134+
LEFT JOIN {statuses.sql()} AS statuses
135+
ON range.date >= statuses.changed_at
136+
AND statuses.changed_at = (
137+
SELECT MAX(changed_at)
138+
FROM {statuses.sql()} AS sub_statuses
139+
WHERE sub_statuses.order_id = statuses.order_id
140+
)
141+
dimensions:
142+
date:
143+
sql: date
144+
type: time
145+
```
146+
147+
</CodeTabs>
148+
114149
<InfoBox>
115150
116151
To generate a range of dates, here we use the

docs/pages/product/data-modeling/concepts/data-blending.mdx

Lines changed: 58 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,8 @@ cube(`online_orders`, {
128128

129129
Given the above cubes, a data blending cube can be introduced as follows:
130130

131+
<CodeTabs>
132+
131133
```javascript
132134
cube(`all_sales`, {
133135
sql: `
@@ -136,14 +138,14 @@ cube(`all_sales`, {
136138
user_id AS customer_id,
137139
created_at,
138140
'online' AS row_type
139-
FROM (${online_orders.sql()}) AS online
141+
FROM ${online_orders.sql()} AS online
140142
UNION ALL
141143
SELECT
142144
amount,
143145
customer_id,
144146
created_at,
145147
'retail' AS row_type
146-
FROM (${retail_orders.sql()}) AS retail
148+
FROM ${retail_orders.sql()} AS retail
147149
`,
148150

149151
measures: {
@@ -193,16 +195,63 @@ cube(`all_sales`, {
193195
});
194196
```
195197

196-
<ReferenceBox>
198+
```yaml
199+
cubes:
200+
- name: all_sales
201+
sql: >
202+
SELECT
203+
amount,
204+
user_id AS customer_id,
205+
created_at,
206+
'online' AS row_type
207+
FROM {online_orders.sql()} AS online
208+
UNION ALL
209+
SELECT
210+
amount,
211+
customer_id,
212+
created_at,
213+
'retail' AS row_type
214+
FROM {retail_orders.sql()} AS retail
215+
216+
measures:
217+
- name: customer_count
218+
sql: customer_id
219+
type: count_distinct
220+
221+
- name: revenue
222+
sql: amount
223+
type: sum
224+
225+
- name: online_revenue
226+
sql: amount
227+
type: sum
228+
filters:
229+
- sql: "{CUBE}.row_type = 'online'"
197230

198-
Currently, [`{cube.sql()}` function][ref-cube-sql-func] is not supported in YAML data models.
199-
Please [track this issue](https://github.com/cube-js/cube/issues/7484).
231+
- name: offline_revenue
232+
sql: amount
233+
type: sum
234+
filters:
235+
- sql: "{CUBE}.row_type = 'retail'"
200236

201-
As a workaround, you can use JavaScript data models, put a SQL query in a
202-
[Jinja](/product/data-modeling/dynamic/jinja#jinja) variable, or load it from
203-
the [template context](/reference/python/cube#templatecontext-class).
237+
- name: online_revenue_percentage
238+
sql: >
239+
{online_revenue} /
240+
NULLIF({online_revenue} + {offline_revenue}, 0)
241+
type: number
242+
format: percent
204243

205-
</ReferenceBox>
244+
dimensions:
245+
- name: created_at
246+
sql: created_at
247+
type: time
248+
249+
- name: revenue_type
250+
sql: row_type
251+
type: string
252+
```
253+
254+
</CodeTabs>
206255
207256
Another use case of the Data Blending approach would be when you want to chart
208257
some measures (business related) together and see how they correlate.

docs/pages/product/data-modeling/concepts/polymorphic-cubes.mdx

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ cube(`users`, {
9090

9191
Then you can derive the `teachers` and `students` cubes from `users`:
9292

93+
<CodeTabs>
94+
9395
```javascript
9496
cube(`teachers`, {
9597
extends: users,
@@ -110,16 +112,24 @@ cube(`students`, {
110112
});
111113
```
112114

113-
<ReferenceBox>
114-
115-
Currently, [`{cube.sql()}` function][ref-cube-sql-func] is not supported in YAML data models.
116-
Please [track this issue](https://github.com/cube-js/cube/issues/7484).
117-
118-
As a workaround, you can use JavaScript data models, put a SQL query in a
119-
[Jinja](/product/data-modeling/dynamic/jinja#jinja) variable, or load it from
120-
the [template context](/reference/python/cube#templatecontext-class).
115+
```yaml
116+
cubes:
117+
- name: teachers
118+
extends: users
119+
sql: >
120+
SELECT *
121+
FROM {users.sql()}
122+
WHERE type = 'teacher'
123+
124+
- name: students
125+
extends: users
126+
sql: >
127+
SELECT *
128+
FROM {users.sql()}
129+
WHERE type = 'student'
130+
```
121131
122-
</ReferenceBox>
132+
</CodeTabs>
123133
124134
Once we have those cubes, we can define correct joins from the `lessons` cube:
125135

docs/pages/product/data-modeling/syntax.mdx

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ model
3838
Cube supports two ways to define data model files: with [YAML][wiki-yaml] or
3939
JavaScript syntax. YAML data model files should have the `.yml` extension,
4040
whereas JavaScript data model files should end with `.js`. You can mix YAML and
41-
JavaScript files within a single data model.
41+
JavaScript files within a single data model.
4242

4343
<CodeTabs>
4444

@@ -547,6 +547,8 @@ when defining [polymorphic cubes][ref-polymorphism] or using [data blending][ref
547547

548548
Consider the following data model:
549549

550+
<CodeTabs>
551+
550552
```javascript
551553
cube(`organisms`, {
552554
sql_table: `organisms`
@@ -564,7 +566,7 @@ cube(`dogs`, {
564566
sql: `
565567
SELECT *
566568
FROM ${animals.sql()}
567-
WHERE species = 'dogs'
569+
WHERE species = 'dogs'
568570
`,
569571

570572
measures: {
@@ -575,6 +577,30 @@ cube(`dogs`, {
575577
})
576578
```
577579

580+
```yaml
581+
cubes:
582+
- name: organisms
583+
sql_table: organisms
584+
585+
- name: animals
586+
sql: >
587+
SELECT *
588+
FROM {organisms.sql()}
589+
WHERE kingdom = 'animals'
590+
591+
- name: dogs
592+
sql: >
593+
SELECT *
594+
FROM {animals.sql()}
595+
WHERE species = 'dogs'
596+
597+
measures:
598+
- name: count
599+
type: count
600+
```
601+
602+
</CodeTabs>
603+
578604
If you query for `dogs.count`, Cube will generate the following SQL:
579605

580606
```sql
@@ -590,18 +616,6 @@ FROM (
590616
) AS "dogs"
591617
```
592618

593-
<ReferenceBox>
594-
595-
Currently, `{cube.sql()}` function is only supported in JavaScript data models.
596-
It is not supported in YAML data models.
597-
Please [track this issue](https://github.com/cube-js/cube/issues/7484).
598-
599-
As a workaround, you can use JavaScript data models, put a SQL query in a
600-
[Jinja](/product/data-modeling/dynamic/jinja#jinja) variable, or load it from
601-
the [template context](/reference/python/cube#templatecontext-class).
602-
603-
</ReferenceBox>
604-
605619
### Curly braces and escaping
606620

607621
As you can see in the examples above, within [SQL expressions][self-sql-expressions],
@@ -761,4 +775,4 @@ defining dynamic data models.
761775
[ref-style-guide]: /guides/style-guide
762776
[ref-polymorphism]: /product/data-modeling/concepts/polymorphic-cubes
763777
[ref-data-blending]: /product/data-modeling/concepts/data-blending
764-
[link-js-template-literals]: https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Scripting/Strings#embedding_javascript
778+
[link-js-template-literals]: https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Scripting/Strings#embedding_javascript

docs/pages/reference/data-model/cube.mdx

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -269,18 +269,34 @@ cubes:
269269
270270
</CodeTabs>
271271
272-
With JavaScript models, you can also reference other cubes' SQL statements for
273-
code reuse:
272+
You can also reference other cubes' SQL statements for code reuse using the
273+
[`{cube.sql()}`][ref-syntax-cube-sql] function:
274+
275+
<CodeTabs>
274276

275277
```javascript
276278
cube(`companies`, {
277279
sql: `
278-
SELECT users.company_name, users.company_id
280+
SELECT
281+
users.company_name,
282+
users.company_id
279283
FROM ${users.sql()} AS users
280284
`,
281285
});
282286
```
283287

288+
```yaml
289+
cubes:
290+
- name: companies
291+
sql:
292+
SELECT
293+
users.company_name,
294+
users.company_id
295+
FROM {users.sql()} AS users
296+
```
297+
298+
</CodeTabs>
299+
284300
It is recommended to prefer the [`sql_table`](#parameters-sql-table) parameter
285301
over the `sql` parameter for all cubes that are supposed to use queries like
286302
this: `SELECT * FROM table`.
@@ -626,4 +642,5 @@ The `access_policy` parameter is used to configure [data access policies][ref-re
626642
[ref-ref-segments]: /reference/data-model/segments
627643
[ref-ref-joins]: /reference/data-model/joins
628644
[ref-ref-pre-aggs]: /reference/data-model/pre-aggregations
629-
[ref-ref-dap]: /reference/data-model/data-access-policies
645+
[ref-ref-dap]: /reference/data-model/data-access-policies
646+
[ref-syntax-cube-sql]: /product/data-modeling/syntax#cubesql-function

packages/cubejs-schema-compiler/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,9 @@
9898
"coverageDirectory": "coverage/",
9999
"collectCoverageFrom": [
100100
"dist/src/**/*.js",
101-
"dist/src/**/*.ts"
101+
"dist/src/**/*.ts",
102+
"!dist/src/parser/GenericSql*.js",
103+
"!dist/src/parser/Python3*.js"
102104
],
103105
"coveragePathIgnorePatterns": [
104106
".*\\.d\\.ts"

packages/cubejs-schema-compiler/src/parser/Python3Parser.g4

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ testlist_comp: (test | star_expr) (
228228
comp_for
229229
| (COMMA (test | star_expr))* (COMMA)?
230230
);
231-
trailer: '(' (arglist)? ')' | '[' subscriptlist ']' | '.' NAME;
231+
trailer: callArguments | '[' subscriptlist ']' | '.' NAME;
232232
subscriptlist: subscript (COMMA subscript)* (COMMA)?;
233233
subscript: test | (test)? ':' (test)? (sliceop)?;
234234
sliceop: ':' (test)?;
@@ -251,6 +251,8 @@ dictorsetmaker: (
251251

252252
classdef: 'class' NAME ('(' (arglist)? ')')? ':' suite;
253253

254+
callArguments: '(' (arglist)? ')';
255+
254256
arglist: argument (COMMA argument)* (COMMA)?;
255257

256258
// The reason that keywords are test nodes instead of NAME is that using NAME results in an

packages/cubejs-schema-compiler/src/parser/Python3Parser.interp

Lines changed: 2 additions & 1 deletion
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)