Skip to content

Conversation

@Tankilevitch
Copy link
Contributor

@Tankilevitch Tankilevitch commented Oct 19, 2025

Fix Oracle Query Generation Issues

Summary

This PR fixes three critical Oracle database compatibility issues that prevented queries with rolling windows, time dimension filters, and subqueries from executing. All fixes maintain backward compatibility with other database adapters.


Issue #1: Invalid AS Keyword in Subquery Aliases

Problem

Oracle does not support the AS keyword before table/subquery aliases. Queries requiring subqueries would fail with:

ORA-00933: SQL command not properly ended

Why It Happened

The query builder was hardcoding the as keyword when generating subquery aliases, which works for PostgreSQL/MySQL but is invalid in Oracle SQL syntax.

What We Did

Modified the query builder to use a database-specific property (asSyntaxJoin) that each adapter can override. Oracle sets this to an empty string, while other databases use "AS".

Example

PostgreSQL (unchanged):

FROM (SELECT ...) AS q_0
INNER JOIN (SELECT ...) AS q_1 ON ...

Oracle (fixed):

FROM (SELECT ...) q_0
INNER JOIN (SELECT ...) q_1 ON ...

Issue #2: TypeError on Time Dimensions Without Granularity

Problem

When using time dimensions for filtering only (without specifying granularity), queries would crash with:

TypeError: Cannot read properties of undefined (reading 'isNaturalAligned')

Example Query

{
  "measures": ["visitors.count"],
  "timeDimensions": [{
    "dimension": "visitors.createdAt",
    "dateRange": ["2020-01-01", "2020-12-31"]
    // No granularity - just filtering
  }]
}

Why It Happened

The code assumed granularity was always present and tried to access properties on an undefined object. Time dimensions used only for filtering don't require granularity.

What We Did

Added a null check before accessing granularity properties. When granularity is not specified, the dimension is used as-is for filtering without grouping.

Result

-- Query now generates correctly:
SELECT count(*) FROM visitors
WHERE created_at >= to_date(...) AND created_at <= to_date(...)
-- No GROUP BY (correct for filtering without granularity)

Issue #3: Invalid Interval Syntax for Oracle

Problem

Queries with rolling windows would fail with:

ORA-17041: Missing IN or OUT parameter at index: 1

Example Query

{
  "measures": ["visitors.unboundedCount"],
  "timeDimensions": [{
    "dimension": "visitors.createdAt",
    "granularity": "year",
    "dateRange": ["2020-01-01", "2022-12-31"]
  }]
}

(where unboundedCount has a rollingWindow configuration)

Why It Happened

The default date arithmetic used PostgreSQL-style interval syntax:

-- PostgreSQL syntax (doesn't work in Oracle)
date_field >= some_date - interval '1 year'

Oracle requires specific functions for date arithmetic instead of the INTERVAL keyword.

What We Did

Implemented Oracle-specific addInterval and subtractInterval methods that use:

  • ADD_MONTHS(date, n) for year/month/quarter intervals
  • NUMTODSINTERVAL(n, unit) for day/hour/minute/second intervals

Transformation Examples

Before (PostgreSQL syntax):

WHERE date_field >= to_date(:"?", ...) - interval '1 year'

After (Oracle syntax):

WHERE date_field >= ADD_MONTHS(to_date(:"?", ...), -12)

Interval Conversion

Interval Type Oracle Function
Years ADD_MONTHS(date, ±12)
Quarters ADD_MONTHS(date, ±3)
Months ADD_MONTHS(date, ±1)
Days date ± NUMTODSINTERVAL(n, 'DAY')
Hours date ± NUMTODSINTERVAL(n, 'HOUR')

Testing

New Test Coverage

Created comprehensive test suite in oracle-query.test.ts with 10 tests covering:

  • Basic query generation
  • Subquery aliases without AS keyword (multiple scenarios)
  • FETCH NEXT syntax instead of LIMIT
  • Group by dimensions (not indexes)
  • Time dimensions without granularity
  • Oracle-specific interval arithmetic

Regression Protection

Added test in postgres-query.test.ts to ensure PostgreSQL continues using AS keyword correctly.


Backward Compatibility

All changes are backward compatible:

  • PostgreSQL, MySQL, BigQuery, etc. continue using AS keyword
  • The granularity null check works for all database adapters
  • Only Oracle uses the new interval arithmetic methods
  • All existing queries continue to work as before

Impact

These fixes enable Oracle users to:

  • Use time dimensions for filtering without specifying granularity
  • Execute queries with rolling windows (trailing, leading, offset)
  • Perform time-based comparisons with proper date arithmetic
  • Use any query pattern that requires subqueries or date calculations

… for Oracle compatibility

Oracle database does not support the AS keyword for table/subquery aliasing,
while other databases like PostgreSQL and MySQL do. The existing BaseQuery
implementation hardcoded 'as' in subquery alias generation, causing Oracle
queries to fail.

This change:
- Replaces hardcoded 'as' with asSyntaxJoin property in BaseQuery
- Oracle returns empty string for asSyntaxJoin (no AS keyword)
- PostgreSQL/MySQL return 'AS' (maintains existing behavior)
- Adds comprehensive Oracle query test suite validating AS syntax removal
- Adds PostgreSQL regression test ensuring AS keyword is still present

This fixes queries with rolling windows and multiple subqueries on Oracle,
which previously generated invalid SQL like:
  SELECT ... FROM (...) as q_0 INNER JOIN (...) as q_1 ON ...

Now Oracle correctly generates:
  SELECT ... FROM (...) q_0 INNER JOIN (...) q_1 ON ...
@Tankilevitch Tankilevitch requested a review from a team as a code owner October 19, 2025 14:31
@github-actions github-actions bot added javascript Pull requests that update Javascript code pr:community Contribution from Cube.js community members. labels Oct 19, 2025
- Fix AS keyword in subquery aliases (Oracle doesn't support it)
- Handle time dimensions without granularity to prevent TypeError
- Implement Oracle-specific interval arithmetic using ADD_MONTHS and NUMTODSINTERVAL
- Add comprehensive test suite for Oracle query generation

These changes enable Oracle users to execute queries with:
- Time dimension filters without granularity specification
- Rolling windows and time-based calculations
- Multiple subqueries with proper aliasing

All changes maintain backward compatibility with other database adapters.
@Tankilevitch Tankilevitch changed the title fix(schema-compiler): remove hardcoded AS keyword in subquery aliases for Oracle compatibility fix(schema-compiler): add support for time filters and rolling windows and fix subquery aliasing in oracle Oct 19, 2025
@KSDaemon KSDaemon self-assigned this Oct 21, 2025
@KSDaemon
Copy link
Member

@Tankilevitch thank you for the fix! I'll review it a bit later.
In the meantime, as I merged your previous fix, please rebase and resolve the conflicts in the tests :)

expect(sql).toMatch(/\s+AS\s+q_0\s+/);

// Should NOT have pattern ) q_0 (without AS)
// This regex checks for closing paren followed by space(s), q_0, space, but NOT preceded by AS
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the comment is not aligned with the regex as it doesn't include parentheses.

*/
dimensionTimeGroupedColumn(dimension, granularity) {
// Handle case when granularity is not specified (e.g., time dimension used only for filtering)
if (!granularity) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you show the example when this function is called without granularity object?

@KSDaemon KSDaemon added the driver:oracle Issues relating to the Oracle driver label Oct 21, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

driver:oracle Issues relating to the Oracle driver javascript Pull requests that update Javascript code pr:community Contribution from Cube.js community members.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants