Skip to content

Commit 666c0ad

Browse files
committed
fix(cubesql): Fix NULLS FIRST/LAST SQL push down for several dialects
1 parent 5827244 commit 666c0ad

File tree

5 files changed

+42
-1
lines changed

5 files changed

+42
-1
lines changed

packages/cubejs-pinot-driver/src/PinotQuery.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ export class PinotQuery extends BaseQuery {
149149
'{% if limit %}\nLIMIT {{ limit }}{% endif %}';
150150
templates.expressions.extract = 'EXTRACT({{ date_part }} FROM {{ expr }})';
151151
templates.expressions.timestamp_literal = `fromDateTime('{{ value }}', ${DATE_TIME_FORMAT})`;
152+
templates.expressions.sort = '{{ expr }} IS NULL {% if nulls_first %}DESC{% else %}ASC{% endif %}, {{ expr }} {% if asc %}ASC{% else %}DESC{% endif %}';
152153
templates.quotes.identifiers = '"';
153154
delete templates.types.time;
154155
delete templates.types.interval;

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3230,7 +3230,7 @@ export class BaseQuery {
32303230
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',
32313231
is_null: '{{ expr }} IS {% if negate %}NOT {% endif %}NULL',
32323232
binary: '({{ left }} {{ op }} {{ right }})',
3233-
sort: '{{ expr }} {% if asc %}ASC{% else %}DESC{% endif %}{% if nulls_first %} NULLS FIRST{% endif %}',
3233+
sort: '{{ expr }} {% if asc %}ASC{% else %}DESC{% endif %} NULLS {% if nulls_first %}FIRST{% else %}LAST{% endif %}',
32343234
cast: 'CAST({{ expr }} AS {{ data_type }})',
32353235
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 %})',
32363236
window_frame_bounds: '{{ frame_type }} BETWEEN {{ frame_start }} AND {{ frame_end }}',

packages/cubejs-schema-compiler/src/adapter/MssqlQuery.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,7 @@ export class MssqlQuery extends BaseQuery {
224224
templates.functions.LEAST = 'LEAST({{ args_concat }})';
225225
templates.functions.GREATEST = 'GREATEST({{ args_concat }})';
226226
delete templates.expressions.ilike;
227+
templates.expressions.sort = '{{ expr }} IS NULL {% if nulls_first %}DESC{% else %}ASC{% endif %}, {{ expr }} {% if asc %}ASC{% else %}DESC{% endif %}';
227228
templates.types.string = 'VARCHAR';
228229
templates.types.boolean = 'BIT';
229230
templates.types.integer = 'INT';

packages/cubejs-schema-compiler/src/adapter/MysqlQuery.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ export class MysqlQuery extends BaseQuery {
158158
const templates = super.sqlTemplates();
159159
templates.quotes.identifiers = '`';
160160
templates.quotes.escape = '\\`';
161+
templates.expressions.sort = '{{ expr }} IS NULL {% if nulls_first %}DESC{% else %}ASC{% endif %}, {{ expr }} {% if asc %}ASC{% else %}DESC{% endif %}';
161162
delete templates.expressions.ilike;
162163
templates.types.string = 'VARCHAR';
163164
templates.types.boolean = 'TINYINT';

rust/cubesql/cubesql/src/compile/mod.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16251,4 +16251,42 @@ LIMIT {{ limit }}{% endif %}"#.to_string(),
1625116251
displayable(physical_plan.as_ref()).indent()
1625216252
);
1625316253
}
16254+
16255+
#[tokio::test]
16256+
async fn test_mysql_nulls_last() {
16257+
if !Rewriter::sql_push_down_enabled() {
16258+
return;
16259+
}
16260+
init_testing_logger();
16261+
16262+
let query_plan = convert_select_to_query_plan_customized(
16263+
"
16264+
SELECT customer_gender AS s
16265+
FROM KibanaSampleDataEcommerce AS k
16266+
WHERE LOWER(customer_gender) = 'test'
16267+
GROUP BY 1
16268+
ORDER BY 1 DESC
16269+
"
16270+
.to_string(),
16271+
DatabaseProtocol::PostgreSQL,
16272+
vec![
16273+
("expressions/sort".to_string(), "{{ expr }} IS NULL {% if nulls_first %}DESC{% else %}ASC{% endif %}, {{ expr }} {% if asc %}ASC{% else %}DESC{% endif %}".to_string()),
16274+
]
16275+
)
16276+
.await;
16277+
16278+
let physical_plan = query_plan.as_physical_plan().await.unwrap();
16279+
println!(
16280+
"Physical plan: {}",
16281+
displayable(physical_plan.as_ref()).indent()
16282+
);
16283+
16284+
let logical_plan = query_plan.as_logical_plan();
16285+
let sql = logical_plan
16286+
.find_cube_scan_wrapper()
16287+
.wrapped_sql
16288+
.unwrap()
16289+
.sql;
16290+
assert!(sql.contains(" IS NULL DESC, "));
16291+
}
1625416292
}

0 commit comments

Comments
 (0)