diff --git a/CHANGELOG.md b/CHANGELOG.md index dad1e9b9dacfb..847c4ea6d1f8f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,19 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.27](https://github.com/cube-js/cube/compare/v1.7.26...v1.7.27) (2026-08-26) + +### Bug Fixes + +- **backend-native:** treat a client disconnect on /v1/cubesql as a graceful end ([#11649](https://github.com/cube-js/cube/issues/11649)) ([e803c6c](https://github.com/cube-js/cube/commit/e803c6cb58610c5da1a0710664a99d3047a6b3de)) +- **cubesql:** Prefer SQL pushdown over limitless post-processing ([#11559](https://github.com/cube-js/cube/issues/11559)) ([b022de1](https://github.com/cube-js/cube/commit/b022de166acefa29766c4760f81241e31cf9ac16)) +- **cubestore:** inline aggregate dropped rows past the first partition ([#11631](https://github.com/cube-js/cube/issues/11631)) ([6f3e66d](https://github.com/cube-js/cube/commit/6f3e66d4fcf89733a21d730d3e90fcb493a72dd0)) +- **schema-compiler:** a cube that extends another broke the parent's multi-stage measures ([#11641](https://github.com/cube-js/cube/issues/11641)) ([657f4e1](https://github.com/cube-js/cube/commit/657f4e18ae75c69dd4db4057e14867f8a7d91247)) + +### Features + +- **cubesql:** Support `COPY ... FROM STDIN` ([#11538](https://github.com/cube-js/cube/issues/11538)) ([534f839](https://github.com/cube-js/cube/commit/534f8392830df33b7934129cbcbe068984e42903)) + ## [1.7.26](https://github.com/cube-js/cube/compare/v1.7.25...v1.7.26) (2026-08-24) ### Bug Fixes diff --git a/docs-mintlify/docs/pre-aggregations/cube-store-architecture.mdx b/docs-mintlify/docs/pre-aggregations/cube-store-architecture.mdx index dd7913e34e82e..4e7cac725e350 100644 --- a/docs-mintlify/docs/pre-aggregations/cube-store-architecture.mdx +++ b/docs-mintlify/docs/pre-aggregations/cube-store-architecture.mdx @@ -996,6 +996,42 @@ Because detection is timestamp-based and cancellation is idempotent, it does not matter which instance notices first: recovery works even if the failed node never comes back. +### Message size limits + +Cube talks to the cache and queue store over a WebSocket connection, and +every cache entry, queue payload, and query result crosses it as one +message. That transport has a hard ceiling: + +| Limit | Environment variable | Default | Allowed range | +|---|---|---|---| +| Transport message size | `CUBESTORE_TRANSPORT_MAX_MESSAGE_SIZE` | 64 MiB | 16 MiB — 256 MiB | +| Transport frame size | `CUBESTORE_TRANSPORT_MAX_FRAME_SIZE` | 64 MiB | 4 MiB — 256 MiB | +| Single cache entry | `CUBESTORE_CACHE_MAX_ENTRY_SIZE` | 63 MiB | up to transport message size − 1 MiB | + +Query results are the usual reason these limits are reached. A result is +handed to the queue store so that every API instance waiting on that query +can pick it up, and it is written to the cache under its cache key — so a +result set larger than the transport limit cannot be delivered at all, +however fast the query that produced it was. + +A cache entry at or above `CUBESTORE_CACHE_MAX_ENTRY_SIZE` is rejected explicitly, +with `Unable to SET cache with '' key, exceeds maximum allowed size for +payload: , max allowed: `. + + + +**Known limitation.** A message above the *transport* limit is not answered +with an error. The deployment then reports `ConnectionError: CubeStore connection error: +write EPIPE`. + + + +If a query hits this limit, reduce the size of its result set — add filters +or a lower `limit`, drop dimensions, or split it into several queries. +Self-hosted deployments can raise the ceiling instead, but +`CUBESTORE_TRANSPORT_MAX_MESSAGE_SIZE` and `CUBESTORE_TRANSPORT_MAX_FRAME_SIZE` +have to go up together, since a message is sent as a single frame. + ### Why it lives in Cube Store Folding the cache and queue into Cube Store is itself a design decision in diff --git a/docs-mintlify/reference/configuration/environment-variables.mdx b/docs-mintlify/reference/configuration/environment-variables.mdx index 979325bf9ef2f..b5856225db9ec 100644 --- a/docs-mintlify/reference/configuration/environment-variables.mdx +++ b/docs-mintlify/reference/configuration/environment-variables.mdx @@ -1870,6 +1870,18 @@ The address/port pair for Cube Store's MySQL-compatible interface. | ------------------------- | ---------------------- | --------------------- | | A valid address/port pair | `0.0.0.0:3306` | `0.0.0.0:3306` | +## `CUBESTORE_CACHE_MAX_ENTRY_SIZE` + +The maximum size of a single entry in Cube Store's +[cache store](/docs/pre-aggregations/cube-store-architecture#cache-and-queue-store). +Larger entries are rejected. Cannot exceed +[`CUBESTORE_TRANSPORT_MAX_MESSAGE_SIZE`](#cubestore_transport_max_message_size) +minus 1 MiB. + +| Possible Values | Default in Development | Default in Production | +| --------------- | ---------------------- | --------------------- | +| A size, e.g. `63MiB` | `63MiB` | `63MiB` | + ## `CUBESTORE_DATA_DIR` A path on the local filesystem to store a local replica of the data. Must be @@ -2134,6 +2146,26 @@ If `true`, then sends telemetry to Cube. | --------------- | ---------------------- | --------------------- | | `true`, `false` | `true` | `true` | +## `CUBESTORE_TRANSPORT_MAX_MESSAGE_SIZE` + +The maximum size of a single message on the WebSocket connection between Cube +and Cube Store. Messages above this size close the connection; see +[message size limits](/docs/pre-aggregations/cube-store-architecture#message-size-limits). + +| Possible Values | Default in Development | Default in Production | +| --------------- | ---------------------- | --------------------- | +| A size from `16MiB` to `256MiB` | `64MiB` | `64MiB` | + +## `CUBESTORE_TRANSPORT_MAX_FRAME_SIZE` + +The maximum size of a single WebSocket frame on the connection between Cube +and Cube Store. A message is split into frames, so this can be smaller than +[`CUBESTORE_TRANSPORT_MAX_MESSAGE_SIZE`](#cubestore_transport_max_message_size). + +| Possible Values | Default in Development | Default in Production | +| --------------- | ---------------------- | --------------------- | +| A size from `4MiB` to `256MiB` | `64MiB` | `64MiB` | + ## `CUBESTORE_WAL_SPLIT_THRESHOLD` The maximum number of rows to keep in a single chunk of data right after diff --git a/lerna.json b/lerna.json index 720149e2d0b99..ca0c93c498eb0 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "1.7.26", + "version": "1.7.27", "npmClient": "yarn", "command": { "bootstrap": { diff --git a/packages/cubejs-api-gateway/CHANGELOG.md b/packages/cubejs-api-gateway/CHANGELOG.md index 0aa7e1651b913..eabda1dd058a1 100644 --- a/packages/cubejs-api-gateway/CHANGELOG.md +++ b/packages/cubejs-api-gateway/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.27](https://github.com/cube-js/cube/compare/v1.7.26...v1.7.27) (2026-08-26) + +**Note:** Version bump only for package @cubejs-backend/api-gateway + ## [1.7.26](https://github.com/cube-js/cube/compare/v1.7.25...v1.7.26) (2026-08-24) **Note:** Version bump only for package @cubejs-backend/api-gateway diff --git a/packages/cubejs-api-gateway/package.json b/packages/cubejs-api-gateway/package.json index 029c10694fbad..38a1ce078dbff 100644 --- a/packages/cubejs-api-gateway/package.json +++ b/packages/cubejs-api-gateway/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/api-gateway", "description": "Cube API Gateway", "author": "Cube Dev, Inc.", - "version": "1.7.26", + "version": "1.7.27", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -27,9 +27,9 @@ "dist/src/*" ], "dependencies": { - "@cubejs-backend/native": "1.7.26", - "@cubejs-backend/query-orchestrator": "1.7.26", - "@cubejs-backend/shared": "1.7.26", + "@cubejs-backend/native": "1.7.27", + "@cubejs-backend/query-orchestrator": "1.7.27", + "@cubejs-backend/shared": "1.7.27", "@ungap/structured-clone": "^0.3.4", "assert-never": "^1.4.0", "body-parser": "^1.19.0", @@ -53,7 +53,7 @@ "zod": "^4.1.13" }, "devDependencies": { - "@cubejs-backend/linter": "1.7.26", + "@cubejs-backend/linter": "1.7.27", "@types/express": "^4.17.21", "@types/jest": "^29", "@types/jsonwebtoken": "^9.0.2", diff --git a/packages/cubejs-athena-driver/CHANGELOG.md b/packages/cubejs-athena-driver/CHANGELOG.md index 845db75b18973..dc78153be4acf 100644 --- a/packages/cubejs-athena-driver/CHANGELOG.md +++ b/packages/cubejs-athena-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.27](https://github.com/cube-js/cube/compare/v1.7.26...v1.7.27) (2026-08-26) + +**Note:** Version bump only for package @cubejs-backend/athena-driver + ## [1.7.26](https://github.com/cube-js/cube/compare/v1.7.25...v1.7.26) (2026-08-24) **Note:** Version bump only for package @cubejs-backend/athena-driver diff --git a/packages/cubejs-athena-driver/package.json b/packages/cubejs-athena-driver/package.json index c2bd7fbb850ea..43ee64c2999d7 100644 --- a/packages/cubejs-athena-driver/package.json +++ b/packages/cubejs-athena-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/athena-driver", "description": "Cube.js Athena database driver", "author": "Cube Dev, Inc.", - "version": "1.7.26", + "version": "1.7.27", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -31,12 +31,12 @@ "dependencies": { "@aws-sdk/client-athena": "^3.22.0", "@aws-sdk/credential-providers": "^3.22.0", - "@cubejs-backend/base-driver": "1.7.26", - "@cubejs-backend/shared": "1.7.26" + "@cubejs-backend/base-driver": "1.7.27", + "@cubejs-backend/shared": "1.7.27" }, "devDependencies": { - "@cubejs-backend/linter": "1.7.26", - "@cubejs-backend/testing-shared": "1.7.26", + "@cubejs-backend/linter": "1.7.27", + "@cubejs-backend/testing-shared": "1.7.27", "@types/ramda": "^0.27.40", "typescript": "~5.2.2" }, diff --git a/packages/cubejs-backend-cloud/CHANGELOG.md b/packages/cubejs-backend-cloud/CHANGELOG.md index f2d34bab27dbb..c7c2acc9bd6df 100644 --- a/packages/cubejs-backend-cloud/CHANGELOG.md +++ b/packages/cubejs-backend-cloud/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.27](https://github.com/cube-js/cube/compare/v1.7.26...v1.7.27) (2026-08-26) + +**Note:** Version bump only for package @cubejs-backend/cloud + ## [1.7.26](https://github.com/cube-js/cube/compare/v1.7.25...v1.7.26) (2026-08-24) **Note:** Version bump only for package @cubejs-backend/cloud diff --git a/packages/cubejs-backend-cloud/package.json b/packages/cubejs-backend-cloud/package.json index 4b3111c2ad2aa..fabf31b9f54a8 100644 --- a/packages/cubejs-backend-cloud/package.json +++ b/packages/cubejs-backend-cloud/package.json @@ -1,6 +1,6 @@ { "name": "@cubejs-backend/cloud", - "version": "1.7.26", + "version": "1.7.27", "description": "Cube Cloud package", "main": "dist/src/index.js", "typings": "dist/src/index.d.ts", @@ -30,7 +30,7 @@ "devDependencies": { "@babel/core": "^7.24.5", "@babel/preset-env": "^7.24.5", - "@cubejs-backend/linter": "1.7.26", + "@cubejs-backend/linter": "1.7.27", "@types/fs-extra": "^9.0.8", "@types/jest": "^29", "jest": "^29", @@ -38,7 +38,7 @@ }, "dependencies": { "@cubejs-backend/dotenv": "^9.0.2", - "@cubejs-backend/shared": "1.7.26", + "@cubejs-backend/shared": "1.7.27", "chokidar": "^3.5.1", "env-var": "^6.3.0", "form-data": "^4.0.0", diff --git a/packages/cubejs-backend-maven/CHANGELOG.md b/packages/cubejs-backend-maven/CHANGELOG.md index dd86abf1f6b57..408c7670cc0c7 100644 --- a/packages/cubejs-backend-maven/CHANGELOG.md +++ b/packages/cubejs-backend-maven/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.27](https://github.com/cube-js/cube/compare/v1.7.26...v1.7.27) (2026-08-26) + +**Note:** Version bump only for package @cubejs-backend/maven + ## [1.7.26](https://github.com/cube-js/cube/compare/v1.7.25...v1.7.26) (2026-08-24) **Note:** Version bump only for package @cubejs-backend/maven diff --git a/packages/cubejs-backend-maven/package.json b/packages/cubejs-backend-maven/package.json index aea2e8c2f1a81..fc10cfae81702 100644 --- a/packages/cubejs-backend-maven/package.json +++ b/packages/cubejs-backend-maven/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/maven", "description": "Cube.js Maven Wrapper for java dependencies downloading", "author": "Cube Dev, Inc.", - "version": "1.7.26", + "version": "1.7.27", "license": "Apache-2.0", "repository": { "type": "git", @@ -31,12 +31,12 @@ "dist/src/*" ], "dependencies": { - "@cubejs-backend/shared": "1.7.26", + "@cubejs-backend/shared": "1.7.27", "source-map-support": "^0.5.19", "xmlbuilder2": "^2.4.0" }, "devDependencies": { - "@cubejs-backend/linter": "1.7.26", + "@cubejs-backend/linter": "1.7.27", "@types/jest": "^29", "@types/node": "^22", "jest": "^29", diff --git a/packages/cubejs-backend-native/CHANGELOG.md b/packages/cubejs-backend-native/CHANGELOG.md index 701861af2a30f..aed3734f9888d 100644 --- a/packages/cubejs-backend-native/CHANGELOG.md +++ b/packages/cubejs-backend-native/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.27](https://github.com/cube-js/cube/compare/v1.7.26...v1.7.27) (2026-08-26) + +### Bug Fixes + +- **backend-native:** treat a client disconnect on /v1/cubesql as a graceful end ([#11649](https://github.com/cube-js/cube/issues/11649)) ([e803c6c](https://github.com/cube-js/cube/commit/e803c6cb58610c5da1a0710664a99d3047a6b3de)) + ## [1.7.26](https://github.com/cube-js/cube/compare/v1.7.25...v1.7.26) (2026-08-24) **Note:** Version bump only for package @cubejs-backend/native diff --git a/packages/cubejs-backend-native/package.json b/packages/cubejs-backend-native/package.json index 0173c85dcba94..6add5c1ec2237 100644 --- a/packages/cubejs-backend-native/package.json +++ b/packages/cubejs-backend-native/package.json @@ -1,6 +1,6 @@ { "name": "@cubejs-backend/native", - "version": "1.7.26", + "version": "1.7.27", "author": "Cube Dev, Inc.", "description": "Native module for Cube.js (binding to Rust codebase)", "main": "dist/js/index.js", @@ -39,7 +39,7 @@ "dist/js" ], "devDependencies": { - "@cubejs-backend/linter": "1.7.26", + "@cubejs-backend/linter": "1.7.27", "@types/jest": "^29", "@types/node": "^22", "cargo-cp-artifact": "^0.1.9", @@ -50,8 +50,8 @@ "uuid": "^11.1.1" }, "dependencies": { - "@cubejs-backend/cubesql": "1.7.26", - "@cubejs-backend/shared": "1.7.26", + "@cubejs-backend/cubesql": "1.7.27", + "@cubejs-backend/shared": "1.7.27", "@cubejs-infra/post-installer": "^0.1.2" }, "resources": { diff --git a/packages/cubejs-backend-shared/CHANGELOG.md b/packages/cubejs-backend-shared/CHANGELOG.md index ffd5595085c51..1eead5215a522 100644 --- a/packages/cubejs-backend-shared/CHANGELOG.md +++ b/packages/cubejs-backend-shared/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.27](https://github.com/cube-js/cube/compare/v1.7.26...v1.7.27) (2026-08-26) + +**Note:** Version bump only for package @cubejs-backend/shared + ## [1.7.26](https://github.com/cube-js/cube/compare/v1.7.25...v1.7.26) (2026-08-24) ### Features diff --git a/packages/cubejs-backend-shared/package.json b/packages/cubejs-backend-shared/package.json index b35f9be3be5a6..2e5a5144f9d9e 100644 --- a/packages/cubejs-backend-shared/package.json +++ b/packages/cubejs-backend-shared/package.json @@ -1,6 +1,6 @@ { "name": "@cubejs-backend/shared", - "version": "1.7.26", + "version": "1.7.27", "description": "Shared code for Cube.js backend packages", "main": "dist/src/index.js", "typings": "dist/src/index.d.ts", @@ -27,7 +27,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@cubejs-backend/linter": "1.7.26", + "@cubejs-backend/linter": "1.7.27", "@types/bytes": "^3.1.5", "@types/cli-progress": "^3.9.1", "@types/jest": "^29", diff --git a/packages/cubejs-base-driver/CHANGELOG.md b/packages/cubejs-base-driver/CHANGELOG.md index a1960770bf16f..f9c03c4d70d2b 100644 --- a/packages/cubejs-base-driver/CHANGELOG.md +++ b/packages/cubejs-base-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.27](https://github.com/cube-js/cube/compare/v1.7.26...v1.7.27) (2026-08-26) + +**Note:** Version bump only for package @cubejs-backend/base-driver + ## [1.7.26](https://github.com/cube-js/cube/compare/v1.7.25...v1.7.26) (2026-08-24) ### Features diff --git a/packages/cubejs-base-driver/package.json b/packages/cubejs-base-driver/package.json index 90aea2c3b8bf7..dda5cc756bcae 100644 --- a/packages/cubejs-base-driver/package.json +++ b/packages/cubejs-base-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/base-driver", "description": "Cube.js Base Driver", "author": "Cube Dev, Inc.", - "version": "1.7.26", + "version": "1.7.27", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -33,11 +33,11 @@ "@aws-sdk/s3-request-presigner": "^3.49.0", "@azure/identity": "^4.4.1", "@azure/storage-blob": "^12.9.0", - "@cubejs-backend/shared": "1.7.26", + "@cubejs-backend/shared": "1.7.27", "@google-cloud/storage": "^7.13.0" }, "devDependencies": { - "@cubejs-backend/linter": "1.7.26", + "@cubejs-backend/linter": "1.7.27", "@types/jest": "^29", "@types/node": "^22", "jest": "^29", diff --git a/packages/cubejs-bigquery-driver/CHANGELOG.md b/packages/cubejs-bigquery-driver/CHANGELOG.md index a2b165c4ee68f..1b323d3e5bc6d 100644 --- a/packages/cubejs-bigquery-driver/CHANGELOG.md +++ b/packages/cubejs-bigquery-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.27](https://github.com/cube-js/cube/compare/v1.7.26...v1.7.27) (2026-08-26) + +**Note:** Version bump only for package @cubejs-backend/bigquery-driver + ## [1.7.26](https://github.com/cube-js/cube/compare/v1.7.25...v1.7.26) (2026-08-24) **Note:** Version bump only for package @cubejs-backend/bigquery-driver diff --git a/packages/cubejs-bigquery-driver/package.json b/packages/cubejs-bigquery-driver/package.json index 9c5aa6af93aa2..be427c75837de 100644 --- a/packages/cubejs-bigquery-driver/package.json +++ b/packages/cubejs-bigquery-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/bigquery-driver", "description": "Cube.js BigQuery database driver", "author": "Cube Dev, Inc.", - "version": "1.7.26", + "version": "1.7.27", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -29,15 +29,15 @@ "main": "index.js", "types": "dist/src/index.d.ts", "dependencies": { - "@cubejs-backend/base-driver": "1.7.26", + "@cubejs-backend/base-driver": "1.7.27", "@cubejs-backend/dotenv": "^9.0.2", - "@cubejs-backend/shared": "1.7.26", + "@cubejs-backend/shared": "1.7.27", "@google-cloud/bigquery": "^7.7.0", "@google-cloud/storage": "^7.13.0", "ramda": "^0.27.2" }, "devDependencies": { - "@cubejs-backend/testing-shared": "1.7.26", + "@cubejs-backend/testing-shared": "1.7.27", "@types/big.js": "^6.2.2", "@types/dedent": "^0.7.0", "@types/jest": "^29", diff --git a/packages/cubejs-cli/CHANGELOG.md b/packages/cubejs-cli/CHANGELOG.md index 8272183b715d3..96a54fd9078a2 100644 --- a/packages/cubejs-cli/CHANGELOG.md +++ b/packages/cubejs-cli/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.27](https://github.com/cube-js/cube/compare/v1.7.26...v1.7.27) (2026-08-26) + +**Note:** Version bump only for package cubejs-cli + ## [1.7.26](https://github.com/cube-js/cube/compare/v1.7.25...v1.7.26) (2026-08-24) **Note:** Version bump only for package cubejs-cli diff --git a/packages/cubejs-cli/package.json b/packages/cubejs-cli/package.json index 730e9e9d6584c..2cb5bfd135381 100644 --- a/packages/cubejs-cli/package.json +++ b/packages/cubejs-cli/package.json @@ -2,7 +2,7 @@ "name": "cubejs-cli", "description": "Cube.js Command Line Interface", "author": "Cube Dev, Inc.", - "version": "1.7.26", + "version": "1.7.27", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -30,10 +30,10 @@ "LICENSE" ], "dependencies": { - "@cubejs-backend/cloud": "1.7.26", + "@cubejs-backend/cloud": "1.7.27", "@cubejs-backend/dotenv": "^9.0.2", - "@cubejs-backend/schema-compiler": "1.7.26", - "@cubejs-backend/shared": "1.7.26", + "@cubejs-backend/schema-compiler": "1.7.27", + "@cubejs-backend/shared": "1.7.27", "chalk": "^2.4.2", "cli-progress": "^3.10", "commander": "^2.19.0", @@ -50,8 +50,8 @@ "colors": "1.4.0" }, "devDependencies": { - "@cubejs-backend/linter": "1.7.26", - "@cubejs-backend/server": "1.7.26", + "@cubejs-backend/linter": "1.7.27", + "@cubejs-backend/server": "1.7.27", "@oclif/command": "^1.8.0", "@types/cli-progress": "^3.8.0", "@types/cross-spawn": "^6.0.2", diff --git a/packages/cubejs-clickhouse-driver/CHANGELOG.md b/packages/cubejs-clickhouse-driver/CHANGELOG.md index 5d16c298d5fd5..2d7838c010291 100644 --- a/packages/cubejs-clickhouse-driver/CHANGELOG.md +++ b/packages/cubejs-clickhouse-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.27](https://github.com/cube-js/cube/compare/v1.7.26...v1.7.27) (2026-08-26) + +**Note:** Version bump only for package @cubejs-backend/clickhouse-driver + ## [1.7.26](https://github.com/cube-js/cube/compare/v1.7.25...v1.7.26) (2026-08-24) **Note:** Version bump only for package @cubejs-backend/clickhouse-driver diff --git a/packages/cubejs-clickhouse-driver/package.json b/packages/cubejs-clickhouse-driver/package.json index b6d891f939e5f..ccf2498ae8295 100644 --- a/packages/cubejs-clickhouse-driver/package.json +++ b/packages/cubejs-clickhouse-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/clickhouse-driver", "description": "Cube.js ClickHouse database driver", "author": "Cube Dev, Inc.", - "version": "1.7.26", + "version": "1.7.27", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -29,16 +29,16 @@ }, "dependencies": { "@clickhouse/client": "^1.12.0", - "@cubejs-backend/base-driver": "1.7.26", - "@cubejs-backend/shared": "1.7.26", + "@cubejs-backend/base-driver": "1.7.27", + "@cubejs-backend/shared": "1.7.27", "moment": "^2.24.0", "sqlstring": "^2.3.1", "uuid": "^11.1.1" }, "license": "Apache-2.0", "devDependencies": { - "@cubejs-backend/linter": "1.7.26", - "@cubejs-backend/testing-shared": "1.7.26", + "@cubejs-backend/linter": "1.7.27", + "@cubejs-backend/testing-shared": "1.7.27", "@types/jest": "^29", "jest": "^29", "typescript": "~5.2.2" diff --git a/packages/cubejs-client-core/CHANGELOG.md b/packages/cubejs-client-core/CHANGELOG.md index e2cc3e94973ed..44fb3ee8ef4e6 100644 --- a/packages/cubejs-client-core/CHANGELOG.md +++ b/packages/cubejs-client-core/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.27](https://github.com/cube-js/cube/compare/v1.7.26...v1.7.27) (2026-08-26) + +**Note:** Version bump only for package @cubejs-client/core + ## [1.7.26](https://github.com/cube-js/cube/compare/v1.7.25...v1.7.26) (2026-08-24) **Note:** Version bump only for package @cubejs-client/core diff --git a/packages/cubejs-client-core/package.json b/packages/cubejs-client-core/package.json index 4d96750368678..ba8a45356a865 100644 --- a/packages/cubejs-client-core/package.json +++ b/packages/cubejs-client-core/package.json @@ -1,6 +1,6 @@ { "name": "@cubejs-client/core", - "version": "1.7.26", + "version": "1.7.27", "engines": {}, "type": "module", "repository": { @@ -58,7 +58,7 @@ ], "license": "MIT", "devDependencies": { - "@cubejs-backend/linter": "1.7.26", + "@cubejs-backend/linter": "1.7.27", "@types/d3-format": "^3", "@types/d3-time-format": "^4", "@types/moment-range": "^4.0.0", diff --git a/packages/cubejs-client-dx/CHANGELOG.md b/packages/cubejs-client-dx/CHANGELOG.md index 21ba9e1849985..30c531fc211ae 100644 --- a/packages/cubejs-client-dx/CHANGELOG.md +++ b/packages/cubejs-client-dx/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.27](https://github.com/cube-js/cube/compare/v1.7.26...v1.7.27) (2026-08-26) + +**Note:** Version bump only for package @cubejs-client/dx + ## [1.7.26](https://github.com/cube-js/cube/compare/v1.7.25...v1.7.26) (2026-08-24) **Note:** Version bump only for package @cubejs-client/dx diff --git a/packages/cubejs-client-dx/package.json b/packages/cubejs-client-dx/package.json index 8813e7f7a1019..48f91965efb86 100644 --- a/packages/cubejs-client-dx/package.json +++ b/packages/cubejs-client-dx/package.json @@ -1,6 +1,6 @@ { "name": "@cubejs-client/dx", - "version": "1.7.26", + "version": "1.7.27", "engines": {}, "repository": { "type": "git", diff --git a/packages/cubejs-client-ngx/CHANGELOG.md b/packages/cubejs-client-ngx/CHANGELOG.md index 597e74a30a6fd..32f829c6b4da6 100644 --- a/packages/cubejs-client-ngx/CHANGELOG.md +++ b/packages/cubejs-client-ngx/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.27](https://github.com/cube-js/cube/compare/v1.7.26...v1.7.27) (2026-08-26) + +**Note:** Version bump only for package @cubejs-client/ngx + ## [1.7.26](https://github.com/cube-js/cube/compare/v1.7.25...v1.7.26) (2026-08-24) **Note:** Version bump only for package @cubejs-client/ngx diff --git a/packages/cubejs-client-ngx/package.json b/packages/cubejs-client-ngx/package.json index d7729c88aef00..136f9a492a703 100644 --- a/packages/cubejs-client-ngx/package.json +++ b/packages/cubejs-client-ngx/package.json @@ -1,6 +1,6 @@ { "name": "@cubejs-client/ngx", - "version": "1.7.26", + "version": "1.7.27", "author": "Cube Dev, Inc.", "engines": {}, "repository": { diff --git a/packages/cubejs-client-react/CHANGELOG.md b/packages/cubejs-client-react/CHANGELOG.md index 31d0ddb9e48e3..d27f87eab87d9 100644 --- a/packages/cubejs-client-react/CHANGELOG.md +++ b/packages/cubejs-client-react/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.27](https://github.com/cube-js/cube/compare/v1.7.26...v1.7.27) (2026-08-26) + +**Note:** Version bump only for package @cubejs-client/react + ## [1.7.26](https://github.com/cube-js/cube/compare/v1.7.25...v1.7.26) (2026-08-24) **Note:** Version bump only for package @cubejs-client/react diff --git a/packages/cubejs-client-react/package.json b/packages/cubejs-client-react/package.json index 4c157e562ce7a..2e04b912c69ab 100644 --- a/packages/cubejs-client-react/package.json +++ b/packages/cubejs-client-react/package.json @@ -1,6 +1,6 @@ { "name": "@cubejs-client/react", - "version": "1.7.26", + "version": "1.7.27", "author": "Cube Dev, Inc.", "license": "MIT", "engines": {}, @@ -26,7 +26,7 @@ ], "dependencies": { "@babel/runtime": "^7.1.2", - "@cubejs-client/core": "1.7.26", + "@cubejs-client/core": "1.7.27", "core-js": "^3.6.5", "ramda": "^0.27.2" }, diff --git a/packages/cubejs-client-vue3/CHANGELOG.md b/packages/cubejs-client-vue3/CHANGELOG.md index 66f4a3457ad06..8bf7974e71c84 100644 --- a/packages/cubejs-client-vue3/CHANGELOG.md +++ b/packages/cubejs-client-vue3/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.27](https://github.com/cube-js/cube/compare/v1.7.26...v1.7.27) (2026-08-26) + +**Note:** Version bump only for package @cubejs-client/vue3 + ## [1.7.26](https://github.com/cube-js/cube/compare/v1.7.25...v1.7.26) (2026-08-24) **Note:** Version bump only for package @cubejs-client/vue3 diff --git a/packages/cubejs-client-vue3/package.json b/packages/cubejs-client-vue3/package.json index a3a3b7f20bfa4..f5e96384ed8fc 100644 --- a/packages/cubejs-client-vue3/package.json +++ b/packages/cubejs-client-vue3/package.json @@ -1,6 +1,6 @@ { "name": "@cubejs-client/vue3", - "version": "1.7.26", + "version": "1.7.27", "engines": {}, "repository": { "type": "git", @@ -27,7 +27,7 @@ "src" ], "dependencies": { - "@cubejs-client/core": "1.7.26", + "@cubejs-client/core": "1.7.27", "ramda": "^0.27.0" }, "devDependencies": { diff --git a/packages/cubejs-client-ws-transport/CHANGELOG.md b/packages/cubejs-client-ws-transport/CHANGELOG.md index 905ce5b80ab34..2fe08dcaa30ef 100644 --- a/packages/cubejs-client-ws-transport/CHANGELOG.md +++ b/packages/cubejs-client-ws-transport/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.27](https://github.com/cube-js/cube/compare/v1.7.26...v1.7.27) (2026-08-26) + +**Note:** Version bump only for package @cubejs-client/ws-transport + ## [1.7.26](https://github.com/cube-js/cube/compare/v1.7.25...v1.7.26) (2026-08-24) **Note:** Version bump only for package @cubejs-client/ws-transport diff --git a/packages/cubejs-client-ws-transport/package.json b/packages/cubejs-client-ws-transport/package.json index 57129687d2ecf..b01f2d565e935 100644 --- a/packages/cubejs-client-ws-transport/package.json +++ b/packages/cubejs-client-ws-transport/package.json @@ -1,6 +1,6 @@ { "name": "@cubejs-client/ws-transport", - "version": "1.7.26", + "version": "1.7.27", "engines": {}, "repository": { "type": "git", @@ -20,7 +20,7 @@ }, "dependencies": { "@babel/runtime": "^7.1.2", - "@cubejs-client/core": "1.7.26", + "@cubejs-client/core": "1.7.27", "core-js": "^3.6.5", "isomorphic-ws": "^4.0.1", "ws": "^7.3.1" @@ -33,7 +33,7 @@ "@babel/core": "^7.3.3", "@babel/preset-env": "^7.3.1", "@babel/preset-typescript": "^7.12.1", - "@cubejs-backend/linter": "1.7.26", + "@cubejs-backend/linter": "1.7.27", "@types/ws": "^7.2.9", "typescript": "~5.2.2" }, diff --git a/packages/cubejs-crate-driver/CHANGELOG.md b/packages/cubejs-crate-driver/CHANGELOG.md index bb70d8bc736ad..6c95a6785b5fc 100644 --- a/packages/cubejs-crate-driver/CHANGELOG.md +++ b/packages/cubejs-crate-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.27](https://github.com/cube-js/cube/compare/v1.7.26...v1.7.27) (2026-08-26) + +**Note:** Version bump only for package @cubejs-backend/crate-driver + ## [1.7.26](https://github.com/cube-js/cube/compare/v1.7.25...v1.7.26) (2026-08-24) **Note:** Version bump only for package @cubejs-backend/crate-driver diff --git a/packages/cubejs-crate-driver/package.json b/packages/cubejs-crate-driver/package.json index 433dd2b1fc476..daed222cc526c 100644 --- a/packages/cubejs-crate-driver/package.json +++ b/packages/cubejs-crate-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/crate-driver", "description": "Cube.js Crate database driver", "author": "Cube Dev, Inc.", - "version": "1.7.26", + "version": "1.7.27", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -29,13 +29,13 @@ "lint:fix": "eslint --fix src/* --ext .ts" }, "dependencies": { - "@cubejs-backend/postgres-driver": "1.7.26", - "@cubejs-backend/shared": "1.7.26" + "@cubejs-backend/postgres-driver": "1.7.27", + "@cubejs-backend/shared": "1.7.27" }, "license": "Apache-2.0", "devDependencies": { - "@cubejs-backend/linter": "1.7.26", - "@cubejs-backend/testing-shared": "1.7.26", + "@cubejs-backend/linter": "1.7.27", + "@cubejs-backend/testing-shared": "1.7.27", "testcontainers": "^10.28.0", "typescript": "~5.2.2" }, diff --git a/packages/cubejs-cubestore-driver/CHANGELOG.md b/packages/cubejs-cubestore-driver/CHANGELOG.md index 7b515dd690ecb..62476e9e521ab 100644 --- a/packages/cubejs-cubestore-driver/CHANGELOG.md +++ b/packages/cubejs-cubestore-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.27](https://github.com/cube-js/cube/compare/v1.7.26...v1.7.27) (2026-08-26) + +**Note:** Version bump only for package @cubejs-backend/cubestore-driver + ## [1.7.26](https://github.com/cube-js/cube/compare/v1.7.25...v1.7.26) (2026-08-24) ### Features diff --git a/packages/cubejs-cubestore-driver/package.json b/packages/cubejs-cubestore-driver/package.json index 41a5bd59e052e..8cb61a624913d 100644 --- a/packages/cubejs-cubestore-driver/package.json +++ b/packages/cubejs-cubestore-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/cubestore-driver", "description": "Cube Store driver", "author": "Cube Dev, Inc.", - "version": "1.7.26", + "version": "1.7.27", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -26,10 +26,10 @@ "lint:fix": "eslint --fix src/*.ts" }, "dependencies": { - "@cubejs-backend/base-driver": "1.7.26", - "@cubejs-backend/cubestore": "1.7.26", - "@cubejs-backend/native": "1.7.26", - "@cubejs-backend/shared": "1.7.26", + "@cubejs-backend/base-driver": "1.7.27", + "@cubejs-backend/cubestore": "1.7.27", + "@cubejs-backend/native": "1.7.27", + "@cubejs-backend/shared": "1.7.27", "csv-write-stream": "^2.0.0", "flatbuffers": "25.9.23", "fs-extra": "^9.1.0", @@ -40,7 +40,7 @@ "ws": "^7.4.3" }, "devDependencies": { - "@cubejs-backend/linter": "1.7.26", + "@cubejs-backend/linter": "1.7.27", "@types/csv-write-stream": "^2.0.0", "@types/jest": "^29", "@types/node": "^22", diff --git a/packages/cubejs-databricks-jdbc-driver/CHANGELOG.md b/packages/cubejs-databricks-jdbc-driver/CHANGELOG.md index dc093fed8cc83..053ab6ffbab45 100644 --- a/packages/cubejs-databricks-jdbc-driver/CHANGELOG.md +++ b/packages/cubejs-databricks-jdbc-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.27](https://github.com/cube-js/cube/compare/v1.7.26...v1.7.27) (2026-08-26) + +**Note:** Version bump only for package @cubejs-backend/databricks-jdbc-driver + ## [1.7.26](https://github.com/cube-js/cube/compare/v1.7.25...v1.7.26) (2026-08-24) **Note:** Version bump only for package @cubejs-backend/databricks-jdbc-driver diff --git a/packages/cubejs-databricks-jdbc-driver/package.json b/packages/cubejs-databricks-jdbc-driver/package.json index 9aef4a00689ab..43ff6bcbc63f1 100644 --- a/packages/cubejs-databricks-jdbc-driver/package.json +++ b/packages/cubejs-databricks-jdbc-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/databricks-jdbc-driver", "description": "Cube.js Databricks database driver", "author": "Cube Dev, Inc.", - "version": "1.7.26", + "version": "1.7.27", "license": "Apache-2.0", "repository": { "type": "git", @@ -30,17 +30,17 @@ "bin" ], "dependencies": { - "@cubejs-backend/base-driver": "1.7.26", - "@cubejs-backend/jdbc-driver": "1.7.26", - "@cubejs-backend/schema-compiler": "1.7.26", - "@cubejs-backend/shared": "1.7.26", + "@cubejs-backend/base-driver": "1.7.27", + "@cubejs-backend/jdbc-driver": "1.7.27", + "@cubejs-backend/schema-compiler": "1.7.27", + "@cubejs-backend/shared": "1.7.27", "node-fetch": "^2.6.1", "ramda": "^0.27.2", "source-map-support": "^0.5.19", "uuid": "^11.1.1" }, "devDependencies": { - "@cubejs-backend/linter": "1.7.26", + "@cubejs-backend/linter": "1.7.27", "@types/jest": "^29", "@types/node": "^22", "@types/ramda": "^0.27.34", diff --git a/packages/cubejs-dbt-schema-extension/CHANGELOG.md b/packages/cubejs-dbt-schema-extension/CHANGELOG.md index 4479b785e5284..65d77e2bbc650 100644 --- a/packages/cubejs-dbt-schema-extension/CHANGELOG.md +++ b/packages/cubejs-dbt-schema-extension/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.27](https://github.com/cube-js/cube/compare/v1.7.26...v1.7.27) (2026-08-26) + +**Note:** Version bump only for package @cubejs-backend/dbt-schema-extension + ## [1.7.26](https://github.com/cube-js/cube/compare/v1.7.25...v1.7.26) (2026-08-24) **Note:** Version bump only for package @cubejs-backend/dbt-schema-extension diff --git a/packages/cubejs-dbt-schema-extension/package.json b/packages/cubejs-dbt-schema-extension/package.json index 96543bc076689..f4dd77b87abf9 100644 --- a/packages/cubejs-dbt-schema-extension/package.json +++ b/packages/cubejs-dbt-schema-extension/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/dbt-schema-extension", "description": "Cube.js dbt Schema Extension", "author": "Cube Dev, Inc.", - "version": "1.7.26", + "version": "1.7.27", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -25,14 +25,14 @@ "lint:fix": "eslint --fix src/* --ext .ts,.js" }, "dependencies": { - "@cubejs-backend/schema-compiler": "1.7.26", + "@cubejs-backend/schema-compiler": "1.7.27", "fs-extra": "^9.1.0", "inflection": "^1.12.0", "node-fetch": "^2.6.1" }, "devDependencies": { - "@cubejs-backend/linter": "1.7.26", - "@cubejs-backend/testing": "1.7.26", + "@cubejs-backend/linter": "1.7.27", + "@cubejs-backend/testing": "1.7.27", "@types/jest": "^29", "jest": "^29", "stream-to-array": "^2.3.0", diff --git a/packages/cubejs-docker/CHANGELOG.md b/packages/cubejs-docker/CHANGELOG.md index b0e8c478ef8a3..4f71ca392e28f 100644 --- a/packages/cubejs-docker/CHANGELOG.md +++ b/packages/cubejs-docker/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.27](https://github.com/cube-js/cube/compare/v1.7.26...v1.7.27) (2026-08-26) + +**Note:** Version bump only for package @cubejs-backend/docker + ## [1.7.26](https://github.com/cube-js/cube/compare/v1.7.25...v1.7.26) (2026-08-24) **Note:** Version bump only for package @cubejs-backend/docker diff --git a/packages/cubejs-docker/package.json b/packages/cubejs-docker/package.json index 0a94c141bcf8c..72523ff40b5ea 100644 --- a/packages/cubejs-docker/package.json +++ b/packages/cubejs-docker/package.json @@ -1,6 +1,6 @@ { "name": "@cubejs-backend/docker", - "version": "1.7.26", + "version": "1.7.27", "description": "Cube.js In Docker (virtual package)", "author": "Cube Dev, Inc.", "license": "Apache-2.0", @@ -9,34 +9,34 @@ "node": ">=20.0.0" }, "dependencies": { - "@cubejs-backend/athena-driver": "1.7.26", - "@cubejs-backend/bigquery-driver": "1.7.26", - "@cubejs-backend/clickhouse-driver": "1.7.26", - "@cubejs-backend/crate-driver": "1.7.26", - "@cubejs-backend/databricks-jdbc-driver": "1.7.26", - "@cubejs-backend/dbt-schema-extension": "1.7.26", - "@cubejs-backend/dremio-driver": "1.7.26", - "@cubejs-backend/druid-driver": "1.7.26", - "@cubejs-backend/duckdb-driver": "1.7.26", - "@cubejs-backend/firebolt-driver": "1.7.26", - "@cubejs-backend/hive-driver": "1.7.26", - "@cubejs-backend/ksql-driver": "1.7.26", - "@cubejs-backend/materialize-driver": "1.7.26", - "@cubejs-backend/mongobi-driver": "1.7.26", - "@cubejs-backend/mssql-driver": "1.7.26", - "@cubejs-backend/mysql-driver": "1.7.26", - "@cubejs-backend/oracle-driver": "1.7.26", - "@cubejs-backend/pinot-driver": "1.7.26", - "@cubejs-backend/postgres-driver": "1.7.26", - "@cubejs-backend/prestodb-driver": "1.7.26", - "@cubejs-backend/questdb-driver": "1.7.26", - "@cubejs-backend/redshift-driver": "1.7.26", - "@cubejs-backend/server": "1.7.26", - "@cubejs-backend/snowflake-driver": "1.7.26", - "@cubejs-backend/sqlite-driver": "1.7.26", - "@cubejs-backend/trino-driver": "1.7.26", - "@cubejs-backend/vertica-driver": "1.7.26", - "cubejs-cli": "1.7.26", + "@cubejs-backend/athena-driver": "1.7.27", + "@cubejs-backend/bigquery-driver": "1.7.27", + "@cubejs-backend/clickhouse-driver": "1.7.27", + "@cubejs-backend/crate-driver": "1.7.27", + "@cubejs-backend/databricks-jdbc-driver": "1.7.27", + "@cubejs-backend/dbt-schema-extension": "1.7.27", + "@cubejs-backend/dremio-driver": "1.7.27", + "@cubejs-backend/druid-driver": "1.7.27", + "@cubejs-backend/duckdb-driver": "1.7.27", + "@cubejs-backend/firebolt-driver": "1.7.27", + "@cubejs-backend/hive-driver": "1.7.27", + "@cubejs-backend/ksql-driver": "1.7.27", + "@cubejs-backend/materialize-driver": "1.7.27", + "@cubejs-backend/mongobi-driver": "1.7.27", + "@cubejs-backend/mssql-driver": "1.7.27", + "@cubejs-backend/mysql-driver": "1.7.27", + "@cubejs-backend/oracle-driver": "1.7.27", + "@cubejs-backend/pinot-driver": "1.7.27", + "@cubejs-backend/postgres-driver": "1.7.27", + "@cubejs-backend/prestodb-driver": "1.7.27", + "@cubejs-backend/questdb-driver": "1.7.27", + "@cubejs-backend/redshift-driver": "1.7.27", + "@cubejs-backend/server": "1.7.27", + "@cubejs-backend/snowflake-driver": "1.7.27", + "@cubejs-backend/sqlite-driver": "1.7.27", + "@cubejs-backend/trino-driver": "1.7.27", + "@cubejs-backend/vertica-driver": "1.7.27", + "cubejs-cli": "1.7.27", "typescript": "~5.2.2" }, "resolutions": { diff --git a/packages/cubejs-dremio-driver/CHANGELOG.md b/packages/cubejs-dremio-driver/CHANGELOG.md index f41ce16a93ec8..c48a43e5d42c5 100644 --- a/packages/cubejs-dremio-driver/CHANGELOG.md +++ b/packages/cubejs-dremio-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.27](https://github.com/cube-js/cube/compare/v1.7.26...v1.7.27) (2026-08-26) + +**Note:** Version bump only for package @cubejs-backend/dremio-driver + ## [1.7.26](https://github.com/cube-js/cube/compare/v1.7.25...v1.7.26) (2026-08-24) **Note:** Version bump only for package @cubejs-backend/dremio-driver diff --git a/packages/cubejs-dremio-driver/package.json b/packages/cubejs-dremio-driver/package.json index 562ad55c8df75..531adf3f7cb76 100644 --- a/packages/cubejs-dremio-driver/package.json +++ b/packages/cubejs-dremio-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/dremio-driver", "description": "Cube.js Dremio driver", "author": "Cube Dev, Inc.", - "version": "1.7.26", + "version": "1.7.27", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -23,14 +23,14 @@ "lint:fix": "eslint driver/*.js" }, "dependencies": { - "@cubejs-backend/base-driver": "1.7.26", - "@cubejs-backend/schema-compiler": "1.7.26", - "@cubejs-backend/shared": "1.7.26", + "@cubejs-backend/base-driver": "1.7.27", + "@cubejs-backend/schema-compiler": "1.7.27", + "@cubejs-backend/shared": "1.7.27", "axios": "^1.8.3" }, "devDependencies": { - "@cubejs-backend/linter": "1.7.26", - "@cubejs-backend/testing-shared": "1.7.26", + "@cubejs-backend/linter": "1.7.27", + "@cubejs-backend/testing-shared": "1.7.27", "jest": "^29" }, "license": "Apache-2.0", diff --git a/packages/cubejs-druid-driver/CHANGELOG.md b/packages/cubejs-druid-driver/CHANGELOG.md index 91dddd6852bd4..bcd80e2de0def 100644 --- a/packages/cubejs-druid-driver/CHANGELOG.md +++ b/packages/cubejs-druid-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.27](https://github.com/cube-js/cube/compare/v1.7.26...v1.7.27) (2026-08-26) + +**Note:** Version bump only for package @cubejs-backend/druid-driver + ## [1.7.26](https://github.com/cube-js/cube/compare/v1.7.25...v1.7.26) (2026-08-24) **Note:** Version bump only for package @cubejs-backend/druid-driver diff --git a/packages/cubejs-druid-driver/package.json b/packages/cubejs-druid-driver/package.json index 93b92cebe9ff2..3026bf2187d91 100644 --- a/packages/cubejs-druid-driver/package.json +++ b/packages/cubejs-druid-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/druid-driver", "description": "Cube.js Druid database driver", "author": "Cube Dev, Inc.", - "version": "1.7.26", + "version": "1.7.27", "license": "Apache-2.0", "repository": { "type": "git", @@ -28,13 +28,13 @@ "dist/src/*" ], "dependencies": { - "@cubejs-backend/base-driver": "1.7.26", - "@cubejs-backend/schema-compiler": "1.7.26", - "@cubejs-backend/shared": "1.7.26", + "@cubejs-backend/base-driver": "1.7.27", + "@cubejs-backend/schema-compiler": "1.7.27", + "@cubejs-backend/shared": "1.7.27", "axios": "^1.8.3" }, "devDependencies": { - "@cubejs-backend/linter": "1.7.26", + "@cubejs-backend/linter": "1.7.27", "@types/jest": "^29", "@types/node": "^22", "jest": "^29", diff --git a/packages/cubejs-druid-driver/src/DruidQuery.ts b/packages/cubejs-druid-driver/src/DruidQuery.ts index c4e2d212b1e61..b1d1705c9fc92 100644 --- a/packages/cubejs-druid-driver/src/DruidQuery.ts +++ b/packages/cubejs-druid-driver/src/DruidQuery.ts @@ -70,6 +70,10 @@ export class DruidQuery extends BaseQuery { templates.functions.UTCTIMESTAMP = 'CURRENT_TIMESTAMP'; delete templates.functions.WIDTH_BUCKET; + // Druid only accepts set operations in narrow forms and has no UNION of distinct rows, + // so the SQL API leaves them to post processing here. + delete templates.statements.union; + return templates; } } diff --git a/packages/cubejs-duckdb-driver/CHANGELOG.md b/packages/cubejs-duckdb-driver/CHANGELOG.md index 1569068c897f9..abe2bad845565 100644 --- a/packages/cubejs-duckdb-driver/CHANGELOG.md +++ b/packages/cubejs-duckdb-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.27](https://github.com/cube-js/cube/compare/v1.7.26...v1.7.27) (2026-08-26) + +**Note:** Version bump only for package @cubejs-backend/duckdb-driver + ## [1.7.26](https://github.com/cube-js/cube/compare/v1.7.25...v1.7.26) (2026-08-24) **Note:** Version bump only for package @cubejs-backend/duckdb-driver diff --git a/packages/cubejs-duckdb-driver/package.json b/packages/cubejs-duckdb-driver/package.json index 1f91f1f8c7685..e4a578538d120 100644 --- a/packages/cubejs-duckdb-driver/package.json +++ b/packages/cubejs-duckdb-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/duckdb-driver", "description": "Cube DuckDB database driver", "author": "Cube Dev, Inc.", - "version": "1.7.26", + "version": "1.7.27", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -28,15 +28,15 @@ "lint:fix": "eslint --fix src/* --ext .ts" }, "dependencies": { - "@cubejs-backend/base-driver": "1.7.26", - "@cubejs-backend/schema-compiler": "1.7.26", - "@cubejs-backend/shared": "1.7.26", + "@cubejs-backend/base-driver": "1.7.27", + "@cubejs-backend/schema-compiler": "1.7.27", + "@cubejs-backend/shared": "1.7.27", "duckdb": "^1.4.1" }, "license": "Apache-2.0", "devDependencies": { - "@cubejs-backend/linter": "1.7.26", - "@cubejs-backend/testing-shared": "1.7.26", + "@cubejs-backend/linter": "1.7.27", + "@cubejs-backend/testing-shared": "1.7.27", "@types/jest": "^29", "@types/node": "^22", "jest": "^29", diff --git a/packages/cubejs-firebolt-driver/CHANGELOG.md b/packages/cubejs-firebolt-driver/CHANGELOG.md index 71bb906216d6a..0f06bd44d9f04 100644 --- a/packages/cubejs-firebolt-driver/CHANGELOG.md +++ b/packages/cubejs-firebolt-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.27](https://github.com/cube-js/cube/compare/v1.7.26...v1.7.27) (2026-08-26) + +**Note:** Version bump only for package @cubejs-backend/firebolt-driver + ## [1.7.26](https://github.com/cube-js/cube/compare/v1.7.25...v1.7.26) (2026-08-24) **Note:** Version bump only for package @cubejs-backend/firebolt-driver diff --git a/packages/cubejs-firebolt-driver/package.json b/packages/cubejs-firebolt-driver/package.json index 9bac3b7fc7888..f6be80adc7467 100644 --- a/packages/cubejs-firebolt-driver/package.json +++ b/packages/cubejs-firebolt-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/firebolt-driver", "description": "Cube.js Firebolt database driver", "author": "Cube Dev, Inc.", - "version": "1.7.26", + "version": "1.7.27", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -28,15 +28,15 @@ "lint:fix": "eslint --fix src/* --ext .ts" }, "dependencies": { - "@cubejs-backend/base-driver": "1.7.26", - "@cubejs-backend/schema-compiler": "1.7.26", - "@cubejs-backend/shared": "1.7.26", + "@cubejs-backend/base-driver": "1.7.27", + "@cubejs-backend/schema-compiler": "1.7.27", + "@cubejs-backend/shared": "1.7.27", "firebolt-sdk": "1.10.0" }, "license": "Apache-2.0", "devDependencies": { - "@cubejs-backend/linter": "1.7.26", - "@cubejs-backend/testing-shared": "1.7.26", + "@cubejs-backend/linter": "1.7.27", + "@cubejs-backend/testing-shared": "1.7.27", "typescript": "~5.2.2" }, "publishConfig": { diff --git a/packages/cubejs-hive-driver/CHANGELOG.md b/packages/cubejs-hive-driver/CHANGELOG.md index 44e314a6f8626..db347e65440e9 100644 --- a/packages/cubejs-hive-driver/CHANGELOG.md +++ b/packages/cubejs-hive-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.27](https://github.com/cube-js/cube/compare/v1.7.26...v1.7.27) (2026-08-26) + +**Note:** Version bump only for package @cubejs-backend/hive-driver + ## [1.7.26](https://github.com/cube-js/cube/compare/v1.7.25...v1.7.26) (2026-08-24) **Note:** Version bump only for package @cubejs-backend/hive-driver diff --git a/packages/cubejs-hive-driver/package.json b/packages/cubejs-hive-driver/package.json index 8eb308a5234c0..002911ad2f84f 100644 --- a/packages/cubejs-hive-driver/package.json +++ b/packages/cubejs-hive-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/hive-driver", "description": "Cube.js Hive database driver", "author": "Cube Dev, Inc.", - "version": "1.7.26", + "version": "1.7.27", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -17,8 +17,8 @@ "lint:fix": "eslint --fix src/* --ext .ts" }, "dependencies": { - "@cubejs-backend/base-driver": "1.7.26", - "@cubejs-backend/shared": "1.7.26", + "@cubejs-backend/base-driver": "1.7.27", + "@cubejs-backend/shared": "1.7.27", "jshs2": "^0.4.4", "sasl-plain": "^0.1.0", "saslmechanisms": "^0.1.1", @@ -27,7 +27,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@cubejs-backend/linter": "1.7.26" + "@cubejs-backend/linter": "1.7.27" }, "publishConfig": { "access": "public" diff --git a/packages/cubejs-jdbc-driver/CHANGELOG.md b/packages/cubejs-jdbc-driver/CHANGELOG.md index 595c1b79a940d..42fbda5675168 100644 --- a/packages/cubejs-jdbc-driver/CHANGELOG.md +++ b/packages/cubejs-jdbc-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.27](https://github.com/cube-js/cube/compare/v1.7.26...v1.7.27) (2026-08-26) + +**Note:** Version bump only for package @cubejs-backend/jdbc-driver + ## [1.7.26](https://github.com/cube-js/cube/compare/v1.7.25...v1.7.26) (2026-08-24) **Note:** Version bump only for package @cubejs-backend/jdbc-driver diff --git a/packages/cubejs-jdbc-driver/package.json b/packages/cubejs-jdbc-driver/package.json index 732e01b61aa98..90e1f7eff4769 100644 --- a/packages/cubejs-jdbc-driver/package.json +++ b/packages/cubejs-jdbc-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/jdbc-driver", "description": "Cube.js JDBC database driver", "author": "Cube Dev, Inc.", - "version": "1.7.26", + "version": "1.7.27", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -26,9 +26,9 @@ "index.js" ], "dependencies": { - "@cubejs-backend/base-driver": "1.7.26", + "@cubejs-backend/base-driver": "1.7.27", "@cubejs-backend/node-java-maven": "^0.1.3", - "@cubejs-backend/shared": "1.7.26" + "@cubejs-backend/shared": "1.7.27" }, "optionalDependencies": { "@cubejs-backend/jdbc": "^0.9.0", @@ -42,7 +42,7 @@ "testEnvironment": "node" }, "devDependencies": { - "@cubejs-backend/linter": "1.7.26", + "@cubejs-backend/linter": "1.7.27", "@types/node": "^22", "typescript": "~5.2.2" } diff --git a/packages/cubejs-ksql-driver/CHANGELOG.md b/packages/cubejs-ksql-driver/CHANGELOG.md index 6d44a31b2560c..df0cae67705d3 100644 --- a/packages/cubejs-ksql-driver/CHANGELOG.md +++ b/packages/cubejs-ksql-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.27](https://github.com/cube-js/cube/compare/v1.7.26...v1.7.27) (2026-08-26) + +**Note:** Version bump only for package @cubejs-backend/ksql-driver + ## [1.7.26](https://github.com/cube-js/cube/compare/v1.7.25...v1.7.26) (2026-08-24) **Note:** Version bump only for package @cubejs-backend/ksql-driver diff --git a/packages/cubejs-ksql-driver/package.json b/packages/cubejs-ksql-driver/package.json index fdbf79ea4ed6f..890bebc780389 100644 --- a/packages/cubejs-ksql-driver/package.json +++ b/packages/cubejs-ksql-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/ksql-driver", "description": "Cube.js ksql database driver", "author": "Cube Dev, Inc.", - "version": "1.7.26", + "version": "1.7.27", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -26,9 +26,9 @@ "lint:fix": "eslint --fix src/* --ext .ts" }, "dependencies": { - "@cubejs-backend/base-driver": "1.7.26", - "@cubejs-backend/schema-compiler": "1.7.26", - "@cubejs-backend/shared": "1.7.26", + "@cubejs-backend/base-driver": "1.7.27", + "@cubejs-backend/schema-compiler": "1.7.27", + "@cubejs-backend/shared": "1.7.27", "async-mutex": "0.3.2", "axios": "^1.8.3", "kafkajs": "^2.2.3" @@ -41,7 +41,7 @@ "extends": "../cubejs-linter" }, "devDependencies": { - "@cubejs-backend/linter": "1.7.26", + "@cubejs-backend/linter": "1.7.27", "typescript": "~5.2.2" } } diff --git a/packages/cubejs-ksql-driver/src/KsqlQuery.ts b/packages/cubejs-ksql-driver/src/KsqlQuery.ts index 3852e647e1ff4..6dd514deef912 100644 --- a/packages/cubejs-ksql-driver/src/KsqlQuery.ts +++ b/packages/cubejs-ksql-driver/src/KsqlQuery.ts @@ -79,6 +79,9 @@ export class KsqlQuery extends BaseQuery { // expressions instead of column ordinals. templates.statements.group_by_exprs = '{{ group_by | map(attribute=\'expr\') | join(\', \') }}'; delete templates.functions.WIDTH_BUCKET; + // ksqlDB has no set operations, so the SQL API leaves them to post processing here. + delete templates.statements.union; + return templates; } diff --git a/packages/cubejs-linter/CHANGELOG.md b/packages/cubejs-linter/CHANGELOG.md index 94948ab77553f..3282e36658616 100644 --- a/packages/cubejs-linter/CHANGELOG.md +++ b/packages/cubejs-linter/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.27](https://github.com/cube-js/cube/compare/v1.7.26...v1.7.27) (2026-08-26) + +**Note:** Version bump only for package @cubejs-backend/linter + ## [1.7.26](https://github.com/cube-js/cube/compare/v1.7.25...v1.7.26) (2026-08-24) **Note:** Version bump only for package @cubejs-backend/linter diff --git a/packages/cubejs-linter/package.json b/packages/cubejs-linter/package.json index 0ac40a7201ef6..bcf436e7c1a9d 100644 --- a/packages/cubejs-linter/package.json +++ b/packages/cubejs-linter/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/linter", "description": "Cube.js ESLint (virtual package) for linting code", "author": "Cube Dev, Inc.", - "version": "1.7.26", + "version": "1.7.27", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", diff --git a/packages/cubejs-materialize-driver/CHANGELOG.md b/packages/cubejs-materialize-driver/CHANGELOG.md index 174cd54a021a6..08ba06d695699 100644 --- a/packages/cubejs-materialize-driver/CHANGELOG.md +++ b/packages/cubejs-materialize-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.27](https://github.com/cube-js/cube/compare/v1.7.26...v1.7.27) (2026-08-26) + +**Note:** Version bump only for package @cubejs-backend/materialize-driver + ## [1.7.26](https://github.com/cube-js/cube/compare/v1.7.25...v1.7.26) (2026-08-24) **Note:** Version bump only for package @cubejs-backend/materialize-driver diff --git a/packages/cubejs-materialize-driver/package.json b/packages/cubejs-materialize-driver/package.json index 9b1b2e073d778..f6d8a5a1f6cb6 100644 --- a/packages/cubejs-materialize-driver/package.json +++ b/packages/cubejs-materialize-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/materialize-driver", "description": "Cube.js Materialize database driver", "author": "Cube Dev, Inc.", - "version": "1.7.26", + "version": "1.7.27", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -27,15 +27,15 @@ "lint:fix": "eslint --fix src/* --ext .ts" }, "dependencies": { - "@cubejs-backend/base-driver": "1.7.26", - "@cubejs-backend/postgres-driver": "1.7.26", - "@cubejs-backend/shared": "1.7.26", + "@cubejs-backend/base-driver": "1.7.27", + "@cubejs-backend/postgres-driver": "1.7.27", + "@cubejs-backend/shared": "1.7.27", "semver": "^7.6.3" }, "license": "Apache-2.0", "devDependencies": { - "@cubejs-backend/linter": "1.7.26", - "@cubejs-backend/testing": "1.7.26", + "@cubejs-backend/linter": "1.7.27", + "@cubejs-backend/testing": "1.7.27", "typescript": "~5.2.2" }, "publishConfig": { diff --git a/packages/cubejs-mongobi-driver/CHANGELOG.md b/packages/cubejs-mongobi-driver/CHANGELOG.md index 33a242b9eac65..1cfb8a0c2905b 100644 --- a/packages/cubejs-mongobi-driver/CHANGELOG.md +++ b/packages/cubejs-mongobi-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.27](https://github.com/cube-js/cube/compare/v1.7.26...v1.7.27) (2026-08-26) + +**Note:** Version bump only for package @cubejs-backend/mongobi-driver + ## [1.7.26](https://github.com/cube-js/cube/compare/v1.7.25...v1.7.26) (2026-08-24) **Note:** Version bump only for package @cubejs-backend/mongobi-driver diff --git a/packages/cubejs-mongobi-driver/package.json b/packages/cubejs-mongobi-driver/package.json index c50fc8c9a264e..1531c40ef1a43 100644 --- a/packages/cubejs-mongobi-driver/package.json +++ b/packages/cubejs-mongobi-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/mongobi-driver", "description": "Cube.js MongoBI driver", "author": "krunalsabnis@gmail.com", - "version": "1.7.26", + "version": "1.7.27", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -27,8 +27,8 @@ "integration:mongobi": "jest dist/test" }, "dependencies": { - "@cubejs-backend/base-driver": "1.7.26", - "@cubejs-backend/shared": "1.7.26", + "@cubejs-backend/base-driver": "1.7.27", + "@cubejs-backend/shared": "1.7.27", "@types/node": "^22", "moment": "^2.29.1", "mysql2": "^3.11.5" @@ -38,7 +38,7 @@ "access": "public" }, "devDependencies": { - "@cubejs-backend/linter": "1.7.26", + "@cubejs-backend/linter": "1.7.27", "testcontainers": "^10.28.0", "typescript": "~5.2.2" }, diff --git a/packages/cubejs-mssql-driver/CHANGELOG.md b/packages/cubejs-mssql-driver/CHANGELOG.md index f444830adaf56..a1ed20066cad3 100644 --- a/packages/cubejs-mssql-driver/CHANGELOG.md +++ b/packages/cubejs-mssql-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.27](https://github.com/cube-js/cube/compare/v1.7.26...v1.7.27) (2026-08-26) + +**Note:** Version bump only for package @cubejs-backend/mssql-driver + ## [1.7.26](https://github.com/cube-js/cube/compare/v1.7.25...v1.7.26) (2026-08-24) **Note:** Version bump only for package @cubejs-backend/mssql-driver diff --git a/packages/cubejs-mssql-driver/package.json b/packages/cubejs-mssql-driver/package.json index 2a16b5b0d948d..bca390144ac1e 100644 --- a/packages/cubejs-mssql-driver/package.json +++ b/packages/cubejs-mssql-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/mssql-driver", "description": "Cube.js MS SQL database driver", "author": "Cube Dev, Inc.", - "version": "1.7.26", + "version": "1.7.27", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -25,8 +25,8 @@ "lint:fix": "eslint --fix src/* --ext .ts,.js" }, "dependencies": { - "@cubejs-backend/base-driver": "1.7.26", - "@cubejs-backend/shared": "1.7.26", + "@cubejs-backend/base-driver": "1.7.27", + "@cubejs-backend/shared": "1.7.27", "mssql": "^11.0.1" }, "devDependencies": { diff --git a/packages/cubejs-mysql-aurora-serverless-driver/CHANGELOG.md b/packages/cubejs-mysql-aurora-serverless-driver/CHANGELOG.md index 072ee9c551c5b..71d3e0542ebe8 100644 --- a/packages/cubejs-mysql-aurora-serverless-driver/CHANGELOG.md +++ b/packages/cubejs-mysql-aurora-serverless-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.27](https://github.com/cube-js/cube/compare/v1.7.26...v1.7.27) (2026-08-26) + +**Note:** Version bump only for package @cubejs-backend/mysql-aurora-serverless-driver + ## [1.7.26](https://github.com/cube-js/cube/compare/v1.7.25...v1.7.26) (2026-08-24) **Note:** Version bump only for package @cubejs-backend/mysql-aurora-serverless-driver diff --git a/packages/cubejs-mysql-aurora-serverless-driver/package.json b/packages/cubejs-mysql-aurora-serverless-driver/package.json index 8327b9ff39db9..751f7984415c4 100644 --- a/packages/cubejs-mysql-aurora-serverless-driver/package.json +++ b/packages/cubejs-mysql-aurora-serverless-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/mysql-aurora-serverless-driver", "description": "Cube.js Aurora Serverless Mysql database driver", "author": "Cube Dev, Inc.", - "version": "1.7.26", + "version": "1.7.27", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -21,14 +21,14 @@ "lint": "eslint driver/*.js test/*.js" }, "dependencies": { - "@cubejs-backend/base-driver": "1.7.26", - "@cubejs-backend/shared": "1.7.26", + "@cubejs-backend/base-driver": "1.7.27", + "@cubejs-backend/shared": "1.7.27", "@types/mysql": "^2.15.15", "aws-sdk": "^2.787.0", "data-api-client": "^1.1.0" }, "devDependencies": { - "@cubejs-backend/linter": "1.7.26", + "@cubejs-backend/linter": "1.7.27", "@types/data-api-client": "^1.2.1", "@types/jest": "^29", "jest": "^29", diff --git a/packages/cubejs-mysql-driver/CHANGELOG.md b/packages/cubejs-mysql-driver/CHANGELOG.md index 28b1f95946ee1..9764335438f1f 100644 --- a/packages/cubejs-mysql-driver/CHANGELOG.md +++ b/packages/cubejs-mysql-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.27](https://github.com/cube-js/cube/compare/v1.7.26...v1.7.27) (2026-08-26) + +**Note:** Version bump only for package @cubejs-backend/mysql-driver + ## [1.7.26](https://github.com/cube-js/cube/compare/v1.7.25...v1.7.26) (2026-08-24) **Note:** Version bump only for package @cubejs-backend/mysql-driver diff --git a/packages/cubejs-mysql-driver/package.json b/packages/cubejs-mysql-driver/package.json index 6de25bd9d92ec..8fc97a6aed142 100644 --- a/packages/cubejs-mysql-driver/package.json +++ b/packages/cubejs-mysql-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/mysql-driver", "description": "Cube.js Mysql database driver", "author": "Cube Dev, Inc.", - "version": "1.7.26", + "version": "1.7.27", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -27,13 +27,13 @@ "lint:fix": "eslint --fix src/* test/* --ext .ts,.js" }, "dependencies": { - "@cubejs-backend/base-driver": "1.7.26", - "@cubejs-backend/shared": "1.7.26", + "@cubejs-backend/base-driver": "1.7.27", + "@cubejs-backend/shared": "1.7.27", "mysql2": "^3.16.1" }, "devDependencies": { - "@cubejs-backend/linter": "1.7.26", - "@cubejs-backend/testing-shared": "1.7.26", + "@cubejs-backend/linter": "1.7.27", + "@cubejs-backend/testing-shared": "1.7.27", "@types/jest": "^29", "jest": "^29", "stream-to-array": "^2.3.0", diff --git a/packages/cubejs-oracle-driver/CHANGELOG.md b/packages/cubejs-oracle-driver/CHANGELOG.md index acba2afc9be5e..54ffc9154e3ec 100644 --- a/packages/cubejs-oracle-driver/CHANGELOG.md +++ b/packages/cubejs-oracle-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.27](https://github.com/cube-js/cube/compare/v1.7.26...v1.7.27) (2026-08-26) + +**Note:** Version bump only for package @cubejs-backend/oracle-driver + ## [1.7.26](https://github.com/cube-js/cube/compare/v1.7.25...v1.7.26) (2026-08-24) **Note:** Version bump only for package @cubejs-backend/oracle-driver diff --git a/packages/cubejs-oracle-driver/package.json b/packages/cubejs-oracle-driver/package.json index 7aa76436df20b..ba7b718b2cca4 100644 --- a/packages/cubejs-oracle-driver/package.json +++ b/packages/cubejs-oracle-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/oracle-driver", "description": "Cube.js oracle database driver", "author": "Cube Dev, Inc.", - "version": "1.7.26", + "version": "1.7.27", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -13,8 +13,8 @@ }, "main": "driver/OracleDriver.js", "dependencies": { - "@cubejs-backend/base-driver": "1.7.26", - "@cubejs-backend/shared": "1.7.26", + "@cubejs-backend/base-driver": "1.7.27", + "@cubejs-backend/shared": "1.7.27", "ramda": "^0.27.0" }, "devDependencies": { diff --git a/packages/cubejs-pinot-driver/CHANGELOG.md b/packages/cubejs-pinot-driver/CHANGELOG.md index f095f3a319063..2d632f4e3f1c5 100644 --- a/packages/cubejs-pinot-driver/CHANGELOG.md +++ b/packages/cubejs-pinot-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.27](https://github.com/cube-js/cube/compare/v1.7.26...v1.7.27) (2026-08-26) + +**Note:** Version bump only for package @cubejs-backend/pinot-driver + ## [1.7.26](https://github.com/cube-js/cube/compare/v1.7.25...v1.7.26) (2026-08-24) **Note:** Version bump only for package @cubejs-backend/pinot-driver diff --git a/packages/cubejs-pinot-driver/package.json b/packages/cubejs-pinot-driver/package.json index 56773972473fe..f4b1ccacc9d3c 100644 --- a/packages/cubejs-pinot-driver/package.json +++ b/packages/cubejs-pinot-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/pinot-driver", "description": "Cube.js Pinot database driver", "author": "Julian Ronsse, InTheMemory, Cube Dev, Inc.", - "version": "1.7.26", + "version": "1.7.27", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -28,9 +28,9 @@ "lint:fix": "eslint --fix src/* --ext .ts" }, "dependencies": { - "@cubejs-backend/base-driver": "1.7.26", - "@cubejs-backend/schema-compiler": "1.7.26", - "@cubejs-backend/shared": "1.7.26", + "@cubejs-backend/base-driver": "1.7.27", + "@cubejs-backend/schema-compiler": "1.7.27", + "@cubejs-backend/shared": "1.7.27", "node-fetch": "^2.6.1", "ramda": "^0.27.2" }, @@ -39,7 +39,7 @@ "access": "public" }, "devDependencies": { - "@cubejs-backend/linter": "1.7.26", + "@cubejs-backend/linter": "1.7.27", "@types/jest": "^29", "jest": "^29", "should": "^13.2.3", diff --git a/packages/cubejs-playground/CHANGELOG.md b/packages/cubejs-playground/CHANGELOG.md index 113bbe1e85e17..d7d581fec092d 100644 --- a/packages/cubejs-playground/CHANGELOG.md +++ b/packages/cubejs-playground/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.27](https://github.com/cube-js/cube/compare/v1.7.26...v1.7.27) (2026-08-26) + +**Note:** Version bump only for package @cubejs-client/playground + ## [1.7.26](https://github.com/cube-js/cube/compare/v1.7.25...v1.7.26) (2026-08-24) **Note:** Version bump only for package @cubejs-client/playground diff --git a/packages/cubejs-playground/package.json b/packages/cubejs-playground/package.json index 9e298c93f6961..db613bde80caf 100644 --- a/packages/cubejs-playground/package.json +++ b/packages/cubejs-playground/package.json @@ -1,7 +1,7 @@ { "name": "@cubejs-client/playground", "author": "Cube Dev, Inc.", - "version": "1.7.26", + "version": "1.7.27", "engines": {}, "repository": { "type": "git", @@ -68,8 +68,8 @@ "@ant-design/compatible": "^1.0.1", "@ant-design/icons": "^5.3.5", "@cube-dev/ui-kit": "0.52.3", - "@cubejs-client/core": "1.7.26", - "@cubejs-client/react": "1.7.26", + "@cubejs-client/core": "1.7.27", + "@cubejs-client/react": "1.7.27", "@types/flexsearch": "^0.7.3", "@types/node": "^22", "@types/react": "^18.3.4", diff --git a/packages/cubejs-postgres-driver/CHANGELOG.md b/packages/cubejs-postgres-driver/CHANGELOG.md index 751b4c509a9e7..ca607a388629d 100644 --- a/packages/cubejs-postgres-driver/CHANGELOG.md +++ b/packages/cubejs-postgres-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.27](https://github.com/cube-js/cube/compare/v1.7.26...v1.7.27) (2026-08-26) + +**Note:** Version bump only for package @cubejs-backend/postgres-driver + ## [1.7.26](https://github.com/cube-js/cube/compare/v1.7.25...v1.7.26) (2026-08-24) **Note:** Version bump only for package @cubejs-backend/postgres-driver diff --git a/packages/cubejs-postgres-driver/package.json b/packages/cubejs-postgres-driver/package.json index 1b012e469ae3e..9dcc61a593613 100644 --- a/packages/cubejs-postgres-driver/package.json +++ b/packages/cubejs-postgres-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/postgres-driver", "description": "Cube.js Postgres database driver", "author": "Cube Dev, Inc.", - "version": "1.7.26", + "version": "1.7.27", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -27,8 +27,8 @@ "lint:fix": "eslint --fix src/* --ext .ts" }, "dependencies": { - "@cubejs-backend/base-driver": "1.7.26", - "@cubejs-backend/shared": "1.7.26", + "@cubejs-backend/base-driver": "1.7.27", + "@cubejs-backend/shared": "1.7.27", "@types/pg": "^8.16.0", "@types/pg-query-stream": "^1.0.3", "pg": "^8.18.0", @@ -36,8 +36,8 @@ }, "license": "Apache-2.0", "devDependencies": { - "@cubejs-backend/linter": "1.7.26", - "@cubejs-backend/testing-shared": "1.7.26", + "@cubejs-backend/linter": "1.7.27", + "@cubejs-backend/testing-shared": "1.7.27", "testcontainers": "^10.28.0", "typescript": "~5.2.2" }, diff --git a/packages/cubejs-prestodb-driver/CHANGELOG.md b/packages/cubejs-prestodb-driver/CHANGELOG.md index 5d91857bb9ae0..3b90a03a0eaf8 100644 --- a/packages/cubejs-prestodb-driver/CHANGELOG.md +++ b/packages/cubejs-prestodb-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.27](https://github.com/cube-js/cube/compare/v1.7.26...v1.7.27) (2026-08-26) + +**Note:** Version bump only for package @cubejs-backend/prestodb-driver + ## [1.7.26](https://github.com/cube-js/cube/compare/v1.7.25...v1.7.26) (2026-08-24) **Note:** Version bump only for package @cubejs-backend/prestodb-driver diff --git a/packages/cubejs-prestodb-driver/package.json b/packages/cubejs-prestodb-driver/package.json index f9f22613adcc3..299d712441a16 100644 --- a/packages/cubejs-prestodb-driver/package.json +++ b/packages/cubejs-prestodb-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/prestodb-driver", "description": "Cube.js Presto database driver", "author": "Cube Dev, Inc.", - "version": "1.7.26", + "version": "1.7.27", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -28,8 +28,8 @@ "lint:fix": "eslint --fix src/* --ext .ts" }, "dependencies": { - "@cubejs-backend/base-driver": "1.7.26", - "@cubejs-backend/shared": "1.7.26", + "@cubejs-backend/base-driver": "1.7.27", + "@cubejs-backend/shared": "1.7.27", "presto-client": "1.2.0", "ramda": "^0.27.0" }, @@ -38,7 +38,7 @@ "access": "public" }, "devDependencies": { - "@cubejs-backend/linter": "1.7.26", + "@cubejs-backend/linter": "1.7.27", "@types/jest": "^29", "jest": "^29", "should": "^13.2.3", diff --git a/packages/cubejs-query-orchestrator/CHANGELOG.md b/packages/cubejs-query-orchestrator/CHANGELOG.md index 92391f70bdb53..323fa211865be 100644 --- a/packages/cubejs-query-orchestrator/CHANGELOG.md +++ b/packages/cubejs-query-orchestrator/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.27](https://github.com/cube-js/cube/compare/v1.7.26...v1.7.27) (2026-08-26) + +**Note:** Version bump only for package @cubejs-backend/query-orchestrator + ## [1.7.26](https://github.com/cube-js/cube/compare/v1.7.25...v1.7.26) (2026-08-24) ### Features diff --git a/packages/cubejs-query-orchestrator/package.json b/packages/cubejs-query-orchestrator/package.json index 35f95486d71ce..8c5dd1d79ef19 100644 --- a/packages/cubejs-query-orchestrator/package.json +++ b/packages/cubejs-query-orchestrator/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/query-orchestrator", "description": "Cube.js Query Orchestrator and Cache", "author": "Cube Dev, Inc.", - "version": "1.7.26", + "version": "1.7.27", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -30,15 +30,15 @@ "dist/src/*" ], "dependencies": { - "@cubejs-backend/base-driver": "1.7.26", - "@cubejs-backend/cubestore-driver": "1.7.26", - "@cubejs-backend/shared": "1.7.26", + "@cubejs-backend/base-driver": "1.7.27", + "@cubejs-backend/cubestore-driver": "1.7.27", + "@cubejs-backend/shared": "1.7.27", "csv-write-stream": "^2.0.0", "lru-cache": "^11.1.0", "ramda": "^0.27.2" }, "devDependencies": { - "@cubejs-backend/linter": "1.7.26", + "@cubejs-backend/linter": "1.7.27", "@types/jest": "^29", "@types/node": "^22", "@types/ramda": "^0.27.32", diff --git a/packages/cubejs-query-orchestrator/src/orchestrator/QueryQueue.ts b/packages/cubejs-query-orchestrator/src/orchestrator/QueryQueue.ts index 8ad5b2f4f81d1..17b2283015099 100644 --- a/packages/cubejs-query-orchestrator/src/orchestrator/QueryQueue.ts +++ b/packages/cubejs-query-orchestrator/src/orchestrator/QueryQueue.ts @@ -812,30 +812,15 @@ export class QueryQueue { protected async retrieveQueryForProcessing(queryKeyHashed: QueryKeyHash, queueId: QueueId): Promise { const queueConnection = await this.queueDriver.createConnection(); - let query; - try { const retrieved = await queueConnection.retrieveForProcessing(queryKeyHashed, queueId); if (!retrieved) { - query = await queueConnection.getQueryDef(queryKeyHashed, null); - - // TODO Ideally streaming queries should reconcile queue here after waiting on open slot however in practice continue wait timeout reconciles faster CPU-wise - // if (query?.queryHandler === 'stream') { - // const [active] = await queueConnection.getQueryStageState(true); - // if (active && active.length > 0) { - // await Promise.race(active.map(keyHash => queueConnection.getResultBlocking(keyHash))); - // await this.reconcileQueue(); - // } - // } - this.logger('Skip processing', { queueId, - queryKey: query && query.queryKey || queryKeyHashed, - requestId: query && query.requestId, + queryKey: queryKeyHashed, queuePrefix: this.redisQueuePrefix, - query, - queryExists: !!query }); + return null; } @@ -843,8 +828,7 @@ export class QueryQueue { } catch (e: any) { this.logger('Queue storage error', { queueId, - queryKey: query && query.queryKey || queryKeyHashed, - requestId: query && query.requestId, + queryKey: queryKeyHashed, error: (e.stack || e).toString(), queuePrefix: this.redisQueuePrefix }); diff --git a/packages/cubejs-questdb-driver/CHANGELOG.md b/packages/cubejs-questdb-driver/CHANGELOG.md index fefba38b24638..a9a0a210771ca 100644 --- a/packages/cubejs-questdb-driver/CHANGELOG.md +++ b/packages/cubejs-questdb-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.27](https://github.com/cube-js/cube/compare/v1.7.26...v1.7.27) (2026-08-26) + +**Note:** Version bump only for package @cubejs-backend/questdb-driver + ## [1.7.26](https://github.com/cube-js/cube/compare/v1.7.25...v1.7.26) (2026-08-24) **Note:** Version bump only for package @cubejs-backend/questdb-driver diff --git a/packages/cubejs-questdb-driver/package.json b/packages/cubejs-questdb-driver/package.json index 7cb5367eeb164..803b3e0c1212c 100644 --- a/packages/cubejs-questdb-driver/package.json +++ b/packages/cubejs-questdb-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/questdb-driver", "description": "Cube.js QuestDB database driver", "author": "Cube Dev, Inc.", - "version": "1.7.26", + "version": "1.7.27", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -27,9 +27,9 @@ "lint:fix": "eslint --fix src/* --ext .ts" }, "dependencies": { - "@cubejs-backend/base-driver": "1.7.26", - "@cubejs-backend/schema-compiler": "1.7.26", - "@cubejs-backend/shared": "1.7.26", + "@cubejs-backend/base-driver": "1.7.27", + "@cubejs-backend/schema-compiler": "1.7.27", + "@cubejs-backend/shared": "1.7.27", "@types/pg": "^8.6.0", "moment": "^2.24.0", "pg": "^8.7.0", @@ -37,8 +37,8 @@ }, "license": "Apache-2.0", "devDependencies": { - "@cubejs-backend/linter": "1.7.26", - "@cubejs-backend/testing-shared": "1.7.26", + "@cubejs-backend/linter": "1.7.27", + "@cubejs-backend/testing-shared": "1.7.27", "testcontainers": "^10.28.0", "typescript": "~5.2.2" }, diff --git a/packages/cubejs-redshift-driver/CHANGELOG.md b/packages/cubejs-redshift-driver/CHANGELOG.md index a1bc797192e93..b70f383023689 100644 --- a/packages/cubejs-redshift-driver/CHANGELOG.md +++ b/packages/cubejs-redshift-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.27](https://github.com/cube-js/cube/compare/v1.7.26...v1.7.27) (2026-08-26) + +**Note:** Version bump only for package @cubejs-backend/redshift-driver + ## [1.7.26](https://github.com/cube-js/cube/compare/v1.7.25...v1.7.26) (2026-08-24) **Note:** Version bump only for package @cubejs-backend/redshift-driver diff --git a/packages/cubejs-redshift-driver/package.json b/packages/cubejs-redshift-driver/package.json index a40b7c9b84e75..2d3c3468e3354 100644 --- a/packages/cubejs-redshift-driver/package.json +++ b/packages/cubejs-redshift-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/redshift-driver", "description": "Cube.js Redshift database driver", "author": "Cube Dev, Inc.", - "version": "1.7.26", + "version": "1.7.27", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -27,13 +27,13 @@ "dependencies": { "@aws-sdk/client-redshift": "^3.22.0", "@aws-sdk/credential-providers": "^3.22.0", - "@cubejs-backend/base-driver": "1.7.26", - "@cubejs-backend/postgres-driver": "1.7.26", - "@cubejs-backend/shared": "1.7.26" + "@cubejs-backend/base-driver": "1.7.27", + "@cubejs-backend/postgres-driver": "1.7.27", + "@cubejs-backend/shared": "1.7.27" }, "license": "Apache-2.0", "devDependencies": { - "@cubejs-backend/linter": "1.7.26", + "@cubejs-backend/linter": "1.7.27", "typescript": "~5.2.2" }, "publishConfig": { diff --git a/packages/cubejs-schema-compiler/CHANGELOG.md b/packages/cubejs-schema-compiler/CHANGELOG.md index f4845fd91abe7..71ae6ab9fa685 100644 --- a/packages/cubejs-schema-compiler/CHANGELOG.md +++ b/packages/cubejs-schema-compiler/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.27](https://github.com/cube-js/cube/compare/v1.7.26...v1.7.27) (2026-08-26) + +### Bug Fixes + +- **schema-compiler:** a cube that extends another broke the parent's multi-stage measures ([#11641](https://github.com/cube-js/cube/issues/11641)) ([657f4e1](https://github.com/cube-js/cube/commit/657f4e18ae75c69dd4db4057e14867f8a7d91247)) + ## [1.7.26](https://github.com/cube-js/cube/compare/v1.7.25...v1.7.26) (2026-08-24) ### Bug Fixes diff --git a/packages/cubejs-schema-compiler/package.json b/packages/cubejs-schema-compiler/package.json index 640969ba19ff3..3f7833bee51fd 100644 --- a/packages/cubejs-schema-compiler/package.json +++ b/packages/cubejs-schema-compiler/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/schema-compiler", "description": "Cube schema compiler", "author": "Cube Dev, Inc.", - "version": "1.7.26", + "version": "1.7.27", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -40,8 +40,8 @@ "@babel/standalone": "^7.24", "@babel/traverse": "^7.24", "@babel/types": "^7.24", - "@cubejs-backend/native": "1.7.26", - "@cubejs-backend/shared": "1.7.26", + "@cubejs-backend/native": "1.7.27", + "@cubejs-backend/shared": "1.7.27", "antlr4": "^4.13.2", "camelcase": "^6.2.0", "cron-parser": "^4.9.0", @@ -60,9 +60,9 @@ }, "devDependencies": { "@clickhouse/client": "^1.12.0", - "@cubejs-backend/linter": "1.7.26", - "@cubejs-backend/mssql-driver": "1.7.26", - "@cubejs-backend/query-orchestrator": "1.7.26", + "@cubejs-backend/linter": "1.7.27", + "@cubejs-backend/mssql-driver": "1.7.27", + "@cubejs-backend/query-orchestrator": "1.7.27", "@types/babel__code-frame": "^7.0.6", "@types/babel__generator": "^7.6.8", "@types/babel__traverse": "^7.20.5", diff --git a/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js b/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js index b74e4b5279b60..5649c9bc9554a 100644 --- a/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js +++ b/packages/cubejs-schema-compiler/src/adapter/BaseQuery.js @@ -4600,6 +4600,12 @@ export class BaseQuery { '{% if offset is not none %}\nOFFSET {{ offset }}{% endif %}', group_by_exprs: '{{ group_by | map(attribute=\'index\') | join(\', \') }}', join: '{{ join_type }} JOIN {{ source }} ON {{ condition }}', + union: '{% for query in queries %}(\n' + + '{{ query | indent(2, true) }}\n' + + ')' + + '{% if not loop.last %}\nUNION {% if not distinct %}ALL {% endif %}{% endif %}' + + '{% endfor %}' + + '{% if limit is not none %}\nLIMIT {{ limit }}{% endif %}', cte: '{{ alias }} AS ({{ query | indent(2, true) }})', time_series_select: 'SELECT date_from::timestamp AS "date_from",\n' + 'date_to::timestamp AS "date_to" \n' + diff --git a/packages/cubejs-schema-compiler/src/adapter/BigqueryQuery.ts b/packages/cubejs-schema-compiler/src/adapter/BigqueryQuery.ts index eb773b66b7598..a161b955b9a44 100644 --- a/packages/cubejs-schema-compiler/src/adapter/BigqueryQuery.ts +++ b/packages/cubejs-schema-compiler/src/adapter/BigqueryQuery.ts @@ -383,6 +383,14 @@ export class BigqueryQuery extends BaseQuery { 'GENERATE_TIMESTAMP_ARRAY(TIMESTAMP({{ range_source }}.{{ min_name }}), TIMESTAMP({{ range_source }}.{{ max_name }}), INTERVAL {{ granularity }})\n' + '{% endif %}' + ') AS d'; + // GoogleSQL requires a set operator to say which mode it is: a bare UNION does not parse. + templates.statements.union = '{% for query in queries %}(\n' + + '{{ query | indent(2, true) }}\n' + + ')' + + '{% if not loop.last %}\nUNION {% if distinct %}DISTINCT{% else %}ALL{% endif %} {% endif %}' + + '{% endfor %}' + + '{% if limit is not none %}\nLIMIT {{ limit }}{% endif %}'; + return templates; } } diff --git a/packages/cubejs-schema-compiler/src/adapter/ClickHouseQuery.ts b/packages/cubejs-schema-compiler/src/adapter/ClickHouseQuery.ts index 6a988b54e92f8..427b383c1f04b 100644 --- a/packages/cubejs-schema-compiler/src/adapter/ClickHouseQuery.ts +++ b/packages/cubejs-schema-compiler/src/adapter/ClickHouseQuery.ts @@ -295,6 +295,15 @@ export class ClickHouseQuery extends BaseQuery { '{% endfor %}' + ') AS dates'; + // ClickHouse rejects a bare UNION unless `union_default_mode` is set, so a set + // operation has to say which one it is. + templates.statements.union = '{% for query in queries %}(\n' + + '{{ query | indent(2, true) }}\n' + + ')' + + '{% if not loop.last %}\nUNION {% if distinct %}DISTINCT{% else %}ALL{% endif %} {% endif %}' + + '{% endfor %}' + + '{% if limit is not none %}\nLIMIT {{ limit }}{% endif %}'; + return templates; } } diff --git a/packages/cubejs-schema-compiler/src/adapter/MssqlQuery.ts b/packages/cubejs-schema-compiler/src/adapter/MssqlQuery.ts index cfde724a5c916..0eedd8690482d 100644 --- a/packages/cubejs-schema-compiler/src/adapter/MssqlQuery.ts +++ b/packages/cubejs-schema-compiler/src/adapter/MssqlQuery.ts @@ -359,6 +359,16 @@ export class MssqlQuery extends BaseQuery { '{% 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 %}'; + // T-SQL has no LIMIT, and neither TOP nor OFFSET/FETCH can be attached to a set + // operation directly (OFFSET/FETCH also requires an ORDER BY), so a bounded set + // operation is read through a derived table. + templates.statements.union = '{% if limit is not none %}SELECT TOP {{ limit }} * FROM (\n{% endif %}' + + '{% for query in queries %}(\n' + + '{{ query | indent(2, true) }}\n' + + ')' + + '{% if not loop.last %}\nUNION {% if not distinct %}ALL {% endif %}{% endif %}' + + '{% endfor %}' + + '{% if limit is not none %}\n) AS union_result{% endif %}'; // MSSQL has no boolean type — a segment projected as a dimension must be a BIT. templates.expressions.wrap_segment_select = 'CAST((CASE WHEN {{ expr }} THEN 1 ELSE 0 END) AS BIT)'; // Reading a segment back from a pre-aggregation: it is a stored BIT column, diff --git a/packages/cubejs-schema-compiler/src/adapter/OracleQuery.ts b/packages/cubejs-schema-compiler/src/adapter/OracleQuery.ts index 2546630780add..2f8dd5dde8e97 100644 --- a/packages/cubejs-schema-compiler/src/adapter/OracleQuery.ts +++ b/packages/cubejs-schema-compiler/src/adapter/OracleQuery.ts @@ -245,6 +245,13 @@ export class OracleQuery extends BaseQuery { '{% if order_by %}\nORDER BY {{ order_by | map(attribute=\'expr\') | join(\', \') }}{% endif %}' + '{% if offset is not none %}\nOFFSET {{ offset }} ROWS{% endif %}' + '{% if limit is not none %}\nFETCH NEXT {{ limit }} ROWS ONLY{% endif %}'; + // Oracle row-limiting syntax again: the base template ends a set operation with LIMIT. + templates.statements.union = '{% for query in queries %}(\n' + + '{{ query | indent(2, true) }}\n' + + ')' + + '{% if not loop.last %}\nUNION {% if not distinct %}ALL {% endif %}{% endif %}' + + '{% endfor %}' + + '{% if limit is not none %}\nFETCH NEXT {{ limit }} ROWS ONLY{% endif %}'; // Oracle has no `::` cast and no `VALUES` row-constructor table source. Build the // series with TO_TIMESTAMP + UNION ALL SELECT ... FROM DUAL, and no `AS` before // the derived-table alias (Oracle forbids it). `seria` items are [from, to] pairs. diff --git a/packages/cubejs-schema-compiler/src/adapter/SqliteQuery.ts b/packages/cubejs-schema-compiler/src/adapter/SqliteQuery.ts index e28c488605c57..e16e38c9ca23b 100644 --- a/packages/cubejs-schema-compiler/src/adapter/SqliteQuery.ts +++ b/packages/cubejs-schema-compiler/src/adapter/SqliteQuery.ts @@ -85,6 +85,9 @@ export class SqliteQuery extends BaseQuery { public sqlTemplates() { const templates = super.sqlTemplates(); delete templates.functions.WIDTH_BUCKET; + // A compound select takes no parenthesised operands in SQLite, so the SQL API leaves + // set operations to post processing here rather than pushing them down. + delete templates.statements.union; return templates; } } diff --git a/packages/cubejs-schema-compiler/test/unit/allDialects.ts b/packages/cubejs-schema-compiler/test/unit/allDialects.ts new file mode 100644 index 0000000000000..8b94bdfc6709f --- /dev/null +++ b/packages/cubejs-schema-compiler/test/unit/allDialects.ts @@ -0,0 +1,37 @@ +import fs from 'fs'; +import path from 'path'; + +// Tests run from `dist`, so this resolves to the compiled adapters. +const ADAPTER_DIR = path.join(__dirname, '..', '..', 'src', 'adapter'); + +// The adapter directory holds far more than this; the floor is only here so a scan that +// silently matched nothing fails loudly rather than passing every invariant vacuously. +const MIN_DIALECTS = 10; + +/** + * Every dialect, read off the adapter directory rather than listed by hand, so a dialect + * added later cannot quietly escape an invariant asserted over all of them. + * + * Paired with its name, which is what a caller needs to report which dialect failed. + */ +export function allDialects(): [string, any][] { + const classes = fs.readdirSync(ADAPTER_DIR) + // `.ts` keeps this working if the tests are ever run from source. + .map(file => file.match(/^(\w+Query)\.(?:ts|js)$/)?.[1]) + .filter((name): name is string => !!name && name !== 'BaseQuery') + // eslint-disable-next-line global-require, import/no-dynamic-require + .map(name => [name, require(path.join(ADAPTER_DIR, name))[name]] as [string, any]); + + if (classes.length < MIN_DIALECTS) { + throw new Error( + `Expected the adapter directory to hold at least ${MIN_DIALECTS} dialects, found ${classes.length}` + ); + } + + return classes; +} + +export function dialect(name: string): any { + // eslint-disable-next-line global-require, import/no-dynamic-require + return require(path.join(ADAPTER_DIR, name))[name]; +} diff --git a/packages/cubejs-schema-compiler/test/unit/positional-params.test.ts b/packages/cubejs-schema-compiler/test/unit/positional-params.test.ts index e20ff044ff27d..5410da0af546a 100644 --- a/packages/cubejs-schema-compiler/test/unit/positional-params.test.ts +++ b/packages/cubejs-schema-compiler/test/unit/positional-params.test.ts @@ -1,7 +1,6 @@ -import fs from 'fs'; -import path from 'path'; import { BigqueryQuery } from '../../src/adapter/BigqueryQuery'; import { PostgresQuery } from '../../src/adapter/PostgresQuery'; +import { allDialects } from './allDialects'; import { prepareJsCompiler } from './PrepareCompiler'; // `?` placeholders are positional: a value referenced from two places in the @@ -63,23 +62,6 @@ function placeholdersCount(sql: string) { return (sql.match(/\?/g) || []).length; } -// Read off the directory rather than listed by hand, so a dialect added later -// cannot quietly escape the invariant below. -const ADAPTER_DIR = path.join(__dirname, '..', '..', 'src', 'adapter'); - -function allDialects() { - const classes = fs.readdirSync(ADAPTER_DIR) - // Tests run from `dist`, so the adapter dir holds `.js`; `.ts` keeps this - // working if they are ever run from source. - .map(file => file.match(/^(\w+Query)\.(?:ts|js)$/)?.[1]) - .filter((name): name is string => !!name && name !== 'BaseQuery') - // eslint-disable-next-line global-require, import/no-dynamic-require - .map(name => require(path.join(ADAPTER_DIR, name))[name]); - - expect(classes.length).toBeGreaterThan(10); - return classes; -} - describe('positional params', () => { // Dialects whose placeholder carries the param index are free to share a param // between placeholders; those rendering a bare placeholder are not, since the @@ -88,7 +70,7 @@ describe('positional params', () => { const { compiler, joinGraph, cubeEvaluator } = prepareJsCompiler(model); await compiler.compile(); - const reusingPositionalDialects = allDialects().filter(QueryClass => { + const reusingPositionalDialects = allDialects().filter(([, QueryClass]) => { const query = new QueryClass({ joinGraph, cubeEvaluator, compiler }, { measures: ['orders.count'], timezone: 'UTC', @@ -96,7 +78,7 @@ describe('positional params', () => { const indexedPlaceholder = query.sqlTemplates().params.param.includes('param_index'); return !indexedPlaceholder && query.shouldReuseParams; - }).map(QueryClass => QueryClass.name); + }).map(([name]) => name); expect(reusingPositionalDialects).toEqual([]); }); diff --git a/packages/cubejs-schema-compiler/test/unit/union-template.test.ts b/packages/cubejs-schema-compiler/test/unit/union-template.test.ts new file mode 100644 index 0000000000000..c5916a12a6f9a --- /dev/null +++ b/packages/cubejs-schema-compiler/test/unit/union-template.test.ts @@ -0,0 +1,71 @@ +import { allDialects, dialect } from './allDialects'; + +// `statements/union` is read by the SQL API when it pushes a set operation down to the +// data source, and by nothing else, so these assertions are what stands between an edit +// to one of these templates and a syntax error at a customer's warehouse. +// +// Only the templates are read here, off a bare prototype rather than a query built on a +// compiled model. That reads the templates a dialect writes unconditionally, which every +// `statements.union` is today; a dialect that gated one on instance state would need a +// real query here, since `this` carries nothing. +function unionTemplate(QueryClass: any): string | undefined { + return QueryClass.prototype.sqlTemplates + .call(Object.create(QueryClass.prototype)) + .statements + .union; +} + +describe('statements/union', () => { + it.each(allDialects())('%s renders every query, once, bounded', (_name, QueryClass) => { + const template = unionTemplate(QueryClass); + if (template === undefined) { + // Deliberately opted out: with no template the SQL API leaves the set operation to + // post processing rather than pushing down SQL the data source cannot parse + return; + } + + // The operator itself: without it the rendered SQL is not a set operation at all + expect(template).toMatch(/\bUNION\b/); + // Every query of the operation, rendered rather than merely looped over + expect(template).toMatch(/\{%\s*for query in queries\s*%\}/); + expect(template).toContain('{{ query'); + // The row cap bounds the result of the whole operation, and is interpolated into the + // clause that bounds it rather than only guarding it + expect(template).toMatch(/\{%\s*if limit is not none\s*%\}[^]*\{\{\s*limit\s*\}\}/); + // However the dialect spells the operator, `ALL` is what keeps the duplicates, so + // every `ALL` sits on the branch that keeps them and nowhere else. The dialects write + // that branch two ways: naming only `ALL`, or naming both modes. + const keepsDuplicates = /\{%\s*if not distinct\s*%\}ALL|\{%\s*if distinct\s*%\}DISTINCT\{%\s*else\s*%\}ALL/; + expect(template).toMatch(keepsDuplicates); + expect(template.replace(new RegExp(keepsDuplicates.source, 'g'), '')).not.toContain('ALL'); + // Balanced tags: an unclosed block renders as a template error at query time + expect((template.match(/\{%\s*if\b/g) || []).length) + .toEqual((template.match(/\{%\s*endif\s*%\}/g) || []).length); + expect((template.match(/\{%\s*for\b/g) || []).length) + .toEqual((template.match(/\{%\s*endfor\s*%\}/g) || []).length); + }); + + it('leaves the template undefined where a set operation cannot be expressed', () => { + // A compound select takes no parenthesised operand in SQLite + expect(unionTemplate(dialect('SqliteQuery'))).toBeUndefined(); + }); + + it('names the mode of the operation where the dialect requires it', () => { + // GoogleSQL and ClickHouse both reject a bare UNION + for (const name of ['BigqueryQuery', 'ClickHouseQuery']) { + expect(unionTemplate(dialect(name))).toContain('{% if distinct %}DISTINCT{% else %}ALL{% endif %}'); + } + }); + + it('bounds the operation with the row limiting clause of the dialect', () => { + const clauses: [string, string][] = [ + ['PostgresQuery', 'LIMIT {{ limit }}'], + ['OracleQuery', 'FETCH NEXT {{ limit }} ROWS ONLY'], + // Neither TOP nor OFFSET/FETCH attaches to a compound query in T-SQL + ['MssqlQuery', 'SELECT TOP {{ limit }} * FROM ('], + ]; + for (const [name, clause] of clauses) { + expect(unionTemplate(dialect(name))).toContain(clause); + } + }); +}); diff --git a/packages/cubejs-server-core/CHANGELOG.md b/packages/cubejs-server-core/CHANGELOG.md index ee520e86fe188..7429bc10d2e73 100644 --- a/packages/cubejs-server-core/CHANGELOG.md +++ b/packages/cubejs-server-core/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.27](https://github.com/cube-js/cube/compare/v1.7.26...v1.7.27) (2026-08-26) + +**Note:** Version bump only for package @cubejs-backend/server-core + ## [1.7.26](https://github.com/cube-js/cube/compare/v1.7.25...v1.7.26) (2026-08-24) ### Features diff --git a/packages/cubejs-server-core/package.json b/packages/cubejs-server-core/package.json index e3f63cc47141d..ac2cab1603083 100644 --- a/packages/cubejs-server-core/package.json +++ b/packages/cubejs-server-core/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/server-core", "description": "Cube.js base component to wire all backend components together", "author": "Cube Dev, Inc.", - "version": "1.7.26", + "version": "1.7.27", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -29,16 +29,16 @@ "unit": "jest --runInBand --forceExit --coverage dist/test" }, "dependencies": { - "@cubejs-backend/api-gateway": "1.7.26", - "@cubejs-backend/base-driver": "1.7.26", - "@cubejs-backend/cloud": "1.7.26", - "@cubejs-backend/cubestore-driver": "1.7.26", + "@cubejs-backend/api-gateway": "1.7.27", + "@cubejs-backend/base-driver": "1.7.27", + "@cubejs-backend/cloud": "1.7.27", + "@cubejs-backend/cubestore-driver": "1.7.27", "@cubejs-backend/dotenv": "^9.0.2", - "@cubejs-backend/native": "1.7.26", - "@cubejs-backend/query-orchestrator": "1.7.26", - "@cubejs-backend/schema-compiler": "1.7.26", - "@cubejs-backend/shared": "1.7.26", - "@cubejs-backend/templates": "1.7.26", + "@cubejs-backend/native": "1.7.27", + "@cubejs-backend/query-orchestrator": "1.7.27", + "@cubejs-backend/schema-compiler": "1.7.27", + "@cubejs-backend/shared": "1.7.27", + "@cubejs-backend/templates": "1.7.27", "codesandbox-import-utils": "^2.1.12", "cross-spawn": "^7.0.1", "fs-extra": "^8.1.0", @@ -62,8 +62,8 @@ "ws": "^7.5.3" }, "devDependencies": { - "@cubejs-backend/linter": "1.7.26", - "@cubejs-client/playground": "1.7.26", + "@cubejs-backend/linter": "1.7.27", + "@cubejs-client/playground": "1.7.27", "@types/cross-spawn": "^6.0.2", "@types/express": "^4.17.21", "@types/fs-extra": "^9.0.8", diff --git a/packages/cubejs-server/CHANGELOG.md b/packages/cubejs-server/CHANGELOG.md index b05544cda3056..3319b8bb6f468 100644 --- a/packages/cubejs-server/CHANGELOG.md +++ b/packages/cubejs-server/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.27](https://github.com/cube-js/cube/compare/v1.7.26...v1.7.27) (2026-08-26) + +**Note:** Version bump only for package @cubejs-backend/server + ## [1.7.26](https://github.com/cube-js/cube/compare/v1.7.25...v1.7.26) (2026-08-24) **Note:** Version bump only for package @cubejs-backend/server diff --git a/packages/cubejs-server/package.json b/packages/cubejs-server/package.json index e9b208195a197..24a63fbe17002 100644 --- a/packages/cubejs-server/package.json +++ b/packages/cubejs-server/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/server", "description": "Cube.js all-in-one server", "author": "Cube Dev, Inc.", - "version": "1.7.26", + "version": "1.7.27", "types": "index.d.ts", "repository": { "type": "git", @@ -40,11 +40,11 @@ "jest:shapshot": "jest --updateSnapshot test" }, "dependencies": { - "@cubejs-backend/cubestore-driver": "1.7.26", + "@cubejs-backend/cubestore-driver": "1.7.27", "@cubejs-backend/dotenv": "^9.0.2", - "@cubejs-backend/native": "1.7.26", - "@cubejs-backend/server-core": "1.7.26", - "@cubejs-backend/shared": "1.7.26", + "@cubejs-backend/native": "1.7.27", + "@cubejs-backend/server-core": "1.7.27", + "@cubejs-backend/shared": "1.7.27", "@oclif/color": "^1.0.0", "@oclif/command": "^1.8.13", "@oclif/config": "^1.18.2", @@ -61,8 +61,8 @@ "ws": "^7.1.2" }, "devDependencies": { - "@cubejs-backend/linter": "1.7.26", - "@cubejs-backend/query-orchestrator": "1.7.26", + "@cubejs-backend/linter": "1.7.27", + "@cubejs-backend/query-orchestrator": "1.7.27", "@oclif/dev-cli": "^1.23.1", "@types/body-parser": "^1.19.0", "@types/cors": "^2.8.8", diff --git a/packages/cubejs-snowflake-driver/CHANGELOG.md b/packages/cubejs-snowflake-driver/CHANGELOG.md index f8460aab737cd..0e5425ea53886 100644 --- a/packages/cubejs-snowflake-driver/CHANGELOG.md +++ b/packages/cubejs-snowflake-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.27](https://github.com/cube-js/cube/compare/v1.7.26...v1.7.27) (2026-08-26) + +**Note:** Version bump only for package @cubejs-backend/snowflake-driver + ## [1.7.26](https://github.com/cube-js/cube/compare/v1.7.25...v1.7.26) (2026-08-24) **Note:** Version bump only for package @cubejs-backend/snowflake-driver diff --git a/packages/cubejs-snowflake-driver/package.json b/packages/cubejs-snowflake-driver/package.json index 0c2a10f3a89a9..2565ea174ec9d 100644 --- a/packages/cubejs-snowflake-driver/package.json +++ b/packages/cubejs-snowflake-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/snowflake-driver", "description": "Cube.js Snowflake database driver", "author": "Cube Dev, Inc.", - "version": "1.7.26", + "version": "1.7.27", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -29,8 +29,8 @@ }, "dependencies": { "@aws-sdk/client-s3": "^3.726.0", - "@cubejs-backend/base-driver": "1.7.26", - "@cubejs-backend/shared": "1.7.26", + "@cubejs-backend/base-driver": "1.7.27", + "@cubejs-backend/shared": "1.7.27", "snowflake-sdk": "^2.4.0" }, "license": "Apache-2.0", @@ -41,7 +41,7 @@ "extends": "../cubejs-linter" }, "devDependencies": { - "@cubejs-backend/linter": "1.7.26", + "@cubejs-backend/linter": "1.7.27", "typescript": "~5.2.2", "vitest": "^4" } diff --git a/packages/cubejs-sqlite-driver/CHANGELOG.md b/packages/cubejs-sqlite-driver/CHANGELOG.md index 5aaabc4af61bf..5f4de4cf71311 100644 --- a/packages/cubejs-sqlite-driver/CHANGELOG.md +++ b/packages/cubejs-sqlite-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.27](https://github.com/cube-js/cube/compare/v1.7.26...v1.7.27) (2026-08-26) + +**Note:** Version bump only for package @cubejs-backend/sqlite-driver + ## [1.7.26](https://github.com/cube-js/cube/compare/v1.7.25...v1.7.26) (2026-08-24) **Note:** Version bump only for package @cubejs-backend/sqlite-driver diff --git a/packages/cubejs-sqlite-driver/package.json b/packages/cubejs-sqlite-driver/package.json index ede80338c7057..08d93e90f705e 100644 --- a/packages/cubejs-sqlite-driver/package.json +++ b/packages/cubejs-sqlite-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/sqlite-driver", "description": "Cube.js Sqlite database driver", "author": "Cube Dev, Inc.", - "version": "1.7.26", + "version": "1.7.27", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -18,13 +18,13 @@ "unit": "jest" }, "dependencies": { - "@cubejs-backend/base-driver": "1.7.26", - "@cubejs-backend/shared": "1.7.26", + "@cubejs-backend/base-driver": "1.7.27", + "@cubejs-backend/shared": "1.7.27", "sqlite3": "^5.1.7" }, "license": "Apache-2.0", "devDependencies": { - "@cubejs-backend/linter": "1.7.26", + "@cubejs-backend/linter": "1.7.27", "jest": "^29" }, "publishConfig": { diff --git a/packages/cubejs-templates/CHANGELOG.md b/packages/cubejs-templates/CHANGELOG.md index 8046deb546f72..2e30d5698b4b7 100644 --- a/packages/cubejs-templates/CHANGELOG.md +++ b/packages/cubejs-templates/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.27](https://github.com/cube-js/cube/compare/v1.7.26...v1.7.27) (2026-08-26) + +**Note:** Version bump only for package @cubejs-backend/templates + ## [1.7.26](https://github.com/cube-js/cube/compare/v1.7.25...v1.7.26) (2026-08-24) **Note:** Version bump only for package @cubejs-backend/templates diff --git a/packages/cubejs-templates/package.json b/packages/cubejs-templates/package.json index 45f93fd19abfb..3831eb914013f 100644 --- a/packages/cubejs-templates/package.json +++ b/packages/cubejs-templates/package.json @@ -1,6 +1,6 @@ { "name": "@cubejs-backend/templates", - "version": "1.7.26", + "version": "1.7.27", "description": "Cube.js Templates helpers", "author": "Cube Dev, Inc.", "repository": { @@ -31,7 +31,7 @@ "extends": "../cubejs-linter" }, "dependencies": { - "@cubejs-backend/shared": "1.7.26", + "@cubejs-backend/shared": "1.7.27", "cross-spawn": "^7.0.3", "fs-extra": "^9.1.0", "node-fetch": "^2.6.1", @@ -40,7 +40,7 @@ "tar": "^7.5.22" }, "devDependencies": { - "@cubejs-backend/linter": "1.7.26", + "@cubejs-backend/linter": "1.7.27", "typescript": "~5.2.2" } } diff --git a/packages/cubejs-testing-drivers/CHANGELOG.md b/packages/cubejs-testing-drivers/CHANGELOG.md index d2c4e9ca4f048..214e193110e57 100644 --- a/packages/cubejs-testing-drivers/CHANGELOG.md +++ b/packages/cubejs-testing-drivers/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.27](https://github.com/cube-js/cube/compare/v1.7.26...v1.7.27) (2026-08-26) + +**Note:** Version bump only for package @cubejs-backend/testing-drivers + ## [1.7.26](https://github.com/cube-js/cube/compare/v1.7.25...v1.7.26) (2026-08-24) **Note:** Version bump only for package @cubejs-backend/testing-drivers diff --git a/packages/cubejs-testing-drivers/package.json b/packages/cubejs-testing-drivers/package.json index 8aa4a6f49f549..c70c2b3513fe1 100644 --- a/packages/cubejs-testing-drivers/package.json +++ b/packages/cubejs-testing-drivers/package.json @@ -1,6 +1,6 @@ { "name": "@cubejs-backend/testing-drivers", - "version": "1.7.26", + "version": "1.7.27", "description": "Cube.js drivers test suite", "author": "Cube Dev, Inc.", "repository": { @@ -87,29 +87,29 @@ "dist/src" ], "dependencies": { - "@cubejs-backend/athena-driver": "1.7.26", - "@cubejs-backend/base-driver": "1.7.26", - "@cubejs-backend/bigquery-driver": "1.7.26", - "@cubejs-backend/clickhouse-driver": "1.7.26", - "@cubejs-backend/crate-driver": "1.7.26", - "@cubejs-backend/cubestore-driver": "1.7.26", - "@cubejs-backend/databricks-jdbc-driver": "1.7.26", + "@cubejs-backend/athena-driver": "1.7.27", + "@cubejs-backend/base-driver": "1.7.27", + "@cubejs-backend/bigquery-driver": "1.7.27", + "@cubejs-backend/clickhouse-driver": "1.7.27", + "@cubejs-backend/crate-driver": "1.7.27", + "@cubejs-backend/cubestore-driver": "1.7.27", + "@cubejs-backend/databricks-jdbc-driver": "1.7.27", "@cubejs-backend/dotenv": "^9.0.2", - "@cubejs-backend/linter": "1.7.26", - "@cubejs-backend/mssql-driver": "1.7.26", - "@cubejs-backend/mysql-driver": "1.7.26", - "@cubejs-backend/oracle-driver": "1.7.26", - "@cubejs-backend/pinot-driver": "1.7.26", - "@cubejs-backend/postgres-driver": "1.7.26", - "@cubejs-backend/query-orchestrator": "1.7.26", - "@cubejs-backend/questdb-driver": "1.7.26", - "@cubejs-backend/server-core": "1.7.26", - "@cubejs-backend/shared": "1.7.26", - "@cubejs-backend/snowflake-driver": "1.7.26", - "@cubejs-backend/testing-shared": "1.7.26", - "@cubejs-backend/trino-driver": "1.7.26", - "@cubejs-client/core": "1.7.26", - "@cubejs-client/ws-transport": "1.7.26", + "@cubejs-backend/linter": "1.7.27", + "@cubejs-backend/mssql-driver": "1.7.27", + "@cubejs-backend/mysql-driver": "1.7.27", + "@cubejs-backend/oracle-driver": "1.7.27", + "@cubejs-backend/pinot-driver": "1.7.27", + "@cubejs-backend/postgres-driver": "1.7.27", + "@cubejs-backend/query-orchestrator": "1.7.27", + "@cubejs-backend/questdb-driver": "1.7.27", + "@cubejs-backend/server-core": "1.7.27", + "@cubejs-backend/shared": "1.7.27", + "@cubejs-backend/snowflake-driver": "1.7.27", + "@cubejs-backend/testing-shared": "1.7.27", + "@cubejs-backend/trino-driver": "1.7.27", + "@cubejs-client/core": "1.7.27", + "@cubejs-client/ws-transport": "1.7.27", "@jest/globals": "^29", "@types/jest": "^29", "@types/node": "^22", diff --git a/packages/cubejs-testing-shared/CHANGELOG.md b/packages/cubejs-testing-shared/CHANGELOG.md index ac4cce20eb2d3..715c417e56823 100644 --- a/packages/cubejs-testing-shared/CHANGELOG.md +++ b/packages/cubejs-testing-shared/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.27](https://github.com/cube-js/cube/compare/v1.7.26...v1.7.27) (2026-08-26) + +**Note:** Version bump only for package @cubejs-backend/testing-shared + ## [1.7.26](https://github.com/cube-js/cube/compare/v1.7.25...v1.7.26) (2026-08-24) **Note:** Version bump only for package @cubejs-backend/testing-shared diff --git a/packages/cubejs-testing-shared/package.json b/packages/cubejs-testing-shared/package.json index 4f455dd4ac173..3ea08e3f76190 100644 --- a/packages/cubejs-testing-shared/package.json +++ b/packages/cubejs-testing-shared/package.json @@ -1,6 +1,6 @@ { "name": "@cubejs-backend/testing-shared", - "version": "1.7.26", + "version": "1.7.27", "description": "Cube.js Testing Helpers", "author": "Cube Dev, Inc.", "repository": { @@ -26,16 +26,16 @@ ], "dependencies": { "@cubejs-backend/dotenv": "^9.0.2", - "@cubejs-backend/query-orchestrator": "1.7.26", - "@cubejs-backend/schema-compiler": "1.7.26", - "@cubejs-backend/shared": "1.7.26", + "@cubejs-backend/query-orchestrator": "1.7.27", + "@cubejs-backend/schema-compiler": "1.7.27", + "@cubejs-backend/shared": "1.7.27", "@testcontainers/kafka": "~10.28.0", "dedent": "^0.7.0", "node-fetch": "^2.6.7", "testcontainers": "^10.28.0" }, "devDependencies": { - "@cubejs-backend/linter": "1.7.26", + "@cubejs-backend/linter": "1.7.27", "@jest/globals": "^29", "@types/dedent": "^0.7.0", "@types/jest": "^29", diff --git a/packages/cubejs-testing/CHANGELOG.md b/packages/cubejs-testing/CHANGELOG.md index 3cc03efd116a0..7060e51c7eb4f 100644 --- a/packages/cubejs-testing/CHANGELOG.md +++ b/packages/cubejs-testing/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.27](https://github.com/cube-js/cube/compare/v1.7.26...v1.7.27) (2026-08-26) + +**Note:** Version bump only for package @cubejs-backend/testing + ## [1.7.26](https://github.com/cube-js/cube/compare/v1.7.25...v1.7.26) (2026-08-24) **Note:** Version bump only for package @cubejs-backend/testing diff --git a/packages/cubejs-testing/package.json b/packages/cubejs-testing/package.json index 7c757bd73b02e..7ebda6080aaab 100644 --- a/packages/cubejs-testing/package.json +++ b/packages/cubejs-testing/package.json @@ -1,6 +1,6 @@ { "name": "@cubejs-backend/testing", - "version": "1.7.26", + "version": "1.7.27", "description": "Cube.js e2e tests", "author": "Cube Dev, Inc.", "repository": { @@ -91,15 +91,15 @@ "birdbox-fixtures" ], "dependencies": { - "@cubejs-backend/cubestore-driver": "1.7.26", + "@cubejs-backend/cubestore-driver": "1.7.27", "@cubejs-backend/dotenv": "^9.0.2", - "@cubejs-backend/ksql-driver": "1.7.26", - "@cubejs-backend/postgres-driver": "1.7.26", - "@cubejs-backend/query-orchestrator": "1.7.26", - "@cubejs-backend/schema-compiler": "1.7.26", - "@cubejs-backend/shared": "1.7.26", - "@cubejs-backend/testing-shared": "1.7.26", - "@cubejs-client/ws-transport": "1.7.26", + "@cubejs-backend/ksql-driver": "1.7.27", + "@cubejs-backend/postgres-driver": "1.7.27", + "@cubejs-backend/query-orchestrator": "1.7.27", + "@cubejs-backend/schema-compiler": "1.7.27", + "@cubejs-backend/shared": "1.7.27", + "@cubejs-backend/testing-shared": "1.7.27", + "@cubejs-client/ws-transport": "1.7.27", "dedent": "^0.7.0", "fs-extra": "^8.1.0", "http-proxy": "^1.18.1", @@ -110,8 +110,8 @@ }, "devDependencies": { "@4tw/cypress-drag-drop": "^1.6.0", - "@cubejs-backend/linter": "1.7.26", - "@cubejs-client/core": "1.7.26", + "@cubejs-backend/linter": "1.7.27", + "@cubejs-client/core": "1.7.27", "@jest/globals": "^29", "@types/dedent": "^0.7.0", "@types/http-proxy": "^1.17.5", diff --git a/packages/cubejs-trino-driver/CHANGELOG.md b/packages/cubejs-trino-driver/CHANGELOG.md index d6faace9c2fd3..5825b2f0ef28a 100644 --- a/packages/cubejs-trino-driver/CHANGELOG.md +++ b/packages/cubejs-trino-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.27](https://github.com/cube-js/cube/compare/v1.7.26...v1.7.27) (2026-08-26) + +**Note:** Version bump only for package @cubejs-backend/trino-driver + ## [1.7.26](https://github.com/cube-js/cube/compare/v1.7.25...v1.7.26) (2026-08-24) **Note:** Version bump only for package @cubejs-backend/trino-driver diff --git a/packages/cubejs-trino-driver/package.json b/packages/cubejs-trino-driver/package.json index a5eb6b0c07541..7947a673691dd 100644 --- a/packages/cubejs-trino-driver/package.json +++ b/packages/cubejs-trino-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/trino-driver", "description": "Cube.js Trino database driver", "author": "Cube Dev, Inc.", - "version": "1.7.26", + "version": "1.7.27", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -28,10 +28,10 @@ "lint:fix": "eslint --fix src/* --ext .ts" }, "dependencies": { - "@cubejs-backend/base-driver": "1.7.26", - "@cubejs-backend/prestodb-driver": "1.7.26", - "@cubejs-backend/schema-compiler": "1.7.26", - "@cubejs-backend/shared": "1.7.26", + "@cubejs-backend/base-driver": "1.7.27", + "@cubejs-backend/prestodb-driver": "1.7.27", + "@cubejs-backend/schema-compiler": "1.7.27", + "@cubejs-backend/shared": "1.7.27", "node-fetch": "^2.6.1", "presto-client": "^1.2.0" }, @@ -40,7 +40,7 @@ "access": "public" }, "devDependencies": { - "@cubejs-backend/linter": "1.7.26", + "@cubejs-backend/linter": "1.7.27", "@types/jest": "^29", "jest": "^29", "testcontainers": "^10.28.0", diff --git a/packages/cubejs-vertica-driver/CHANGELOG.md b/packages/cubejs-vertica-driver/CHANGELOG.md index 0504968199e12..85f5b77ffbed2 100644 --- a/packages/cubejs-vertica-driver/CHANGELOG.md +++ b/packages/cubejs-vertica-driver/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.27](https://github.com/cube-js/cube/compare/v1.7.26...v1.7.27) (2026-08-26) + +**Note:** Version bump only for package @cubejs-backend/vertica-driver + ## [1.7.26](https://github.com/cube-js/cube/compare/v1.7.25...v1.7.26) (2026-08-24) **Note:** Version bump only for package @cubejs-backend/vertica-driver diff --git a/packages/cubejs-vertica-driver/package.json b/packages/cubejs-vertica-driver/package.json index 2093092eb870d..e40c95dcf142d 100644 --- a/packages/cubejs-vertica-driver/package.json +++ b/packages/cubejs-vertica-driver/package.json @@ -2,7 +2,7 @@ "name": "@cubejs-backend/vertica-driver", "description": "Cube.js Vertica database driver", "author": "Eduard Karacharov, Tim Brown, Cube Dev, Inc.", - "version": "1.7.26", + "version": "1.7.27", "repository": { "type": "git", "url": "https://github.com/cube-js/cube.git", @@ -19,16 +19,16 @@ "lint:fix": "eslint --fix **/*.js" }, "dependencies": { - "@cubejs-backend/base-driver": "1.7.26", - "@cubejs-backend/query-orchestrator": "1.7.26", - "@cubejs-backend/schema-compiler": "1.7.26", - "@cubejs-backend/shared": "1.7.26", + "@cubejs-backend/base-driver": "1.7.27", + "@cubejs-backend/query-orchestrator": "1.7.27", + "@cubejs-backend/schema-compiler": "1.7.27", + "@cubejs-backend/shared": "1.7.27", "vertica-nodejs": "^1.0.3" }, "license": "Apache-2.0", "devDependencies": { - "@cubejs-backend/linter": "1.7.26", - "@cubejs-backend/testing-shared": "1.7.26", + "@cubejs-backend/linter": "1.7.27", + "@cubejs-backend/testing-shared": "1.7.27", "@types/jest": "^29", "jest": "^29", "testcontainers": "^10.28.0" diff --git a/rust/cubesql/CHANGELOG.md b/rust/cubesql/CHANGELOG.md index d7b36c4a01f98..70640821c287e 100644 --- a/rust/cubesql/CHANGELOG.md +++ b/rust/cubesql/CHANGELOG.md @@ -3,6 +3,16 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.27](https://github.com/cube-js/cube/compare/v1.7.26...v1.7.27) (2026-08-26) + +### Bug Fixes + +- **cubesql:** Prefer SQL pushdown over limitless post-processing ([#11559](https://github.com/cube-js/cube/issues/11559)) ([b022de1](https://github.com/cube-js/cube/commit/b022de166acefa29766c4760f81241e31cf9ac16)) + +### Features + +- **cubesql:** Support `COPY ... FROM STDIN` ([#11538](https://github.com/cube-js/cube/issues/11538)) ([534f839](https://github.com/cube-js/cube/commit/534f8392830df33b7934129cbcbe068984e42903)) + ## [1.7.26](https://github.com/cube-js/cube/compare/v1.7.25...v1.7.26) (2026-08-24) **Note:** Version bump only for package @cubejs-backend/cubesql diff --git a/rust/cubesql/CLAUDE.md b/rust/cubesql/CLAUDE.md index 9b220d2adfa1f..f023e725d8ebe 100644 --- a/rust/cubesql/CLAUDE.md +++ b/rust/cubesql/CLAUDE.md @@ -113,6 +113,47 @@ cargo bench 3. Add tests with snapshot expectations 4. Update protocol-specific handling if needed +### Rewrite rules: never traverse a list recursively + +Every matcher must match exactly **one** level. A list is consumed by dedicated rules +that match the list node itself — never by a transform that walks it. + +- **Lists are flat, not head/tail.** A list node holds all of its elements as children + (`UnionInputs(a, b, c)`), not nested cons cells (`UnionInputs(a, UnionInputs(b, ...))`). + Cons lists are legacy — do not add new ones, and prefer converting one you touch. + Build them with the flat branch of `add_expr_flat_list_node!` (or an equivalent that + adds a single node with every element), register the node in `ListType`, and traverse + with `flat_list_pushdown_pullup_rules` / `replacer_flat_push_down_node` / + `replacer_flat_pull_up_node`, or `transforming_list_rewrite_with_lists_and_vars` when the + output needs a node no pattern can spell out (a cleared replacer context, an alias + converted to another node type). The flat pull-up matches the whole list in one rule and + takes `top_level_elem_vars`, which is how a fact that must hold across every element — + all queries reaching the same data source — is enforced: name the variable there and + unification does the rest, with no comparison of your own. +- Generate the traversal with the existing helpers rather than by hand: + `WrapperRules::list_pushdown_pullup_rules` / `flat_list_pushdown_pullup_rules` + (or `replacer_push_down_node` / `replacer_pull_up_node` underneath them). They emit + the `-push-down`, `-pull-up` and `-tail` rules that distribute a replacer over the + list's elements and collect it back once they are all done. +- Push-down and pull-up are **separate rules**. Do not fold both directions, or the + list walk, into one rewrite with a big transform. +- Do not write an imperative reader over `egraph[id].nodes` to collect a list's + elements inside a transform. An e-class holds many representations, so picking one is + arbitrary; the cons shape is also an implementation detail of how the list was built + (`add_plan_list_node!`), which a hand-rolled reader silently couples itself to. +- A transform should only decide scalar facts (a flag, an alias, whether a template + exists) about nodes the **pattern** already bound. Relationships between several + matched nodes — "both sides reach the same data source" — belong in the pattern, by + reusing one pattern variable in both places, so unification enforces them. +- A push-down replacer must never end up on top of an already pulled-up subtree. Inputs + that arrive as a finished `cube_scan_wrapper(wrapper_pullup_replacer(..))` — the queries + of a set operation, the sides of a join — have nothing left to push into, so their list + carries a **pull-up** replacer and is consumed by pull-up rules. Putting a push-down + replacer there instead makes `wrapper-subqueries-wrapped-scan-to-pull` + (`rules/wrapper/subquery.rs`) match, which re-contexts the element without comparing + input data sources — see the `TODO` on it. That rule is for the subquery path only; + reaching it from anywhere else means the rules above it are shaped wrong. + ### Debugging Query Compilation ```bash # Enable detailed logging diff --git a/rust/cubesql/cubesql/src/compile/engine/df/wrapper.rs b/rust/cubesql/cubesql/src/compile/engine/df/wrapper.rs index d09de2207999b..643f1f3b560cf 100644 --- a/rust/cubesql/cubesql/src/compile/engine/df/wrapper.rs +++ b/rust/cubesql/cubesql/src/compile/engine/df/wrapper.rs @@ -839,6 +839,12 @@ impl CubeScanWrapperNode { .map(|subq| subq.as_ref()) .any(Self::has_ungrouped_wrapped_node) } + } else if let Some(wrapped_union) = node.as_any().downcast_ref::() + { + wrapped_union + .inputs + .iter() + .any(|input| Self::has_ungrouped_wrapped_node(input)) } else { false } @@ -941,6 +947,18 @@ impl CubeScanWrapperNode { Arc::new(LogicalPlan::Extension(Extension { node: Arc::new(new_node), })) + } else if let Some(node) = extension_node + .as_any() + .downcast_ref::() + .cloned() + { + let mut new_node = node.clone(); + new_node.limit = Some(new_node.limit.map_or(query_limit as usize, |limit| { + min(limit, query_limit as usize) + })); + Arc::new(LogicalPlan::Extension(Extension { + node: Arc::new(new_node), + })) } else { node.clone() } @@ -1192,6 +1210,18 @@ impl CubeScanWrapperNode { parent_data_source, ) .await + } else if let Some(wrapped_union_node) = node_any.downcast_ref::() + { + wrapped_union_node + .generate_sql( + meta, + transport, + load_request_meta, + state, + values, + parent_data_source, + ) + .await } else { return Err(CubeError::internal(format!( "Can't generate SQL for node: {node:?}" @@ -1280,6 +1310,232 @@ impl UserDefinedLogicalNode for CubeScanWrapperNode { } } +/// A set operation whose inputs all push down to the same data source, rendered as a +/// `UNION` in the generated SQL rather than run as post processing in DataFusion. +/// +/// The row limit is not part of the plan the rewriter builds: like [`WrappedSelectNode`], +/// this node is capped by [`CubeScanWrapperNode::set_max_limit_for_node`] when it ends up +/// at the root of a wrapped plan. +#[derive(Debug, Clone)] +pub struct WrappedUnionNode { + pub schema: DFSchemaRef, + pub inputs: Vec>, + /// `UNION` when set, `UNION ALL` when not + pub distinct: bool, + pub alias: Option, + pub limit: Option, +} + +impl WrappedUnionNode { + pub fn new( + schema: DFSchemaRef, + inputs: Vec>, + distinct: bool, + alias: Option, + limit: Option, + ) -> Self { + Self { + schema, + inputs, + distinct, + alias, + limit, + } + } + + /// Renders every input as its own query and joins them into one set operation. + /// + /// Every input has to reach the same data source: a set operation the data source + /// evaluates cannot span two of them. + async fn generate_sql( + &self, + meta: &MetaContext, + transport: Arc, + load_request_meta: Arc, + state: Arc, + values: Vec>, + parent_data_source: Option<&str>, + ) -> result::Result { + let Some((first_input, rest_inputs)) = self.inputs.split_first() else { + return Err(CubeError::internal( + "Can't generate SQL for wrapped union: no inputs".to_string(), + )); + }; + + let SqlGenerationResult { + data_source, + from_alias, + column_remapping, + mut sql, + request, + } = CubeScanWrapperNode::generate_sql_for_node_rec( + meta, + Arc::clone(&transport), + Arc::clone(&load_request_meta), + Arc::clone(&state), + Arc::clone(first_input), + // Every input must keep the column names of the union's schema, which is the + // schema of the first one + false, + values.clone(), + parent_data_source, + ) + .await?; + + let Some(data_source) = data_source else { + return Err(CubeError::internal( + "Can't generate SQL for wrapped union: no data source for the first input" + .to_string(), + )); + }; + + let mut queries = Vec::with_capacity(self.inputs.len()); + queries.push(sql.sql.to_string()); + + for input in rest_inputs { + let input_result = CubeScanWrapperNode::generate_sql_for_node_rec( + meta, + Arc::clone(&transport), + Arc::clone(&load_request_meta), + Arc::clone(&state), + Arc::clone(input), + false, + values.clone(), + Some(&data_source), + ) + .await?; + + if input_result.data_source.as_ref() != Some(&data_source) { + return Err(CubeError::internal(format!( + "Can't generate SQL for wrapped union: inputs use different data sources, {:?} and {:?}", + data_source, input_result.data_source + ))); + } + + // Every input generated its SQL on its own, so its placeholders reference its + // own values and have to be remapped onto the combined ones + let (input_sql, input_values) = input_result.sql.unpack(); + let mapping = sql.add_values(input_values); + queries.push(SqlQuery::remap_placeholders(&input_sql, &mapping)?); + } + + let generator = meta + .data_source_to_sql_generator + .get(&data_source) + .ok_or_else(|| { + CubeError::internal(format!( + "Can't generate SQL for wrapped union: no sql generator for '{data_source}' data source" + )) + })?; + + let resulting_sql = generator + .get_sql_templates() + .union(queries, self.distinct, self.limit) + .map_err(|e| { + CubeError::internal(format!("Can't generate SQL for wrapped union: {e}")) + })?; + sql.replace_sql(resulting_sql); + + // Plans above see the union through a single alias, and read its columns by the + // names the first input gave them + let alias = self.alias.clone().or(from_alias); + let column_remapping = column_remapping + .map(|column_remapping| self.requalify_remapping(column_remapping, alias.as_deref())); + + // The column names of the union are the first input's, so its remapping is the one + // that describes them for the plans above. + // TODO only the first input's request is carried up, so the request that travels + // with this SQL describes one input out of several. `WrappedSelectNode` drops the + // requests of its joins the same way + Ok(SqlGenerationResult { + data_source: Some(data_source), + from_alias: alias, + sql, + column_remapping, + request, + }) + } + + /// The union's own schema drops the qualifiers of the first input, or replaces them with + /// its alias, so a column reference from above can carry a qualifier the first input's + /// remapping has never seen. Add those references, pointing at the same targets. + fn requalify_remapping( + &self, + column_remapping: ColumnRemapping, + alias: Option<&str>, + ) -> ColumnRemapping { + let mut column_remapping = column_remapping; + for field in self.schema.fields() { + let name = field.name(); + let Some(target) = column_remapping + .column_remapping + .get(&Column::from_name(name.clone())) + .cloned() + else { + continue; + }; + for qualifier in alias.map(str::to_string).into_iter().chain( + field + .qualifier() + .filter(|qualifier| Some(qualifier.as_str()) != alias) + .cloned(), + ) { + column_remapping.column_remapping.insert( + Column { + relation: Some(qualifier), + name: name.clone(), + }, + target.clone(), + ); + } + } + column_remapping + } +} + +impl UserDefinedLogicalNode for WrappedUnionNode { + fn as_any(&self) -> &dyn Any { + self + } + + fn inputs(&self) -> Vec<&LogicalPlan> { + self.inputs.iter().map(|input| input.as_ref()).collect() + } + + fn schema(&self) -> &DFSchemaRef { + &self.schema + } + + fn expressions(&self) -> Vec { + vec![] + } + + fn fmt_for_explain(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "WrappedUnion: distinct={:?}, alias={:?}, limit={:?}", + self.distinct, self.alias, self.limit + ) + } + + fn from_template( + &self, + exprs: &[Expr], + inputs: &[LogicalPlan], + ) -> Arc { + assert_eq!(inputs.len(), self.inputs.len(), "input size inconsistent"); + assert_eq!(exprs.len(), 0, "expression size inconsistent"); + + Arc::new(WrappedUnionNode { + schema: self.schema.clone(), + inputs: inputs.iter().cloned().map(Arc::new).collect(), + distinct: self.distinct, + alias: self.alias.clone(), + limit: self.limit, + }) + } +} + #[derive(Debug, Clone)] pub struct WrappedSelectNode { pub schema: DFSchemaRef, diff --git a/rust/cubesql/cubesql/src/compile/rewrite/converter.rs b/rust/cubesql/cubesql/src/compile/rewrite/converter.rs index 1f4fdc809b54c..2d7e53d3e5ac7 100644 --- a/rust/cubesql/cubesql/src/compile/rewrite/converter.rs +++ b/rust/cubesql/cubesql/src/compile/rewrite/converter.rs @@ -3,7 +3,7 @@ use crate::{ compile::{ engine::df::{ scan::{CubeScanNode, CubeScanOptions, MemberField}, - wrapper::{CubeScanWrapperNode, WrappedSelectNode}, + wrapper::{CubeScanWrapperNode, WrappedSelectNode, WrappedUnionNode}, }, rewrite::{ analysis::LogicalPlanAnalysis, @@ -30,7 +30,7 @@ use crate::{ ValuesValues, WindowFunctionExprFun, WindowFunctionExprWindowFrame, WrappedSelectAlias, WrappedSelectDistinct, WrappedSelectJoinJoinType, WrappedSelectLimit, WrappedSelectOffset, WrappedSelectPushToCube, WrappedSelectSelectType, - WrappedSelectType, + WrappedSelectType, WrappedUnionAlias, WrappedUnionDistinct, }, CubeContext, }, @@ -152,6 +152,20 @@ macro_rules! add_binary_expr_list_node { }}; } +/// A flat list node: every element is a child of one node. Lists are flat here, and the +/// cons cells `add_plan_list_node!` builds are legacy. +macro_rules! add_plan_flat_list_node { + ($converter:expr, $value_expr:expr, $query_params:expr, $ctx:expr, $field_variant:ident) => {{ + let list = $value_expr + .iter() + .map(|expr| $converter.add_logical_plan_replace_params(expr, $query_params, $ctx)) + .collect::, _>>()?; + $converter + .graph + .add(LogicalPlanLanguage::$field_variant(list)) + }}; +} + macro_rules! add_plan_list_node { ($converter:expr, $value_expr:expr, $query_params:expr, $ctx:expr, $field_variant:ident) => {{ let list = $value_expr @@ -698,7 +712,8 @@ impl LogicalPlanToLanguageConverter { self.graph.add(LogicalPlanLanguage::Repartition([input])) } LogicalPlan::Union(node) => { - let inputs = add_plan_list_node!(self, node.inputs, query_params, ctx, UnionInputs); + let inputs = + add_plan_flat_list_node!(self, node.inputs, query_params, ctx, UnionInputs); let alias = add_data_node!(self, node.alias, UnionAlias); self.graph.add(LogicalPlanLanguage::Union([inputs, alias])) } @@ -2373,6 +2388,40 @@ impl LanguageToLogicalPlanConverter { )), }) } + LogicalPlanLanguage::WrappedUnion(params) => { + let inputs = match_list_node_ids!(node_by_id, params[0], WrappedUnionInputs) + .into_iter() + .map(|n| self.to_logical_plan(n).map(Arc::new)) + .collect::, _>>()?; + + let distinct: bool = match_data_node!(node_by_id, params[1], WrappedUnionDistinct); + let alias: Option = + match_data_node!(node_by_id, params[2], WrappedUnionAlias); + + let Some(first_input) = inputs.first() else { + return Err(CubeError::internal( + "Can't convert wrapped union with no inputs".to_string(), + )); + }; + + // Same as `Union`: the inputs are union compatible, so the first one's schema + // stands for all of them + let schema = first_input.schema().as_ref().clone(); + let schema = match alias { + Some(ref alias) => schema.replace_qualifier(alias.as_str()), + None => schema.strip_qualifiers(), + }; + + LogicalPlan::Extension(Extension { + node: Arc::new(WrappedUnionNode::new( + Arc::new(schema), + inputs, + distinct, + alias, + None, + )), + }) + } LogicalPlanLanguage::Union(params) => { let inputs = match_list_node_ids!(node_by_id, params[0], UnionInputs) .into_iter() diff --git a/rust/cubesql/cubesql/src/compile/rewrite/cost.rs b/rust/cubesql/cubesql/src/compile/rewrite/cost.rs index 362be5f7699ea..a0d0739e07aef 100644 --- a/rust/cubesql/cubesql/src/compile/rewrite/cost.rs +++ b/rust/cubesql/cubesql/src/compile/rewrite/cost.rs @@ -73,8 +73,12 @@ impl BestCubePlan { _ => 0, }; + // What a wrapper does on its own, as opposed to merely holding a scan: a wrapper + // with none of it is an empty wrapper, and preferring it over the plain scan it + // wraps would be pointless let ast_size_inside_wrapper = match enode { LogicalPlanLanguage::WrappedSelect(_) => 1, + LogicalPlanLanguage::WrappedUnion(_) => 1, _ => 0, }; diff --git a/rust/cubesql/cubesql/src/compile/rewrite/mod.rs b/rust/cubesql/cubesql/src/compile/rewrite/mod.rs index eef32945cece1..e0b50cd040c38 100644 --- a/rust/cubesql/cubesql/src/compile/rewrite/mod.rs +++ b/rust/cubesql/cubesql/src/compile/rewrite/mod.rs @@ -294,6 +294,15 @@ crate::plan_to_language! { join_type: JoinType, }, + // A set operation over inputs that are all pushed down to the same data source. + // Unlike `Union`, which is post processing in DataFusion, this one renders as a + // `UNION` in the generated SQL and is evaluated by the data source itself. + WrappedUnion { + inputs: Vec, + distinct: bool, + alias: Option, + }, + CubeScan { alias_to_cube: Vec<(String, String)>, members: Vec, @@ -927,6 +936,8 @@ pub enum ListType { WrappedSelectAggrExpr, WrappedSelectWindowExpr, CubeScanMembers, + UnionInputs, + WrappedUnionInputs, } impl ListType { @@ -948,6 +959,8 @@ impl ListType { Self::WrappedSelectAggrExpr => wrapped_select_aggr_expr_empty_tail(), Self::WrappedSelectWindowExpr => wrapped_select_window_expr_empty_tail(), Self::CubeScanMembers => cube_scan_members_empty_tail(), + Self::UnionInputs => union_inputs_empty_tail(), + Self::WrappedUnionInputs => wrapped_union_inputs_empty_tail(), } } } @@ -964,6 +977,10 @@ struct ListNodeSearcher { list_pattern: Pattern, elem_pattern: Pattern, top_level_elem_vars: Vec, + /// Lists shorter than this do not match. A set operation needs it: one query is not a + /// union, and folding a `Distinct` into a single-query one would drop the deduplication + /// with no operator to render it on. + min_elements: usize, } impl ListNodeSearcher { @@ -974,9 +991,15 @@ impl ListNodeSearcher { list_pattern: list_pattern.parse().unwrap(), elem_pattern: elem_pattern.parse().unwrap(), top_level_elem_vars: vec![], + min_elements: 1, } } + fn with_min_elements(mut self, min_elements: usize) -> Self { + self.min_elements = min_elements; + self + } + fn with_top_level_elem_vars(mut self, vars: &[&str]) -> Self { self.top_level_elem_vars = vars.iter().map(|s| s.parse().unwrap()).collect(); self @@ -1034,6 +1057,12 @@ impl ListNodeSearcher { ListType::CubeScanMembers => { matches!(node, LogicalPlanLanguage::CubeScanMembers(_)) } + ListType::UnionInputs => { + matches!(node, LogicalPlanLanguage::UnionInputs(_)) + } + ListType::WrappedUnionInputs => { + matches!(node, LogicalPlanLanguage::WrappedUnionInputs(_)) + } } } @@ -1047,7 +1076,7 @@ impl ListNodeSearcher { let list_id = list_subst[self.list_var]; for node in egraph[list_id].iter() { let list_children = node.children(); - if !self.match_node(node) || list_children.is_empty() { + if !self.match_node(node) || list_children.len() < self.min_elements.max(1) { continue; } @@ -1196,6 +1225,8 @@ impl ListNodeApplierList { ListType::WrappedSelectAggrExpr => LogicalPlanLanguage::WrappedSelectAggrExpr(list), ListType::WrappedSelectWindowExpr => LogicalPlanLanguage::WrappedSelectWindowExpr(list), ListType::CubeScanMembers => LogicalPlanLanguage::CubeScanMembers(list), + ListType::UnionInputs => LogicalPlanLanguage::UnionInputs(list), + ListType::WrappedUnionInputs => LogicalPlanLanguage::WrappedUnionInputs(list), } } } @@ -1206,9 +1237,26 @@ pub struct ListApplierListPattern { elem_pattern: String, } +type ListNodeTransform = Box bool + Sync + Send>; + struct ListNodeApplier { list_pattern: PatternAst, lists: Vec, + /// Runs once the substitution carries the list's own variables, so it can build nodes + /// the pattern cannot spell out. Returning false drops this match. + transform: Option, + /// The variables the transform inserts. Only these are exempt from egg's check that the + /// searcher binds every variable the applier uses, so a typo anywhere else in the + /// applier still fails when the rule is built rather than when it first matches. + transform_vars: Vec, +} + +impl ListNodeApplier { + fn with_transform(mut self, transform: ListNodeTransform, transform_vars: &[&str]) -> Self { + self.transform = Some(transform); + self.transform_vars = transform_vars.iter().map(|v| v.parse().unwrap()).collect(); + self + } } impl ListNodeApplier { @@ -1233,6 +1281,8 @@ impl ListNodeApplier { lists: impl IntoIterator, ) -> Self { Self { + transform: None, + transform_vars: vec![], list_pattern: list_pattern.parse().unwrap(), lists: lists .into_iter() @@ -1278,6 +1328,11 @@ impl Applier for ListNodeApplier { } let mut subst = subst.clone(); subst.extend(list_substs[0].iter()); + if let Some(transform) = &self.transform { + if !transform(egraph, &mut subst) { + return; + } + } let new_id = egraph.add_instantiation(&self.list_pattern, &subst); if egraph.union(eclass, new_id) { result_ids.push(new_id); @@ -1294,6 +1349,7 @@ impl Applier for ListNodeApplier { vars.extend(list.elem_pattern.vars()); vars.retain(|v| *v != list.new_list_var); // this is bound by the applier itself } + vars.retain(|v| !self.transform_vars.contains(v)); // and these by the transform vars } } @@ -1650,6 +1706,22 @@ fn wrapped_select_joins_empty_tail() -> String { "WrappedSelectJoins".to_string() } +fn wrapped_union(inputs: impl Display, distinct: impl Display, alias: impl Display) -> String { + format!("(WrappedUnion {inputs} {distinct} {alias})") +} + +fn union(inputs: impl Display, alias: impl Display) -> String { + format!("(Union {inputs} {alias})") +} + +fn union_inputs_empty_tail() -> String { + "(UnionInputs)".to_string() +} + +fn wrapped_union_inputs_empty_tail() -> String { + "(WrappedUnionInputs)".to_string() +} + fn wrapped_select_filter_expr(left: impl Display, right: impl Display) -> String { format!("(WrappedSelectFilterExpr {} {})", left, right) } @@ -2444,6 +2516,37 @@ where } } +/// `list_rewrite_with_lists_and_vars` with a transform. The transform runs once the +/// substitution carries the list's own variables — including the `top_level_elem_vars` +/// every element agreed on — so it can build what a pattern cannot: a cleared replacer +/// context, an alias converted to another node type. +pub fn transforming_list_rewrite_with_lists_and_vars( + name: &str, + list_type: ListType, + searcher: ListPattern, + applier_pattern: &str, + lists: impl IntoIterator, + top_level_elem_vars: &[&str], + min_elements: usize, + transform_vars: &[&str], + transform_fn: T, +) -> CubeRewrite +where + T: Fn(&mut CubeEGraph, &mut Subst) -> bool + Sync + Send + 'static, +{ + let searcher = ListNodeSearcher::new( + list_type, + &searcher.list_var, + &searcher.pattern, + &searcher.elem, + ) + .with_top_level_elem_vars(top_level_elem_vars) + .with_min_elements(min_elements); + let applier = ListNodeApplier::from_lists(applier_pattern, lists) + .with_transform(Box::new(transform_fn), transform_vars); + Rewrite::new(name.to_string(), searcher, applier).unwrap() +} + pub fn transform_original_expr_to_alias( alias_expr_var: &'static str, ) -> impl Fn(&mut CubeEGraph, Id, &mut Subst) -> bool { diff --git a/rust/cubesql/cubesql/src/compile/rewrite/rules/wrapper/mod.rs b/rust/cubesql/cubesql/src/compile/rewrite/rules/wrapper/mod.rs index f430427bf83db..6ab72ff470f9c 100644 --- a/rust/cubesql/cubesql/src/compile/rewrite/rules/wrapper/mod.rs +++ b/rust/cubesql/cubesql/src/compile/rewrite/rules/wrapper/mod.rs @@ -26,6 +26,7 @@ mod sort_expr; mod subquery; mod udaf_function; mod udf_function; +mod union; mod window; mod window_function; mod wrapper_pull_up; @@ -59,6 +60,7 @@ impl RewriteRules for WrapperRules { self.cube_scan_wrapper_rules(&mut rules); self.join_rules(&mut rules); + self.union_rules(&mut rules); self.wrapper_pull_up_rules(&mut rules); self.aggregate_rules(&mut rules); self.aggregate_rules_subquery(&mut rules); diff --git a/rust/cubesql/cubesql/src/compile/rewrite/rules/wrapper/union.rs b/rust/cubesql/cubesql/src/compile/rewrite/rules/wrapper/union.rs new file mode 100644 index 0000000000000..9dda9699405f1 --- /dev/null +++ b/rust/cubesql/cubesql/src/compile/rewrite/rules/wrapper/union.rs @@ -0,0 +1,188 @@ +use crate::{ + compile::rewrite::{ + cube_scan_wrapper, distinct, rewrite, + rewriter::{CubeEGraph, CubeRewrite}, + rules::wrapper::WrapperRules, + transforming_list_rewrite_with_lists_and_vars, union, wrapped_union, + wrapper_pullup_replacer, wrapper_replacer_context, ListApplierListPattern, ListPattern, + ListType, LogicalPlanLanguage, UnionAlias, WrappedUnionAlias, + WrapperReplacerContextAliasToCube, WrapperReplacerContextGroupedSubqueries, + WrapperReplacerContextInputDataSource, + }, + transport::DataSource, + var, var_iter, +}; +use egg::Subst; + +impl WrapperRules { + pub fn union_rules(&self, rules: &mut Vec) { + rules.extend(vec![ + // The queries of a union arrive already pulled up, so there is nothing to push + // into them: the whole list is matched at once and every query is unwrapped into + // the set operation. `?input_data_source` is a top level element variable, so + // every query has to reach the same data source for this to match at all — a + // union spanning two of them is left to post processing without a check of its + // own. + transforming_list_rewrite_with_lists_and_vars( + "wrapper-pull-up-union", + ListType::UnionInputs, + ListPattern { + pattern: union("?list", "?union_alias"), + list_var: "?list".to_string(), + elem: cube_scan_wrapper( + wrapper_pullup_replacer( + "?elem", + wrapper_replacer_context( + "?elem_alias_to_cube", + "?elem_push_to_cube", + "?elem_in_projection", + "?elem_cube_members", + "?elem_grouped_subqueries", + "?elem_ungrouped_scan", + "?input_data_source", + ), + ), + "CubeScanWrapperFinalized:false", + ), + }, + &cube_scan_wrapper( + wrapper_pullup_replacer( + wrapped_union( + "?new_list", + "WrappedUnionDistinct:false", + "?wrapped_union_alias", + ), + wrapper_replacer_context( + "?out_alias_to_cube", + // A set operation is evaluated by the data source, so nothing + // above it reaches a Cube load query anymore + "WrapperReplacerContextPushToCube:false", + "WrapperReplacerContextInProjection:false", + "?out_cube_members", + "?out_grouped_subqueries", + "WrapperReplacerContextUngroupedScan:false", + "?input_data_source", + ), + ), + "CubeScanWrapperFinalized:false", + ), + [ListApplierListPattern { + list_type: ListType::WrappedUnionInputs, + new_list_var: "?new_list".to_string(), + elem_pattern: "?elem".to_string(), + }], + &["?input_data_source"], + // One query is not a union, and folding a `Distinct` into a single query + // one would leave the deduplication with no operator to render it on + 2, + &[ + "?wrapped_union_alias", + "?out_alias_to_cube", + "?out_cube_members", + "?out_grouped_subqueries", + ], + self.transform_union( + "?union_alias", + "?input_data_source", + "?wrapped_union_alias", + "?out_alias_to_cube", + "?out_cube_members", + "?out_grouped_subqueries", + ), + ), + // `UNION` is `UNION ALL` with the duplicates dropped, so the data source can do + // both at once and the deduplication does not have to happen in DataFusion + rewrite( + "wrapper-push-down-distinct-to-union", + distinct(cube_scan_wrapper( + wrapper_pullup_replacer( + wrapped_union("?inputs", "WrappedUnionDistinct:false", "?alias"), + "?context", + ), + "CubeScanWrapperFinalized:false", + )), + cube_scan_wrapper( + wrapper_pullup_replacer( + wrapped_union("?inputs", "WrappedUnionDistinct:true", "?alias"), + "?context", + ), + "CubeScanWrapperFinalized:false", + ), + ), + ]); + } + + fn transform_union( + &self, + union_alias_var: &'static str, + input_data_source_var: &'static str, + wrapped_union_alias_var: &'static str, + alias_to_cube_out_var: &'static str, + cube_members_out_var: &'static str, + grouped_subqueries_out_var: &'static str, + ) -> impl Fn(&mut CubeEGraph, &mut Subst) -> bool { + let union_alias_var = var!(union_alias_var); + let input_data_source_var = var!(input_data_source_var); + let wrapped_union_alias_var = var!(wrapped_union_alias_var); + let alias_to_cube_out_var = var!(alias_to_cube_out_var); + let cube_members_out_var = var!(cube_members_out_var); + let grouped_subqueries_out_var = var!(grouped_subqueries_out_var); + let meta = self.meta_context.clone(); + move |egraph, subst| { + let Some(data_source) = var_iter!( + egraph[subst[input_data_source_var]], + WrapperReplacerContextInputDataSource + ) + .flat_map(|data_source| data_source.clone()) + .next() else { + return false; + }; + + // A data source that has no union template cannot render a set operation, and + // there is no way back to post processing once this plan is chosen + if !Self::can_rewrite_template( + &DataSource::Specific(&data_source), + &meta, + "statements/union", + ) { + return false; + } + + let Some(alias) = var_iter!(egraph[subst[union_alias_var]], UnionAlias) + .cloned() + .next() + else { + return false; + }; + subst.insert( + wrapped_union_alias_var, + egraph.add(LogicalPlanLanguage::WrappedUnionAlias(WrappedUnionAlias( + alias, + ))), + ); + + // The queries are complete by now, so nothing above the union can reach the + // cubes and members they were built from + subst.insert( + alias_to_cube_out_var, + egraph.add(LogicalPlanLanguage::WrapperReplacerContextAliasToCube( + WrapperReplacerContextAliasToCube(vec![]), + )), + ); + subst.insert( + cube_members_out_var, + egraph.add(LogicalPlanLanguage::CubeScanMembers(vec![])), + ); + subst.insert( + grouped_subqueries_out_var, + egraph.add( + LogicalPlanLanguage::WrapperReplacerContextGroupedSubqueries( + WrapperReplacerContextGroupedSubqueries(vec![]), + ), + ), + ); + + true + } + } +} diff --git a/rust/cubesql/cubesql/src/compile/test/mod.rs b/rust/cubesql/cubesql/src/compile/test/mod.rs index ca9956563241e..ecf1f645c10ae 100644 --- a/rust/cubesql/cubesql/src/compile/test/mod.rs +++ b/rust/cubesql/cubesql/src/compile/test/mod.rs @@ -728,6 +728,15 @@ OFFSET {{ offset }}{% endif %}"#.to_string(), "statements/join".to_string(), "{{ join_type }} JOIN {{ source }} ON {{ condition }}".to_string(), ), + ( + "statements/union".to_string(), + r#"{% for query in queries %}( +{{ query | indent(2, true) }} +){% if not loop.last %} +UNION {% if not distinct %}ALL {% endif %}{% endif %}{% endfor %}{% if limit is not none %} +LIMIT {{ limit }}{% endif %}"# + .to_string(), + ), ( "statements/group_by_exprs".to_string(), "{{ group_by | map(attribute='index') | join(', ') }}".to_string(), @@ -830,6 +839,47 @@ pub fn get_test_tenant_ctx_with_meta(meta: Vec) -> Arc { get_test_tenant_ctx_with_meta_and_templates(meta, vec![]) } +/// The standard test meta with its cubes spread across several data sources: every cube +/// named in `cube_data_sources` reaches the data source it is paired with, everything else +/// reaches `default`. +pub fn get_test_tenant_ctx_with_cube_data_sources( + cube_data_sources: Vec<(&str, &str)>, +) -> Arc { + let data_source_for_cube = |cube: &str| { + cube_data_sources + .iter() + .find(|(name, _)| *name == cube) + .map_or("default", |(_, data_source)| *data_source) + .to_string() + }; + + let meta = get_test_meta(); + let member_to_data_source: HashMap<_, _> = meta + .iter() + .flat_map(|cube| { + let data_source = data_source_for_cube(&cube.name); + cube.dimensions + .iter() + .map(|d| &d.name) + .chain(cube.measures.iter().map(|m| &m.name)) + .chain(cube.segments.iter().map(|s| &s.name)) + .map(move |member| (member.clone(), data_source.clone())) + }) + .collect(); + + let data_source_to_sql_generator = member_to_data_source + .values() + .map(|data_source| (data_source.clone(), sql_generator(vec![]))) + .collect(); + + Arc::new(MetaContext::new( + meta, + member_to_data_source, + data_source_to_sql_generator, + Uuid::new_v4(), + )) +} + pub async fn get_test_session( protocol: DatabaseProtocol, meta_context: Arc, diff --git a/rust/cubesql/cubesql/src/compile/test/test_wrapper.rs b/rust/cubesql/cubesql/src/compile/test/test_wrapper.rs index 53df62aea1afc..c03599644a7d6 100644 --- a/rust/cubesql/cubesql/src/compile/test/test_wrapper.rs +++ b/rust/cubesql/cubesql/src/compile/test/test_wrapper.rs @@ -15,8 +15,9 @@ use crate::{ rewrite::rewriter::Rewriter, test::{ convert_select_to_query_plan, convert_select_to_query_plan_customized, - convert_select_to_query_plan_with_config, init_testing_logger, LogicalPlanTestUtils, - TestContext, + convert_select_to_query_plan_with_config, convert_sql_to_cube_query, + get_test_session_with_config, get_test_tenant_ctx_with_cube_data_sources, + init_testing_logger, member_expression_sql, LogicalPlanTestUtils, TestContext, }, DatabaseProtocol, }, @@ -3245,8 +3246,11 @@ async fn test_wrapper_limitless_post_processing_ignored_in_stream_mode() { } /// A limit bounds the rows of the query it sits on, not of a query beside it. Summing -/// limits and limitless scans over the whole plan would let the limited branch here stand -/// in for the unlimited one, and the union would read a truncated half without saying so. +/// limits and limitless scans over the whole plan would let the limited query here stand in +/// for the unlimited one, and the union would read a truncated half without saying so. +/// +/// The two queries read different data sources, so the union cannot be pushed down and is +/// left to post processing, which is what makes the pairing observable at all. #[tokio::test] async fn test_wrapper_limitless_post_processing_sibling_without_limit() { if !Rewriter::sql_push_down_enabled() { @@ -3256,16 +3260,21 @@ async fn test_wrapper_limitless_post_processing_sibling_without_limit() { let mut config = ConfigObjImpl::default(); config.fail_on_limitless_post_processing = true; - let context = TestContext::with_config(DatabaseProtocol::PostgreSQL, Arc::new(config)).await; + let meta = get_test_tenant_ctx_with_cube_data_sources(vec![("Logs", "other")]); + let session = + get_test_session_with_config(DatabaseProtocol::PostgreSQL, Arc::new(config), meta.clone()) + .await; - let error = context - .convert_sql_to_cube_query( - "(SELECT customer_gender AS g FROM KibanaSampleDataEcommerce GROUP BY 1 LIMIT 10) \ - UNION ALL \ - (SELECT customer_gender AS g FROM KibanaSampleDataEcommerce GROUP BY 1)", - ) - .await - .expect_err("union with an unlimited branch should fail"); + let error = convert_sql_to_cube_query( + &"(SELECT customer_gender AS g FROM KibanaSampleDataEcommerce GROUP BY 1 LIMIT 10) \ + UNION ALL \ + (SELECT content AS g FROM Logs GROUP BY 1)" + .to_string(), + meta, + session, + ) + .await + .expect_err("union with an unlimited query should fail"); assert!( error .to_string() @@ -3397,3 +3406,450 @@ async fn test_wrapper_limitless_post_processing_ungrouped_aggregate() { error ); } + +/// A `UNION` of two `SELECT DISTINCT`s over different cubes, the shape a BI user writes to +/// list the values of a dimension across two views. +/// +/// It used to be planned as a `DISTINCT` over a `UNION` of two ungrouped scans, both of them +/// truncated to the row cap before the deduplication ever saw them, so values missing from +/// the answer went missing silently. Both queries reach the same data source, so the whole +/// set operation is pushed down to it instead. +#[tokio::test] +async fn test_wrapper_limitless_post_processing_union_of_distinct_selects() { + if !Rewriter::sql_push_down_enabled() { + return; + } + init_testing_logger(); + + let query = "SELECT DISTINCT 'Medical Claims (MX)' AS table_type, customer_gender AS market \ + FROM KibanaSampleDataEcommerce \ + UNION \ + SELECT DISTINCT 'Pharmacy Claims (RX)' AS table_type, content AS market \ + FROM Logs"; + + let mut config = ConfigObjImpl::default(); + config.fail_on_limitless_post_processing = true; + let context = TestContext::with_config(DatabaseProtocol::PostgreSQL, Arc::new(config)).await; + + let logical_plan = context + .convert_sql_to_cube_query(query) + .await + .expect("union of two queries to the same data source should compile") + .as_logical_plan(); + + // Nothing is left above the pushed down query + let sql = logical_plan.find_cube_scan_wrapped_sql().wrapped_sql.sql; + assert!(sql.contains("UNION"), "union is pushed down: {}", sql); + assert!( + logical_plan + .find_cube_scans() + .iter() + .all(|scan| { scan.request.ungrouped == Some(true) && scan.request.limit.is_none() }), + "the queries themselves stay unlimited, inside the pushed down SQL: {:?}", + logical_plan + ); +} + +/// The queries of a `UNION` reach the same data source, so the data source can evaluate the +/// whole set operation and nothing is left for DataFusion to do on top of two truncated +/// halves. +#[tokio::test] +async fn test_wrapper_union_all_push_down() { + if !Rewriter::sql_push_down_enabled() { + return; + } + init_testing_logger(); + + let query_plan = convert_select_to_query_plan( + "SELECT 'MX' AS table_type, customer_gender AS market \ + FROM KibanaSampleDataEcommerce GROUP BY 1, 2 \ + UNION ALL \ + SELECT 'RX' AS table_type, content AS market FROM Logs GROUP BY 1, 2" + .to_string(), + DatabaseProtocol::PostgreSQL, + ) + .await; + + let logical_plan = query_plan.as_logical_plan(); + let sql = logical_plan.find_cube_scan_wrapped_sql().wrapped_sql.sql; + assert!(sql.contains("UNION ALL"), "union is pushed down: {}", sql); + assert!( + sql.contains("KibanaSampleDataEcommerce.customer_gender") && sql.contains("Logs.content"), + "both queries are in the pushed down SQL: {}", + sql + ); + // The row cap lands on the result of the whole set operation, not on either query + assert!( + sql.trim_end().ends_with(&format!( + "LIMIT {}", + ConfigObjImpl::default().non_streaming_query_max_row_limit + )), + "the row cap bounds the union: {}", + sql + ); +} + +/// A plain `UNION` drops duplicates, which the data source does as part of the same set +/// operation: it is `UNION ALL` with `ALL` left off, not a separate deduplication. +#[tokio::test] +async fn test_wrapper_union_distinct_push_down() { + if !Rewriter::sql_push_down_enabled() { + return; + } + init_testing_logger(); + + let query_plan = convert_select_to_query_plan( + "SELECT DISTINCT 'MX' AS table_type, customer_gender AS market \ + FROM KibanaSampleDataEcommerce \ + UNION \ + SELECT DISTINCT 'RX' AS table_type, content AS market FROM Logs" + .to_string(), + DatabaseProtocol::PostgreSQL, + ) + .await; + + let logical_plan = query_plan.as_logical_plan(); + let sql = logical_plan.find_cube_scan_wrapped_sql().wrapped_sql.sql; + assert!( + sql.contains("UNION") && !sql.contains("UNION ALL"), + "distinct union is pushed down as UNION: {}", + sql + ); +} + +/// The queries of a `UNION` are a list, not a pair +#[tokio::test] +async fn test_wrapper_union_three_queries_push_down() { + if !Rewriter::sql_push_down_enabled() { + return; + } + init_testing_logger(); + + let query_plan = convert_select_to_query_plan( + "SELECT 'MX' AS table_type, customer_gender AS market \ + FROM KibanaSampleDataEcommerce GROUP BY 1, 2 \ + UNION ALL \ + SELECT 'RX' AS table_type, content AS market FROM Logs GROUP BY 1, 2 \ + UNION ALL \ + SELECT 'DX' AS table_type, notes AS market \ + FROM KibanaSampleDataEcommerce GROUP BY 1, 2" + .to_string(), + DatabaseProtocol::PostgreSQL, + ) + .await; + + let sql = query_plan + .as_logical_plan() + .find_cube_scan_wrapped_sql() + .wrapped_sql + .sql; + assert_eq!( + sql.matches("UNION ALL").count(), + 2, + "all three queries are pushed down: {}", + sql + ); +} + +/// A pushed down union is a query like any other, so a projection and a filter above it are +/// pushed into a select that reads from it. +#[tokio::test] +async fn test_wrapper_union_filter_push_down() { + if !Rewriter::sql_push_down_enabled() { + return; + } + init_testing_logger(); + + let query_plan = convert_select_to_query_plan( + "SELECT market FROM ( \ + SELECT 'MX' AS table_type, customer_gender AS market \ + FROM KibanaSampleDataEcommerce GROUP BY 1, 2 \ + UNION ALL \ + SELECT 'RX' AS table_type, content AS market FROM Logs GROUP BY 1, 2 \ + ) markets WHERE market = 'female'" + .to_string(), + DatabaseProtocol::PostgreSQL, + ) + .await; + + let logical_plan = query_plan.as_logical_plan(); + assert!( + logical_plan.find_filter().is_none(), + "no filter is left to post processing: {:?}", + logical_plan + ); + let sql = logical_plan.find_cube_scan_wrapped_sql().wrapped_sql.sql; + assert!(sql.contains("UNION ALL"), "union is pushed down: {}", sql); + assert!( + sql.contains("WHERE"), + "the filter reads from the pushed down union: {}", + sql + ); +} + +/// A set operation is evaluated by one data source, so queries that reach two of them cannot +/// be pushed down and the union stays in DataFusion. +#[tokio::test] +async fn test_wrapper_union_different_data_sources_not_pushed_down() { + if !Rewriter::sql_push_down_enabled() { + return; + } + init_testing_logger(); + + let meta = get_test_tenant_ctx_with_cube_data_sources(vec![("Logs", "other")]); + let session = get_test_session_with_config( + DatabaseProtocol::PostgreSQL, + Arc::new(ConfigObjImpl::default()), + meta.clone(), + ) + .await; + + let logical_plan = convert_sql_to_cube_query( + &"SELECT 'MX' AS table_type, customer_gender AS market \ + FROM KibanaSampleDataEcommerce GROUP BY 1, 2 \ + UNION ALL \ + SELECT 'RX' AS table_type, content AS market FROM Logs GROUP BY 1, 2" + .to_string(), + meta, + session, + ) + .await + .expect("union of two data sources should compile") + .as_logical_plan(); + + assert_eq!( + logical_plan.find_cube_scans().len(), + 2, + "every query keeps its own scan: {:?}", + logical_plan + ); + assert!( + matches!(logical_plan, LogicalPlan::Union(_)), + "the union is left to post processing: {:?}", + logical_plan + ); +} + +/// A data source with no union template cannot render a set operation. The rule has to +/// know that before the plan is chosen, because SQL generation has no way back to post +/// processing. +#[tokio::test] +async fn test_wrapper_union_without_template_not_pushed_down() { + if !Rewriter::sql_push_down_enabled() { + return; + } + init_testing_logger(); + + let logical_plan = convert_select_to_query_plan_customized( + "SELECT 'MX' AS table_type, customer_gender AS market \ + FROM KibanaSampleDataEcommerce GROUP BY 1, 2 \ + UNION ALL \ + SELECT 'RX' AS table_type, content AS market FROM Logs GROUP BY 1, 2" + .to_string(), + DatabaseProtocol::PostgreSQL, + // An empty template removes it from this data source + vec![("statements/union".to_string(), "".to_string())], + ) + .await + .as_logical_plan(); + + assert!( + matches!(logical_plan, LogicalPlan::Union(_)), + "the union is left to post processing: {:?}", + logical_plan + ); +} + +/// A union left to post processing keeps every one of its queries. `UnionInputs` is a flat +/// list, so the converter reads it back with `match_list_node_ids!` rather than by walking +/// cons cells — this pins that round trip at more than two queries, where the two shapes +/// differ. +#[tokio::test] +async fn test_wrapper_union_three_queries_not_pushed_down_keeps_every_query() { + if !Rewriter::sql_push_down_enabled() { + return; + } + init_testing_logger(); + + let logical_plan = convert_select_to_query_plan_customized( + "SELECT 'MX' AS table_type, customer_gender AS market \ + FROM KibanaSampleDataEcommerce GROUP BY 1, 2 \ + UNION ALL \ + SELECT 'RX' AS table_type, content AS market FROM Logs GROUP BY 1, 2 \ + UNION ALL \ + SELECT 'DX' AS table_type, notes AS market \ + FROM KibanaSampleDataEcommerce GROUP BY 1, 2" + .to_string(), + DatabaseProtocol::PostgreSQL, + // An empty template removes it from this data source + vec![("statements/union".to_string(), "".to_string())], + ) + .await + .as_logical_plan(); + + let LogicalPlan::Union(union) = &logical_plan else { + panic!("the union is left to post processing: {:?}", logical_plan); + }; + assert_eq!( + union.inputs.len(), + 3, + "one input per query: {:?}", + logical_plan + ); + // The list is written and read back positionally, so identity and order are what the + // flat conversion put at risk: a reader that reversed it, or yielded one query twice, + // would still leave three of everything. Order is not cosmetic for a union — the output + // column names come from the first query, and `WrappedUnionNode` renders it specially. + assert_eq!( + logical_plan + .find_cube_scans() + .iter() + .map(|scan| member_expression_sql(&scan.request.dimensions)) + .collect::>(), + vec![ + vec!["KibanaSampleDataEcommerce.customer_gender".to_string()], + vec!["Logs.content".to_string()], + vec!["KibanaSampleDataEcommerce.notes".to_string()], + ], + "every query survives the round trip, in order: {:?}", + logical_plan + ); +} + +/// A `Sort` directly above a union is not pushed down yet: the select it would be pushed +/// into projects nothing, and a query without a projection is not SQL. The union itself is +/// still pushed down, and the sort of its truncated result is still post processing that +/// `CUBESQL_FAIL_ON_LIMITLESS_POST_PROCESSING` reports. +#[tokio::test] +async fn test_wrapper_union_sort_left_to_post_processing() { + if !Rewriter::sql_push_down_enabled() { + return; + } + init_testing_logger(); + + let query = "SELECT 'MX' AS table_type, customer_gender AS market \ + FROM KibanaSampleDataEcommerce GROUP BY 1, 2 \ + UNION ALL \ + SELECT 'RX' AS table_type, content AS market FROM Logs GROUP BY 1, 2 \ + ORDER BY 1, 2"; + + let logical_plan = + convert_select_to_query_plan(query.to_string(), DatabaseProtocol::PostgreSQL) + .await + .as_logical_plan(); + assert!( + matches!(logical_plan, LogicalPlan::Sort(_)), + "the sort is left to post processing: {:?}", + logical_plan + ); + let sql = logical_plan + .find_cube_scan_wrapped_sql_deep() + .wrapped_sql + .sql; + assert!(sql.contains("UNION ALL"), "union is pushed down: {}", sql); + + let mut config = ConfigObjImpl::default(); + config.fail_on_limitless_post_processing = true; + let context = TestContext::with_config(DatabaseProtocol::PostgreSQL, Arc::new(config)).await; + let error = context + .convert_sql_to_cube_query(query) + .await + .expect_err("sorting a truncated union should fail"); + assert!( + error + .to_string() + .contains("CUBESQL_FAIL_ON_LIMITLESS_POST_PROCESSING"), + "unexpected error: {}", + error + ); +} + +/// A limit on one query of a union bounds that query, and the row cap bounds the result of +/// the whole set operation. Both end up in the pushed down SQL, so neither query is read +/// through the other's limit. +#[tokio::test] +async fn test_wrapper_union_limited_query_push_down() { + if !Rewriter::sql_push_down_enabled() { + return; + } + init_testing_logger(); + + let logical_plan = convert_select_to_query_plan( + "(SELECT customer_gender AS g FROM KibanaSampleDataEcommerce GROUP BY 1 LIMIT 10) \ + UNION ALL \ + (SELECT content AS g FROM Logs GROUP BY 1)" + .to_string(), + DatabaseProtocol::PostgreSQL, + ) + .await + .as_logical_plan(); + + let limits = logical_plan + .find_cube_scans() + .into_iter() + .map(|scan| scan.request.limit) + .collect::>(); + assert_eq!( + limits, + vec![Some(10), None], + "the limit stays with the query it was written on" + ); + + let sql = logical_plan.find_cube_scan_wrapped_sql().wrapped_sql.sql; + assert!(sql.contains("UNION ALL"), "union is pushed down: {}", sql); + assert!( + sql.trim_end().ends_with(&format!( + "LIMIT {}", + ConfigObjImpl::default().non_streaming_query_max_row_limit + )), + "the row cap bounds the union: {}", + sql + ); +} + +/// A pushed down union is a query the rest of the wrapper can build on: joining against one +/// reads it as a subquery of the Cube query, rather than leaving the union to DataFusion. +#[tokio::test] +async fn test_wrapper_union_in_join_push_down() { + if !Rewriter::sql_push_down_enabled() { + return; + } + init_testing_logger(); + + let sql = convert_select_to_query_plan( + "SELECT k.customer_gender, u.market \ + FROM KibanaSampleDataEcommerce k \ + JOIN ( \ + SELECT 'MX' AS table_type, customer_gender AS market \ + FROM KibanaSampleDataEcommerce GROUP BY 1, 2 \ + UNION ALL \ + SELECT 'RX' AS table_type, content AS market FROM Logs GROUP BY 1, 2 \ + ) u ON k.customer_gender = u.market \ + GROUP BY 1, 2" + .to_string(), + DatabaseProtocol::PostgreSQL, + ) + .await + .as_logical_plan() + .find_cube_scan_wrapped_sql() + .wrapped_sql + .sql; + + // The union has to be in the SQL as the join's subquery, not merely somewhere in it: + // a union that lost its join, or a join that lost its union, would both be wrong + assert!( + sql.contains("UNION ALL"), + "the union is pushed down: {}", + sql + ); + assert!( + sql.contains("subqueryJoins"), + "the union is read as the join's subquery: {}", + sql + ); + assert!( + sql.contains("KibanaSampleDataEcommerce.customer_gender") && sql.contains("Logs.content"), + "both queries of the union are in the pushed down SQL: {}", + sql + ); +} diff --git a/rust/cubesql/cubesql/src/transport/service.rs b/rust/cubesql/cubesql/src/transport/service.rs index 764748ff7f4bc..56a55b959e53c 100644 --- a/rust/cubesql/cubesql/src/transport/service.rs +++ b/rust/cubesql/cubesql/src/transport/service.rs @@ -1090,6 +1090,20 @@ impl SqlTemplates { context! { join_type => join_type, source => source, condition => condition }, ) } + + /// Renders `queries` as a single set operation: `UNION` when `distinct`, `UNION ALL` + /// otherwise. `limit` bounds the result of the whole operation, not of any one query. + pub fn union( + &self, + queries: Vec, + distinct: bool, + limit: Option, + ) -> Result { + self.render_template( + "statements/union", + context! { queries => queries, distinct => distinct, limit => limit }, + ) + } } #[cfg(test)] diff --git a/rust/cubesql/package.json b/rust/cubesql/package.json index 859f718826eec..0438d17f69b34 100644 --- a/rust/cubesql/package.json +++ b/rust/cubesql/package.json @@ -1,6 +1,6 @@ { "name": "@cubejs-backend/cubesql", - "version": "1.7.26", + "version": "1.7.27", "description": "SQL API for Cube as proxy over MySQL protocol.", "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" diff --git a/rust/cubestore/CHANGELOG.md b/rust/cubestore/CHANGELOG.md index 7b6c74e5ef027..81ce3ccd43421 100644 --- a/rust/cubestore/CHANGELOG.md +++ b/rust/cubestore/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.7.27](https://github.com/cube-js/cube/compare/v1.7.26...v1.7.27) (2026-08-26) + +### Bug Fixes + +- **cubestore:** inline aggregate dropped rows past the first partition ([#11631](https://github.com/cube-js/cube/issues/11631)) ([6f3e66d](https://github.com/cube-js/cube/commit/6f3e66d4fcf89733a21d730d3e90fcb493a72dd0)) + ## [1.7.26](https://github.com/cube-js/cube/compare/v1.7.25...v1.7.26) (2026-08-24) ### Bug Fixes diff --git a/rust/cubestore/Cargo.lock b/rust/cubestore/Cargo.lock index bfbfca08c7543..7571451e9c16f 100644 --- a/rust/cubestore/Cargo.lock +++ b/rust/cubestore/Cargo.lock @@ -1445,7 +1445,7 @@ dependencies = [ [[package]] name = "cubestore" -version = "1.7.26" +version = "1.7.27" dependencies = [ "actix-rt", "anyhow", diff --git a/rust/cubestore/cubestore/Cargo.toml b/rust/cubestore/cubestore/Cargo.toml index 84baf085915f5..161d1d17d9e30 100644 --- a/rust/cubestore/cubestore/Cargo.toml +++ b/rust/cubestore/cubestore/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cubestore" -version = "1.7.26" +version = "1.7.27" authors = ["Cube Dev, Inc."] edition = "2021" license = "Apache-2.0" diff --git a/rust/cubestore/package.json b/rust/cubestore/package.json index e76557833fd8e..5d350f17211d9 100644 --- a/rust/cubestore/package.json +++ b/rust/cubestore/package.json @@ -1,6 +1,6 @@ { "name": "@cubejs-backend/cubestore", - "version": "1.7.26", + "version": "1.7.27", "description": "Cube.js pre-aggregation storage layer.", "main": "dist/src/index.js", "typings": "dist/src/index.d.ts", @@ -33,7 +33,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@cubejs-backend/linter": "1.7.26", + "@cubejs-backend/linter": "1.7.27", "@types/jest": "^29", "@types/node": "^18", "jest": "^29", @@ -43,7 +43,7 @@ "access": "public" }, "dependencies": { - "@cubejs-backend/shared": "1.7.26", + "@cubejs-backend/shared": "1.7.27", "@octokit/core": "^3.2.5", "source-map-support": "^0.5.19" },