Description
When Tesseract SQL Planner is enabled (CUBEJS_TESSERACT_SQL_PLANNER=true), the Rust function replace_date_range_for_rolling_window_without_granularity does not receive or handle the offset parameter from rolling window definitions. This causes all trailing: 'unbounded' rolling windows to be treated identically, generating {col} <= {end_date} regardless of offset value.
For offset: 'end' measures (e.g., ending balance), this is correct: billdate <= end_date.
For offset: 'start' measures (e.g., beginning balance), the generated SQL is wrong: it should be billdate < start_date, but Tesseract also produces billdate <= end_date.
Steps to Reproduce
- Create a cube with a measure using
rollingWindow: { trailing: 'unbounded', offset: 'start' }:
cube(`test_cube`, {
sql_table: `test_table`,
dimensions: {
billdate: { sql: `billdate`, type: `time` },
},
measures: {
beg_balance: {
sql: `amount`, type: `sum`,
rollingWindow: { trailing: `unbounded`, offset: `start` },
},
end_balance: {
sql: `amount`, type: `sum`,
rollingWindow: { trailing: `unbounded`, offset: `end` },
},
},
});
-
Enable Tesseract: CUBEJS_TESSERACT_SQL_PLANNER=true
-
Query both measures with a date range:
{
"measures": ["test_cube.beg_balance", "test_cube.end_balance"],
"timeDimensions": [{
"dimension": "test_cube.billdate",
"dateRange": ["2026-01-01 00:00:00", "2026-06-17 23:59:59"]
}]
}
Actual Behavior
Both measures generate the same WHERE clause:
WHERE ("test_cube"."billdate" <= $1::timestamptz)
with $1 = 2026-06-17T23:59:59.000Z (the end of the date range).
Expected Behavior
The offset: 'end' measure should generate:
WHERE ("test_cube"."billdate" <= $1::timestamptz) -- end_date
The offset: 'start' measure should generate:
WHERE ("test_cube"."billdate" < $1::timestamptz) -- start_date
with $1 = 2026-01-01T00:00:00.000Z (the start of the date range).
Root Cause
The JavaScript path (BaseQuery.rollingWindowDateJoinCondition in @cubejs-backend/schema-compiler) correctly handles the offset parameter:
offset === 'end' → {col} <= {dateTo}
offset === 'start' → {col} < {dateFrom}
However, the Rust counterpart replace_date_range_for_rolling_window_without_granularity in @cubejs-backend/native does not receive the offset argument. It only receives trailing/leading and the date range. For trailing: 'unbounded', it unconditionally generates BeforeOrOnDate(to), always using the end of the date range.
Note: replace_date_range_for_rolling_window_with_granularity does receive and handle offset correctly — the bug is specific to the non-granularity path.
Environment
- Cube.js: 1.6.58
@cubejs-backend/native: 1.6.58
@cubejs-backend/schema-compiler: 1.6.58
- OS: macOS
- Database: PostgreSQL
Workaround
A runtime patch intercepts BaseQuery.prototype.buildSqlAndParamsRust to post-process Tesseract's SQL output, replacing {col} <= {end_date} with {col} < {start_date} for offset: 'start' measures. This is fragile and should not be necessary.
Impact
Any measure using rollingWindow: { trailing: 'unbounded', offset: 'start' } (beginning-of-period calculations) produces incorrect SQL when Tesseract is enabled. The JS path (without Tesseract) is not affected.
Description
When Tesseract SQL Planner is enabled (
CUBEJS_TESSERACT_SQL_PLANNER=true), the Rust functionreplace_date_range_for_rolling_window_without_granularitydoes not receive or handle theoffsetparameter from rolling window definitions. This causes alltrailing: 'unbounded'rolling windows to be treated identically, generating{col} <= {end_date}regardless of offset value.For
offset: 'end'measures (e.g., ending balance), this is correct:billdate <= end_date.For
offset: 'start'measures (e.g., beginning balance), the generated SQL is wrong: it should bebilldate < start_date, but Tesseract also producesbilldate <= end_date.Steps to Reproduce
rollingWindow: { trailing: 'unbounded', offset: 'start' }:Enable Tesseract:
CUBEJS_TESSERACT_SQL_PLANNER=trueQuery both measures with a date range:
{ "measures": ["test_cube.beg_balance", "test_cube.end_balance"], "timeDimensions": [{ "dimension": "test_cube.billdate", "dateRange": ["2026-01-01 00:00:00", "2026-06-17 23:59:59"] }] }Actual Behavior
Both measures generate the same WHERE clause:
with
$1=2026-06-17T23:59:59.000Z(the end of the date range).Expected Behavior
The
offset: 'end'measure should generate:The
offset: 'start'measure should generate:with
$1=2026-01-01T00:00:00.000Z(the start of the date range).Root Cause
The JavaScript path (
BaseQuery.rollingWindowDateJoinConditionin@cubejs-backend/schema-compiler) correctly handles theoffsetparameter:However, the Rust counterpart
replace_date_range_for_rolling_window_without_granularityin@cubejs-backend/nativedoes not receive theoffsetargument. It only receivestrailing/leadingand the date range. Fortrailing: 'unbounded', it unconditionally generatesBeforeOrOnDate(to), always using the end of the date range.Note:
replace_date_range_for_rolling_window_with_granularitydoes receive and handleoffsetcorrectly — the bug is specific to the non-granularity path.Environment
@cubejs-backend/native: 1.6.58@cubejs-backend/schema-compiler: 1.6.58Workaround
A runtime patch intercepts
BaseQuery.prototype.buildSqlAndParamsRustto post-process Tesseract's SQL output, replacing{col} <= {end_date}with{col} < {start_date}foroffset: 'start'measures. This is fragile and should not be necessary.Impact
Any measure using
rollingWindow: { trailing: 'unbounded', offset: 'start' }(beginning-of-period calculations) produces incorrect SQL when Tesseract is enabled. The JS path (without Tesseract) is not affected.