From 5d67018bbb53159ff9c82a2ddcad8c06065eae93 Mon Sep 17 00:00:00 2001 From: dragosrep Date: Thu, 12 Jun 2025 03:55:18 +0300 Subject: [PATCH 1/2] Set alias for dimension columns in ClickHouse queries --- .../src/adapter/ClickHouseQuery.ts | 11 + .../clickhouse/complex-joins.test.ts | 210 ++++++++++++++++++ 2 files changed, 221 insertions(+) create mode 100644 packages/cubejs-schema-compiler/test/integration/clickhouse/complex-joins.test.ts diff --git a/packages/cubejs-schema-compiler/src/adapter/ClickHouseQuery.ts b/packages/cubejs-schema-compiler/src/adapter/ClickHouseQuery.ts index 18b1eec6322e7..6869f726c619b 100644 --- a/packages/cubejs-schema-compiler/src/adapter/ClickHouseQuery.ts +++ b/packages/cubejs-schema-compiler/src/adapter/ClickHouseQuery.ts @@ -266,6 +266,17 @@ export class ClickHouseQuery extends BaseQuery { return `ALTER TABLE ${tableName} ADD INDEX ${indexName} (${escapedColumns.join(', ')}) TYPE minmax GRANULARITY 1`; } + public dimensionColumns(cubeAlias) { + // For the top-level SELECT statement, explicitly set the column alias. + // Clickhouse sometimes includes the "q_0" prefix in the column name, and this + // leads to errors during the result mapping. + if (cubeAlias === 'q_0') { + return this.dimensionAliasNames().map(alias => `${cubeAlias && `${cubeAlias}.` || ''}${alias} ${alias}`); + } else { + return super.dimensionColumns(cubeAlias); + } + } + public sqlTemplates() { const templates = super.sqlTemplates(); templates.functions.DATETRUNC = 'DATE_TRUNC({{ args_concat }})'; diff --git a/packages/cubejs-schema-compiler/test/integration/clickhouse/complex-joins.test.ts b/packages/cubejs-schema-compiler/test/integration/clickhouse/complex-joins.test.ts new file mode 100644 index 0000000000000..b830c27ab8d62 --- /dev/null +++ b/packages/cubejs-schema-compiler/test/integration/clickhouse/complex-joins.test.ts @@ -0,0 +1,210 @@ +import { prepareJsCompiler } from '../../unit/PrepareCompiler'; +import { ClickHouseDbRunner } from './ClickHouseDbRunner'; + +describe('ClickHouse complex joins', () => { + jest.setTimeout(20 * 1000); + + const dbRunner = new ClickHouseDbRunner(); + + afterAll(async () => { + await dbRunner.tearDown(); + }); + + const { compiler, joinGraph, cubeEvaluator } = prepareJsCompiler(` + cube(\`Acube\`, { + sql: \`SELECT * FROM (SELECT 1 as id, 'Category A' as category, 10 as value UNION ALL + SELECT 2, 'Category B', 20 UNION ALL + SELECT 3, 'Category C', 30) as t\`, + + joins: { + Bcube: { + relationship: \`one_to_many\`, + sql: \`\${Acube}.id = \${Bcube}.id\` + }, + Ccube: { + relationship: \`one_to_many\`, + sql: \`\${Acube}.id = \${Ccube}.id\` + }, + }, + + measures: { + AcubeCount: { + type: \`count\`, + }, + AcubeTotalValue: { + type: \`sum\`, + sql: \`value\` + }, + BcubeTotalValue: { + sql: \`\${Bcube.totalValue}\`, + type: \`number\` + }, + CcubeTotalAmount: { + sql: \`\${Ccube.totalAmount}\`, + type: \`number\` + }, + }, + + dimensions: { + id: { + sql: \`id\`, + type: \`number\`, + primaryKey: true, + shown: true, + title: \`id\` + }, + category: { + sql: \`category\`, + type: \`string\` + } + } + }); + + cube(\`Bcube\`, { + sql: \`SELECT 1 as id, 'Bgroup1' as groupName, 50 as value UNION ALL + SELECT 2, 'Bgroup2', 60 UNION ALL + SELECT 3, 'Bgroup3', 70\`, + + measures: { + count: { + type: \`count\` + }, + totalValue: { + type: \`sum\`, + sql: \`value\` + } + }, + + dimensions: { + id: { + sql: \`id\`, + type: \`number\`, + primaryKey: true + }, + groupName: { + sql: \`groupName\`, + type: \`string\` + } + } + }); + + cube(\`Ccube\`, { + sql: \`SELECT 1 as id, 'Ctype1' as type, 15 as amount UNION ALL + SELECT 2, 'Ctype2', 25 UNION ALL + SELECT 3, 'Ctype3', 35\`, + + measures: { + count: { + type: \`count\` + }, + totalAmount: { + type: \`sum\`, + sql: \`amount\` + } + }, + + dimensions: { + id: { + sql: \`id\`, + type: \`number\`, + primaryKey: true, + shown: true + }, + type: { + sql: \`type\`, + type: \`string\` + } + } + }); + `); + + it('query with 1 cube join', async () => dbRunner.runQueryTest( + { + dimensions: [ + 'Acube.category', + 'Acube.id' + ], + measures: [ + 'Acube.AcubeCount', + 'Acube.AcubeTotalValue', + 'Acube.BcubeTotalValue' + ], + order: [{ + id: 'Acube.id', + desc: false + }], + queryType: 'multi' + }, + [ + { + acube__category: 'Category A', + acube__id: '1', + acube___acube_count: '1', + acube___acube_total_value: '10', + acube___bcube_total_value: '50', + }, + { + acube__category: 'Category B', + acube__id: '2', + acube___acube_count: '1', + acube___acube_total_value: '20', + acube___bcube_total_value: '60', + }, + { + acube__category: 'Category C', + acube__id: '3', + acube___acube_count: '1', + acube___acube_total_value: '30', + acube___bcube_total_value: '70', + } + ], + { joinGraph, cubeEvaluator, compiler } + )); + + it('query with 2 cube joins', async () => dbRunner.runQueryTest( + { + dimensions: [ + 'Acube.category', + 'Acube.id' + ], + measures: [ + 'Acube.AcubeCount', + 'Acube.AcubeTotalValue', + 'Acube.BcubeTotalValue', + 'Acube.CcubeTotalAmount' + ], + order: [{ + id: 'Acube.id', + desc: false + }], + queryType: 'multi' + }, + [ + { + acube__category: 'Category A', + acube__id: '1', + acube___acube_count: '1', + acube___acube_total_value: '10', + acube___bcube_total_value: '50', + acube___ccube_total_amount: '15', + }, + { + acube__category: 'Category B', + acube__id: '2', + acube___acube_count: '1', + acube___acube_total_value: '20', + acube___bcube_total_value: '60', + acube___ccube_total_amount: '25', + }, + { + acube__category: 'Category C', + acube__id: '3', + acube___acube_count: '1', + acube___acube_total_value: '30', + acube___bcube_total_value: '70', + acube___ccube_total_amount: '35', + } + ], + { joinGraph, cubeEvaluator, compiler } + )); +}); From 4e20c5e1dca18a12b5e4fab7cdfe7aee6facbe88 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Mon, 7 Jul 2025 11:03:27 +0300 Subject: [PATCH 2/2] Apply suggestion from @KSDaemon --- packages/cubejs-schema-compiler/src/adapter/ClickHouseQuery.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cubejs-schema-compiler/src/adapter/ClickHouseQuery.ts b/packages/cubejs-schema-compiler/src/adapter/ClickHouseQuery.ts index 2b6b0890d12d8..8c3cdaedea6b1 100644 --- a/packages/cubejs-schema-compiler/src/adapter/ClickHouseQuery.ts +++ b/packages/cubejs-schema-compiler/src/adapter/ClickHouseQuery.ts @@ -245,7 +245,7 @@ export class ClickHouseQuery extends BaseQuery { // Clickhouse sometimes includes the "q_0" prefix in the column name, and this // leads to errors during the result mapping. if (cubeAlias === 'q_0') { - return this.dimensionAliasNames().map(alias => `${cubeAlias && `${cubeAlias}.` || ''}${alias} ${alias}`); + return this.dimensionAliasNames().map(alias => `${cubeAlias}.${alias} ${alias}`); } else { return super.dimensionColumns(cubeAlias); }