Skip to content

Commit 99f0cb0

Browse files
refactors; extend unit tests; update docs
1 parent 00214ec commit 99f0cb0

3 files changed

Lines changed: 90 additions & 11 deletions

File tree

docs/concepts/macros/sqlmesh_macros.md

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -855,7 +855,9 @@ FROM foo
855855

856856
`@UNION` returns a `UNION` query that selects all columns with matching names and data types from the tables.
857857

858-
Its first argument is the `UNION` "type", `'DISTINCT'` (removing duplicated rows) or `'ALL'` (returning all rows). Subsequent arguments are the tables to be combined.
858+
Its first argument can be either a condition or the `UNION` "type". If the first argument evaluates to a boolean (`TRUE` or `FALSE`), it's treated as a condition. If the condition is `FALSE`, only the first table is returned. If it's `TRUE`, the union operation is performed.
859+
860+
If the first argument is not a boolean condition, it's treated as the `UNION` "type": either `'DISTINCT'` (removing duplicated rows) or `'ALL'` (returning all rows). Subsequent arguments are the tables to be combined.
859861

860862
Let's assume that:
861863

@@ -882,6 +884,47 @@ SELECT
882884
FROM bar
883885
```
884886

887+
If the union type is omitted, `'ALL'` is used as the default. So the following expression:
888+
889+
```sql linenums="1"
890+
@UNION(foo, bar)
891+
```
892+
893+
would be rendered as:
894+
895+
```sql linenums="1"
896+
SELECT
897+
CAST(a AS INT) AS a,
898+
CAST(c AS TEXT) AS c
899+
FROM foo
900+
UNION ALL
901+
SELECT
902+
CAST(a AS INT) AS a,
903+
CAST(c AS TEXT) AS c
904+
FROM bar
905+
```
906+
907+
You can also use a condition to control whether the union happens:
908+
909+
```sql linenums="1"
910+
@UNION(1 > 0, 'all', foo, bar)
911+
```
912+
913+
This would render the same as above. However, if the condition is `FALSE`:
914+
915+
```sql linenums="1"
916+
@UNION(1 > 2, 'all', foo, bar)
917+
```
918+
919+
Only the first table would be selected:
920+
921+
```sql linenums="1"
922+
SELECT
923+
CAST(a AS INT) AS a,
924+
CAST(c AS TEXT) AS c
925+
FROM foo
926+
```
927+
885928
### @HAVERSINE_DISTANCE
886929

887930
`@HAVERSINE_DISTANCE` returns the [haversine distance](https://en.wikipedia.org/wiki/Haversine_formula) between two geographic points.

sqlmesh/core/macros.py

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -994,33 +994,29 @@ def union(
994994
"""
995995

996996
if not args:
997-
raise SQLMeshError("At least one table is required for UNION.")
997+
raise SQLMeshError("At least one table is required for the @UNION macro.")
998998

999999
arg_idx = 0
10001000
# Check for condition
10011001
condition = evaluator.eval_expression(args[arg_idx])
10021002
if isinstance(condition, bool):
10031003
arg_idx += 1
10041004
if arg_idx >= len(args):
1005-
raise SQLMeshError("Expected more arguments after condition.")
1005+
raise SQLMeshError("Expected more arguments after the condition of the `@UNION` macro.")
10061006

10071007
# Check for union type
10081008
type_ = exp.Literal.string("ALL")
1009-
if arg_idx < len(args) and isinstance(args[arg_idx], exp.Literal):
1009+
if isinstance(args[arg_idx], exp.Literal):
10101010
type_ = args[arg_idx] # type: ignore
10111011
arg_idx += 1
10121012
kind = type_.name.upper()
10131013
if kind not in ("ALL", "DISTINCT"):
10141014
raise SQLMeshError(f"Invalid type '{type_}'. Expected 'ALL' or 'DISTINCT'.")
10151015

10161016
# Remaining args should be tables
1017-
tables = []
1018-
for table in args[arg_idx:]:
1019-
if isinstance(table, exp.Column):
1020-
table = exp.table_(table.this, db=table.args.get("table"), catalog=table.args.get("db"))
1021-
else:
1022-
table = exp.to_table(table, dialect=evaluator.dialect) # type: ignore
1023-
tables.append(table)
1017+
tables = [
1018+
exp.to_table(e.sql(evaluator.dialect), dialect=evaluator.dialect) for e in args[arg_idx:]
1019+
]
10241020

10251021
columns = {
10261022
column

tests/core/test_model.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,46 @@ def test_model_union_query(sushi_context, assert_exp_eq):
318318
2,
319319
lambda expected_select: f"{expected_select}\nUNION ALL\n{expected_select}\n",
320320
),
321+
# Test case 7: Missing union type AND condition
322+
(
323+
"test_7",
324+
"",
325+
"",
326+
2,
327+
lambda expected_select: f"{expected_select}\nUNION ALL\n{expected_select}\n",
328+
),
329+
# Test case 8: Missing union type AND condition multiple tables
330+
(
331+
"test_8",
332+
"",
333+
"",
334+
3,
335+
lambda expected_select: f"{expected_select}\nUNION ALL\n{expected_select}\n\nUNION ALL\n{expected_select}\n",
336+
),
337+
# Test case 9: Missing union type AND condition one table
338+
(
339+
"test_9",
340+
"",
341+
"",
342+
1,
343+
lambda expected_select: f"{expected_select}",
344+
),
345+
# Test case 10: Union type with one table
346+
(
347+
"test_10",
348+
"",
349+
"'distinct'",
350+
1,
351+
lambda expected_select: f"{expected_select}",
352+
),
353+
# Test case 11: Condition with one table
354+
(
355+
"test_9",
356+
"True",
357+
"",
358+
1,
359+
lambda expected_select: f"{expected_select}",
360+
),
321361
],
322362
)
323363
def test_model_union_conditional(

0 commit comments

Comments
 (0)