diff --git a/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js b/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js index 25cc40dcf1f70..ec790486e983c 100644 --- a/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js +++ b/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js @@ -3217,8 +3217,8 @@ export class BaseQuery { '{% if filter %}\nWHERE {{ filter }}{% endif %}' + '{% if group_by %}\nGROUP BY {{ group_by }}{% endif %}' + '{% if order_by %}\nORDER BY {{ order_by | map(attribute=\'expr\') | join(\', \') }}{% endif %}' + - '{% if limit %}\nLIMIT {{ limit }}{% endif %}' + - '{% if offset %}\nOFFSET {{ offset }}{% endif %}', + '{% if limit is not none %}\nLIMIT {{ limit }}{% endif %}' + + '{% if offset is not none %}\nOFFSET {{ offset }}{% endif %}', group_by_exprs: '{{ group_by | map(attribute=\'index\') | join(\', \') }}', }, expressions: { diff --git a/packages/cubejs-schema-compiler/src/adapter/PrestodbQuery.ts b/packages/cubejs-schema-compiler/src/adapter/PrestodbQuery.ts index 6852f94e52f0a..35caf17e3a3db 100644 --- a/packages/cubejs-schema-compiler/src/adapter/PrestodbQuery.ts +++ b/packages/cubejs-schema-compiler/src/adapter/PrestodbQuery.ts @@ -116,8 +116,8 @@ export class PrestodbQuery extends BaseQuery { 'FROM (\n {{ from }}\n) AS {{ from_alias }} \n' + '{% if group_by %} GROUP BY {{ group_by }}{% endif %}' + '{% if order_by %} ORDER BY {{ order_by | map(attribute=\'expr\') | join(\', \') }}{% endif %}' + - '{% if offset %}\nOFFSET {{ offset }}{% endif %}' + - '{% if limit %}\nLIMIT {{ limit }}{% endif %}'; + '{% if offset is not none %}\nOFFSET {{ offset }}{% endif %}' + + '{% if limit is not none %}\nLIMIT {{ limit }}{% endif %}'; templates.expressions.extract = 'EXTRACT({{ date_part }} FROM {{ expr }})'; templates.expressions.interval_single_date_part = 'INTERVAL \'{{ num }}\' {{ date_part }}'; templates.expressions.timestamp_literal = 'from_iso8601_timestamp(\'{{ value }}\')'; diff --git a/packages/cubejs-testing/test/smoke-cubesql.test.ts b/packages/cubejs-testing/test/smoke-cubesql.test.ts index 2caf314734f91..d4a1e38673d78 100644 --- a/packages/cubejs-testing/test/smoke-cubesql.test.ts +++ b/packages/cubejs-testing/test/smoke-cubesql.test.ts @@ -390,6 +390,13 @@ describe('SQL API', () => { expect(res.rows).toEqual([]); }); + test('zero limited dimension aggregated queries through wrapper', async () => { + // Attempts to trigger query generation from SQL templates, not from Cube + const query = 'SELECT MIN(t.maxval) FROM (SELECT MAX(createdAt) as maxval FROM Orders LIMIT 10) t LIMIT 0'; + const res = await connection.query(query); + expect(res.rows).toEqual([]); + }); + test('select dimension agg where false', async () => { const query = 'SELECT MAX("createdAt") AS "max" FROM "BigOrders" WHERE 1 = 0'; diff --git a/rust/cubesql/cubesql/src/compile/mod.rs b/rust/cubesql/cubesql/src/compile/mod.rs index ee46ecf28abc9..011106e977c34 100644 --- a/rust/cubesql/cubesql/src/compile/mod.rs +++ b/rust/cubesql/cubesql/src/compile/mod.rs @@ -15455,8 +15455,8 @@ ORDER BY "source"."str0" ASC r#"SELECT {{ select_concat | map(attribute='aliased') | join(', ') }} FROM ({{ from }}) AS {{ from_alias }} {% if group_by %} GROUP BY {{ group_by | map(attribute='index') | join(', ') }}{% endif %} -{% if order_by %} ORDER BY {{ order_by | map(attribute='expr') | join(', ') }}{% endif %}{% if offset %} -OFFSET {{ offset }}{% endif %}{% if limit %} +{% if order_by %} ORDER BY {{ order_by | map(attribute='expr') | join(', ') }}{% endif %}{% if offset is not none %} +OFFSET {{ offset }}{% endif %}{% if limit is not none %} LIMIT {{ limit }}{% endif %}"#.to_string(), ), ] diff --git a/rust/cubesql/cubesql/src/compile/test/mod.rs b/rust/cubesql/cubesql/src/compile/test/mod.rs index a565563cf89c7..ae39eb9fbce38 100644 --- a/rust/cubesql/cubesql/src/compile/test/mod.rs +++ b/rust/cubesql/cubesql/src/compile/test/mod.rs @@ -506,8 +506,8 @@ FROM ( ) AS {{ from_alias }} {% endif %} {% if filter %} WHERE {{ filter }}{% endif %}{% if group_by %} GROUP BY {{ group_by }}{% endif %}{% if order_by %} -ORDER BY {{ order_by | map(attribute='expr') | join(', ') }}{% endif %}{% if limit %} -LIMIT {{ limit }}{% endif %}{% if offset %} +ORDER BY {{ order_by | map(attribute='expr') | join(', ') }}{% endif %}{% if limit is not none %} +LIMIT {{ limit }}{% endif %}{% if offset is not none %} OFFSET {{ offset }}{% endif %}"#.to_string(), ), ( diff --git a/rust/cubesql/cubesql/src/compile/test/test_wrapper.rs b/rust/cubesql/cubesql/src/compile/test/test_wrapper.rs index 1eb78f92fa5e6..a133391646e65 100644 --- a/rust/cubesql/cubesql/src/compile/test/test_wrapper.rs +++ b/rust/cubesql/cubesql/src/compile/test/test_wrapper.rs @@ -964,3 +964,40 @@ async fn test_case_wrapper_escaping() { // Expect 6 backslashes as output is JSON and it's escaped one more time .contains("\\\\\\\\\\\\`")); } + +/// Test that WrappedSelect(... limit=Some(0) ...) will render it correctly +#[tokio::test] +async fn test_wrapper_limit_zero() { + if !Rewriter::sql_push_down_enabled() { + return; + } + init_testing_logger(); + + let query_plan = convert_select_to_query_plan( + // language=PostgreSQL + r#" + SELECT + MIN(t.a) + FROM ( + SELECT + MAX(order_date) AS a + FROM + KibanaSampleDataEcommerce + LIMIT 10 + ) t LIMIT 0 + "# + .to_string(), + DatabaseProtocol::PostgreSQL, + ) + .await; + + let logical_plan = query_plan.as_logical_plan(); + assert!(logical_plan + .find_cube_scan_wrapper() + .wrapped_sql + .unwrap() + .sql + .contains("LIMIT 0")); + + let _physical_plan = query_plan.as_physical_plan().await.unwrap(); +}