Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/drivers-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,8 @@ jobs:
use_tesseract_sql_planner: true
- database: clickhouse
use_tesseract_sql_planner: true
- database: mssql
use_tesseract_sql_planner: true
fail-fast: false

steps:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ snapshot during the window.
Navigate to <Btn>Admin → Settings → Maintenance Window</Btn> in the Cube Cloud
console.

<Screenshot src="https://lgo0ecceic.ucarecd.net/2ce65ee8-4041-435f-8d74-a84b1b9d6d53/" />
<Screenshot src="https://lgo0ecceic.ucarecd.net/e6db33a7-0d5e-4e28-8494-554393eaf2cb/" />

1. Toggle **Enable Scheduled Maintenance Window** to on.
2. Select the **Day of week** (e.g., Sunday).
Expand Down Expand Up @@ -95,6 +95,27 @@ revert to an older version, contact support.

</WarningBox>

## Upgrade now

If a critical fix has been released and you don't want to wait for the
next scheduled maintenance window, you can apply the latest eligible
snapshot immediately.

When the maintenance window is enabled, an **Upgrade Now** section
appears at the bottom of the settings page. Click **Upgrade Now** to
advance to the latest eligible snapshot right away.

The button is disabled while the upgrade is in progress. Once complete,
the new version will appear in the **Active Version** dropdown.

<InfoBox>

The same eligibility rules apply — only snapshots that are at least
24 hours old are considered. If you are already on the latest eligible
snapshot, the button will return an error.

</InfoBox>

## FAQ

### What happens if I disable the maintenance window?
Expand Down Expand Up @@ -122,4 +143,10 @@ Yes. When the maintenance window is enabled, the settings page shows an
Select any of them to switch back. See [Active version](#active-version)
above for details.

### Can I trigger an immediate upgrade without waiting for the window?

Yes. Use the **Upgrade Now** button on the settings page to apply the
latest eligible snapshot immediately. See [Upgrade now](#upgrade-now)
above for details.

[ref-dedicated]: /product/deployment/cloud/deployment-types#production-cluster
37 changes: 34 additions & 3 deletions packages/cubejs-schema-compiler/src/adapter/MssqlQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -268,11 +268,15 @@ export class MssqlQuery extends BaseQuery {
const templates = super.sqlTemplates();
templates.functions.LEAST = 'LEAST({{ args_concat }})';
templates.functions.GREATEST = 'GREATEST({{ args_concat }})';
// MSSQL ROUND requires 2 arguments: ROUND(number, length)
templates.functions.ROUND = 'ROUND({{ args_concat }}{% if args | length < 2 %}, 0{% endif %})';
// NOTE: MSSQL does not support DISTINCT clause. No workaround is available
delete templates.functions.STRING_AGG;
// PERCENTILE_CONT works but requires PARTITION BY
delete templates.functions.PERCENTILECONT;
delete templates.expressions.ilike;
// MSSQL uses + for string concatenation instead of ||
templates.expressions.concat_strings = '{{ strings | join(\' + \' ) }}';
// NOTE: this template contains a comma; two order expressions are being generated
templates.expressions.sort = '{{ expr }} IS NULL {% if nulls_first %}DESC{% else %}ASC{% endif %}, {{ expr }} {% if asc %}ASC{% else %}DESC{% endif %}';
templates.types.string = 'VARCHAR';
Expand All @@ -298,11 +302,37 @@ export class MssqlQuery extends BaseQuery {
'{% if not loop.last %}, {% endif %}' +
'{% endfor %}' +
') AS dates (date_from, date_to)';
// MSSQL uses recursive CTE for time series generation.
// The template body becomes content of `time_series AS (...)` CTE,
// so it self-references `time_series` for recursion.
templates.statements.generated_time_series_select =
'SELECT CAST({{ start }} AS DATETIME2) AS date_from,\n' +
' DATEADD(MILLISECOND, -1, DATEADD({{ minimal_time_unit }}, 1, CAST({{ start }} AS DATETIME2))) AS date_to\n' +
'UNION ALL\n' +
'SELECT DATEADD({{ minimal_time_unit }}, 1, date_from),\n' +
' DATEADD(MILLISECOND, -1, DATEADD({{ minimal_time_unit }}, 1, DATEADD({{ minimal_time_unit }}, 1, date_from)))\n' +
'FROM time_series\n' +
'WHERE DATEADD({{ minimal_time_unit }}, 1, date_from) <= CAST({{ end }} AS DATETIME2)';

templates.statements.generated_time_series_with_cte_range_source =
'SELECT {{ range_source }}.{{ min_name }} AS date_from,\n' +
' DATEADD(MILLISECOND, -1, DATEADD({{ minimal_time_unit }}, 1, {{ range_source }}.{{ min_name }})) AS date_to,\n' +
' {{ range_source }}.{{ max_name }} AS max_date\n' +
'FROM {{ range_source }}\n' +
'UNION ALL\n' +
'SELECT DATEADD({{ minimal_time_unit }}, 1, date_from),\n' +
' DATEADD(MILLISECOND, -1, DATEADD({{ minimal_time_unit }}, 1, DATEADD({{ minimal_time_unit }}, 1, date_from))),\n' +
' max_date\n' +
'FROM time_series\n' +
'WHERE DATEADD({{ minimal_time_unit }}, 1, date_from) <= max_date';

// MSSQL uses OFFSET/FETCH instead of LIMIT/OFFSET
templates.tesseract.ilike = 'LOWER({{ expr }}) {% if negated %}NOT {% endif %}LIKE LOWER({{ pattern }})';
templates.filters.like_pattern = 'CONCAT({% if start_wild %}\'%\'{% else %}\'\'{% endif %}, LOWER({{ value }}), {% if end_wild %}\'%\'{% else %}\'\'{% endif %})';
templates.statements.select = '{% if ctes %} WITH \n' +
'{{ ctes | join(\',\n\') }}\n' +
'{% endif %}' +
'SELECT {% if distinct %}DISTINCT {% endif %}' +
'SELECT {% if limit is not none and not order_by %}TOP {{ limit }} {% endif %}{% if distinct %}DISTINCT {% endif %}' +
'{{ select_concat | map(attribute=\'aliased\') | join(\', \') }} {% if from %}\n' +
'FROM (\n' +
'{{ from | indent(2, true) }}\n' +
Expand All @@ -312,8 +342,9 @@ export class MssqlQuery extends BaseQuery {
'{% if filter %}\nWHERE {{ filter }}{% endif %}' +
'{% if group_by %}\nGROUP BY {{ group_by }}{% endif %}' +
'{% if having %}\nHAVING {{ having }}{% endif %}' +
'{% if order_by %}\nORDER BY {{ order_by | map(attribute=\'expr\') | join(\', \') }}\nOFFSET {% if offset is not none %}{{ offset }}{% else %}0{% endif %} ROWS{% endif %}' +
'{% if limit is not none %}\nFETCH NEXT {{ limit }} ROWS ONLY{% endif %}';
'{% if order_by %}\nORDER BY {{ order_by | map(attribute=\'expr\') | join(\', \') }}\nOFFSET {% if offset is not none %}{{ offset }}{% else %}0{% endif %} ROWS' +
'\nFETCH NEXT {% if limit is not none %}{{ limit }}{% else %}2147483647{% endif %} ROWS ONLY{% endif %}' +
'{% if ctes %}\nOPTION (MAXRECURSION 0){% endif %}';
return templates;
}
}
2 changes: 1 addition & 1 deletion packages/cubejs-testing-drivers/fixtures/_schemas.json
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,7 @@
{
"name": "percentageOfTotalForStatus",
"type": "number",
"sql": "ROUND(100 * {totalProfit} / NULLIF({totalProfitForStatus}, 0))",
"sql": "ROUND(100 * {totalProfit} / NULLIF({totalProfitForStatus}, 0), 0)",
"multi_stage": true
},
{
Expand Down
69 changes: 69 additions & 0 deletions packages/cubejs-testing-drivers/fixtures/mssql.json
Original file line number Diff line number Diff line change
Expand Up @@ -183,5 +183,74 @@
"---------------------------------------",
"SQL API: Rolling Window YTD (year + month + day + date_trunc equal)",
"SQL API: Rolling Window YTD (year + month + day + date_trunc IN)"
],
"tesseractSkip": [
"---------------------------------------",
"SKIPPED FOR MS SQL (total not supported)",
"---------------------------------------",
"querying Customers: dimensions + total",
"querying Customers: dimensions + order + limit + total",
"querying Customers: dimensions + order + total + offset",
"querying Customers: dimensions + order + limit + total + offset",
"querying Products: dimensions + order + total",
"querying Products: dimensions + order + limit + total",
"querying ECommerce: dimensions + total",
"querying ECommerce: dimensions + order + limit + total",
"querying ECommerce: dimensions + order + total + offset",
"querying ECommerce: dimensions + order + limit + total + offset",

"---------------------------------------",
"SKIPPED FOR ALL ",
"---------------------------------------",
"querying Products: dimensions -- doesn't work wo ordering",
"querying ECommerce: total quantity, avg discount, total sales, total profit by product + order + total -- rounding in athena",
"querying ECommerce: total sales, total profit by month + order (date) + total -- doesn't work with the BigQuery",
"querying ECommerce: total quantity, avg discount, total sales, total profit by product + order + total -- noisy test",
"querying BigECommerce: null sum",
"querying BigECommerce: null boolean",
"---------------------------------------",
"SKIPPED SQL API (Need work)",
"---------------------------------------",
"SQL API: reuse params",
"SQL API: post-aggregate percentage of total",
"SQL API: powerbi min max ungrouped flag",
"SQL API: powerbi min max push down",
"SQL API: Simple Rollup",
"SQL API: Complex Rollup",
"SQL API: Nested Rollup",
"SQL API: Rollup with aliases",
"SQL API: Rollup over exprs",
"SQL API: Nested Rollup with aliases",
"SQL API: Nested Rollup over asterisk",
"SQL API: Extended nested Rollup over asterisk",
"SQL API: ungrouped pre-agg",
"SQL API: NULLS FIRST/LAST SQL push down",
"SQL API: SQL push down push to cube quoted alias",
"SQL API: Date/time comparison with SQL push down",
"SQL API: Date/time comparison with date_trunc with SQL push down",

"---------------------------------------",
"Error during rewrite: Can't detect Cube query and it may be not supported yet.",
"---------------------------------------",
"SQL API: Rolling Window YTD (year + month + day + date_trunc equal)",
"SQL API: Rolling Window YTD (year + month + day + date_trunc IN)",
"---------------------------------------",
"Switch dimensions",
"---------------------------------------",
"querying SwitchSourceTest: simple cross join",
"querying SwitchSourceTest: full cross join",
"querying SwitchSourceTest: filter by switch dimensions",

"-------",
"querying BigECommerce: rolling window YTD (month + week)",
"querying BigECommerce: rolling window YTD (month + week + no gran)",
"querying BigECommerce: rolling window YTD without granularity",
"querying BigECommerce with Retail Calendar: totalCountRetailMonthAgo",
"querying BigECommerce with Retail Calendar: totalCountRetailWeekAgo",
"SQL API: Timeshift measure from cube",
"querying BigECommerce: rolling window YTD (month + week + day + no gran)",
"SQL API: Timeshift measure from cube",
"querying custom granularities ECommerce: count by two_mo_by_feb + no dimension + rollingCountByLeading without date range",
"querying BigECommerce: rolling window YTD (month + week + day)"
]
}
Loading
Loading