From 42fec38294c6d064d56f7497eff741244b7618f7 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Fri, 17 Oct 2025 18:00:08 +0300 Subject: [PATCH 1/4] fix(tesseract): Fix member name case conversion Prepend "_" even to the first UPPERCASE letter in the name --- .../src/planner/sql_templates/plan.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_templates/plan.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_templates/plan.rs index 6d448a9152485..d0773bf7935d2 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_templates/plan.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_templates/plan.rs @@ -161,10 +161,23 @@ impl PlanSqlTemplates { } else { format!("") }; + + + // For strange reasons, the javascript snake caser used in cube + // changes first UPPERCASE letter to "_lowercase", prepending "_" + // This is weird, but to be compatible we have to add it too. + let mut member_alias = Self::alias_name(name); + + if let Some(fl) = name.chars().next() { + if fl.is_uppercase() { + member_alias = format!("_{}", member_alias); + } + } + format!( "{}__{}{}", Self::alias_name(cube_name), - Self::alias_name(name), + member_alias, suffix ) } From c59878f1aaa366add2edb1708ac9b4a90b691271 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Fri, 17 Oct 2025 18:56:02 +0300 Subject: [PATCH 2/4] add tests --- .../postgres/sql-generation.test.ts | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/packages/cubejs-schema-compiler/test/integration/postgres/sql-generation.test.ts b/packages/cubejs-schema-compiler/test/integration/postgres/sql-generation.test.ts index 470876605c21b..af22e9044b3c9 100644 --- a/packages/cubejs-schema-compiler/test/integration/postgres/sql-generation.test.ts +++ b/packages/cubejs-schema-compiler/test/integration/postgres/sql-generation.test.ts @@ -361,6 +361,31 @@ describe('SQL Generation', () => { type: 'string', sql: 'source' }, + // dimensions for testing letter cases + my_favorite_source: { + type: 'string', + sql: 'source' + }, + My_favorite_source: { + type: 'string', + sql: 'source' + }, + MY_favorite_source: { + type: 'string', + sql: 'source' + }, + My_Favorite_Source: { + type: 'string', + sql: 'source' + }, + MY_Favorite_SOURCE: { + type: 'string', + sql: 'source' + }, + MY_FAVORITE_SOURCE: { + type: 'string', + sql: 'source' + }, created_at: { type: 'time', sql: 'created_at', @@ -5372,4 +5397,54 @@ cubes: ]); }); }); + + it('Checking member name letter cases', async () => { + await compiler.compile(); + + const query = new PostgresQuery({ joinGraph, cubeEvaluator, compiler }, { + measures: [], + dimensions: [ + 'visitors.my_favorite_source', + 'visitors.My_favorite_source', + 'visitors.MY_favorite_source', + 'visitors.My_Favorite_Source', + 'visitors.MY_Favorite_SOURCE', + 'visitors.MY_FAVORITE_SOURCE', + ], + order: [{ + id: 'visitors.created_at' + }], + timezone: 'America/Los_Angeles' + }); + + const res = await dbRunner.testQuery(query.buildSqlAndParams()); + console.log(JSON.stringify(res)); + + expect(res).toEqual([ + { + visitors___m_y__f_a_v_o_r_i_t_e__s_o_u_r_c_e: null, + visitors___m_y__favorite__s_o_u_r_c_e: null, + visitors___m_y_favorite_source: null, + visitors___my__favorite__source: null, + visitors___my_favorite_source: null, + visitors__my_favorite_source: null, + }, + { + visitors___m_y__f_a_v_o_r_i_t_e__s_o_u_r_c_e: 'google', + visitors___m_y__favorite__s_o_u_r_c_e: 'google', + visitors___m_y_favorite_source: 'google', + visitors___my__favorite__source: 'google', + visitors___my_favorite_source: 'google', + visitors__my_favorite_source: 'google', + }, + { + visitors___m_y__f_a_v_o_r_i_t_e__s_o_u_r_c_e: 'some', + visitors___m_y__favorite__s_o_u_r_c_e: 'some', + visitors___m_y_favorite_source: 'some', + visitors___my__favorite__source: 'some', + visitors___my_favorite_source: 'some', + visitors__my_favorite_source: 'some', + }, + ]); + }); }); From 36814a50075657686502f412b672fb552cfdf413 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Fri, 17 Oct 2025 18:56:11 +0300 Subject: [PATCH 3/4] fix tests --- .../postgres/views-join-order-2.test.ts | 5 ++--- .../postgres/views-join-order-3.test.ts | 21 +++++++++---------- .../views-join-order-join-maps.test.ts | 13 ++++++------ .../cubejs-schema-compiler/test/unit/utils.ts | 15 ------------- 4 files changed, 18 insertions(+), 36 deletions(-) diff --git a/packages/cubejs-schema-compiler/test/integration/postgres/views-join-order-2.test.ts b/packages/cubejs-schema-compiler/test/integration/postgres/views-join-order-2.test.ts index 0eb9b7f5f5d00..7f278639ff8c5 100644 --- a/packages/cubejs-schema-compiler/test/integration/postgres/views-join-order-2.test.ts +++ b/packages/cubejs-schema-compiler/test/integration/postgres/views-join-order-2.test.ts @@ -1,6 +1,5 @@ import { prepareJsCompiler } from '../../unit/PrepareCompiler'; import { dbRunner } from './PostgresDBRunner'; -import { transformResultsForTesseractIfNeeded } from '../../unit/utils'; describe('Views Join Order 2', () => { jest.setTimeout(200000); @@ -161,7 +160,7 @@ cube('D', { total: true, renewQuery: false, limit: 1 - }, transformResultsForTesseractIfNeeded([{ + }, [{ view___a_id: 1, view___a_name: 'a', view___b_id: 2, @@ -170,5 +169,5 @@ cube('D', { view___d_name: 'd', view___e_id: 4, view___e_name: 'e', - }]), { compiler, joinGraph, cubeEvaluator })); + }], { compiler, joinGraph, cubeEvaluator })); }); diff --git a/packages/cubejs-schema-compiler/test/integration/postgres/views-join-order-3.test.ts b/packages/cubejs-schema-compiler/test/integration/postgres/views-join-order-3.test.ts index 0c87f01bf2b58..ff718b5a15d04 100644 --- a/packages/cubejs-schema-compiler/test/integration/postgres/views-join-order-3.test.ts +++ b/packages/cubejs-schema-compiler/test/integration/postgres/views-join-order-3.test.ts @@ -1,6 +1,5 @@ import { prepareJsCompiler } from '../../unit/PrepareCompiler'; import { dbRunner } from './PostgresDBRunner'; -import { transformResultsForTesseractIfNeeded } from '../../unit/utils'; /** * This tests the cube join correctness for cases, when there are @@ -126,9 +125,9 @@ view(\`V\`, { timeDimensions: [], segments: [], filters: [], - }, transformResultsForTesseractIfNeeded([{ + }, [{ b___activity_balance: 125, - }]), { compiler, joinGraph, cubeEvaluator }); + }], { compiler, joinGraph, cubeEvaluator }); expect(sql).toMatch(/AS "b"/); expect(sql).toMatch(/AS "d"/); @@ -143,36 +142,36 @@ view(\`V\`, { timeDimensions: [], segments: [], filters: [], - }, transformResultsForTesseractIfNeeded([{ + }, [{ v___activity_balance: 125, - }]), { compiler, joinGraph, cubeEvaluator })); + }], { compiler, joinGraph, cubeEvaluator })); it('correct join for simple view F-dimension', async () => dbRunner.runQueryTest({ dimensions: ['V.PlanCode'], timeDimensions: [], segments: [], filters: [], - }, transformResultsForTesseractIfNeeded([{ + }, [{ v___plan_code: 'PLAN_CODE', - }]), { compiler, joinGraph, cubeEvaluator })); + }], { compiler, joinGraph, cubeEvaluator })); it('correct join for view F-dimension + B-dimension', async () => dbRunner.runQueryTest({ dimensions: ['V.PlanCode', 'V.ActivityBalance'], timeDimensions: [], segments: [], filters: [], - }, transformResultsForTesseractIfNeeded([{ + }, [{ v___plan_code: 'PLAN_CODE', v___activity_balance: 125, - }]), { compiler, joinGraph, cubeEvaluator })); + }], { compiler, joinGraph, cubeEvaluator })); it('correct join for view B-dimension + F-dimension', async () => dbRunner.runQueryTest({ dimensions: ['V.ActivityBalance', 'V.PlanCode'], timeDimensions: [], segments: [], filters: [], - }, transformResultsForTesseractIfNeeded([{ + }, [{ v___activity_balance: 125, v___plan_code: 'PLAN_CODE', - }]), { compiler, joinGraph, cubeEvaluator })); + }], { compiler, joinGraph, cubeEvaluator })); }); diff --git a/packages/cubejs-schema-compiler/test/integration/postgres/views-join-order-join-maps.test.ts b/packages/cubejs-schema-compiler/test/integration/postgres/views-join-order-join-maps.test.ts index 975ab47c9b1d4..cb598c55042e4 100644 --- a/packages/cubejs-schema-compiler/test/integration/postgres/views-join-order-join-maps.test.ts +++ b/packages/cubejs-schema-compiler/test/integration/postgres/views-join-order-join-maps.test.ts @@ -1,6 +1,5 @@ import { prepareJsCompiler } from '../../unit/PrepareCompiler'; import { dbRunner } from './PostgresDBRunner'; -import { transformResultsForTesseractIfNeeded } from '../../unit/utils'; describe('Views Join Order using join maps', () => { jest.setTimeout(200000); @@ -140,11 +139,11 @@ cube('D', { segments: [], filters: [], total: true, - }, transformResultsForTesseractIfNeeded([{ + }, [{ view___a_id: 1, view___a_name: 'a', view___a_d_name: 'd3', - }]), { compiler, joinGraph, cubeEvaluator }); + }], { compiler, joinGraph, cubeEvaluator }); expect(sql).toMatch(/AS "b"/); expect(sql).toMatch(/AS "c"/); @@ -166,11 +165,11 @@ cube('D', { segments: [], filters: [], total: true, - }, transformResultsForTesseractIfNeeded([{ + }, [{ view___a_id: 1, view___a_name: 'a', view___a_c_name: 'c1', - }]), { compiler, joinGraph, cubeEvaluator }); + }], { compiler, joinGraph, cubeEvaluator }); expect(sql).toMatch(/AS "b"/); expect(sql).toMatch(/AS "c"/); @@ -193,12 +192,12 @@ cube('D', { segments: [], filters: [], total: true, - }, transformResultsForTesseractIfNeeded([{ + }, [{ view___a_id: 1, view___a_name: 'a', view___a_c_name: 'c1', view___a_d_name: 'd3', - }]), { compiler, joinGraph, cubeEvaluator }); + }], { compiler, joinGraph, cubeEvaluator }); expect(sql).toMatch(/AS "b"/); expect(sql).toMatch(/AS "c"/); diff --git a/packages/cubejs-schema-compiler/test/unit/utils.ts b/packages/cubejs-schema-compiler/test/unit/utils.ts index 37b4f1d442ed4..acc73c2a24e6d 100644 --- a/packages/cubejs-schema-compiler/test/unit/utils.ts +++ b/packages/cubejs-schema-compiler/test/unit/utils.ts @@ -769,18 +769,3 @@ export function createJoinedCubesSchema(): string { }); `; } - -export function transformResultsForTesseractIfNeeded(results: Record[]) { - if (getEnv('nativeSqlPlanner')) { - return results.map(record => { - const fixedRecord: Record = {}; - for (const [key, value] of Object.entries(record)) { - const fixedKey = key.replace(/___/g, '__'); - fixedRecord[fixedKey] = value; - } - return fixedRecord; - }); - } - - return results; -} From 32cc848220ff52f1516592edc9fdff3c3ab34642 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Fri, 17 Oct 2025 19:14:04 +0300 Subject: [PATCH 4/4] cargo fmt --- .../cubesqlplanner/src/planner/sql_templates/plan.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_templates/plan.rs b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_templates/plan.rs index d0773bf7935d2..992f1ced47720 100644 --- a/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_templates/plan.rs +++ b/rust/cubesqlplanner/cubesqlplanner/src/planner/sql_templates/plan.rs @@ -162,7 +162,6 @@ impl PlanSqlTemplates { format!("") }; - // For strange reasons, the javascript snake caser used in cube // changes first UPPERCASE letter to "_lowercase", prepending "_" // This is weird, but to be compatible we have to add it too.