You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A GraphQL query using the root orderBy argument on the cube(...) field fails under the Tesseract SQL planner with TesseractUserError: Cannot resolve: <CapitalizedCubeName> whenever the cube's name: starts with a lowercase letter — reproduces for snake_case and camelCase names alike (e.g. orders, lowercaseOrders). The identical cube works fine in orderBy if its name: happens to start with an uppercase letter (e.g. UppercaseOrders), even though GraphQL's own schema generation still exposes it as a lowercase-first field (uppercaseOrders) in the selection set. where filters, and the alternate per-cube orderBy syntax (cube { someCube(orderBy: {...}) }, as opposed to the root cube(orderBy: {...}) form), both work correctly regardless of casing — only the root-levelorderBy is affected.
Root cause
This traces to getJsonQuery in packages/cubejs-api-gateway/src/graphql.ts, where the root orderBy handler unconditionally capitalizes the cube name with no check for whether the cube already exists under its real (as-declared) name:
For a cube named lowercaseOrders, this produces the order path "LowercaseOrders.count" — a string that doesn't match any real cube name, since the actual cube is lowercaseOrders.
This same file already has the correct, guarded pattern in three other places just a few lines away, which the root orderBy handler should be using instead:
Per-cube orderBy (the cube { someCube(orderBy: {...}) } form, in the same getJsonQuery function) — const cubeName = cubeExists ? cubeNode.name.value : capitalize(cubeNode.name.value);
Only the root orderBy block skips the existence check and always capitalizes.
This also explains why this reads as a "Tesseract bug" without being one in origin: getJsonQuery feeds the same (incorrect) order path to whichever planner is active. The legacy planner's cube-name resolution appears to tolerate the mismatch (likely a case-insensitive or otherwise more forgiving lookup); Tesseract's resolver (resolve_cube_name in rust/cube/cubesqlplanner/cubesqlplanner/src/planner/symbols/common/symbol_path.rs) does an exact, case-sensitive cube_evaluator.cube_exists(name) check with no fallback, so it's the first place this pre-existing bug produces a hard failure instead of silently resolving anyway.
Suggested fix
Mirror the guarded pattern already used elsewhere in the same file:
Run Cube with the minimal schema below (two cubes, identical except for the first letter of name:) with CUBEJS_TESSERACT_SQL_PLANNER unset (default/Tesseract).
Result: succeeds, returns data — even though the cube is declared as UppercaseOrders and GraphQL only exposes it as the lowercase-first field uppercaseOrders.
Remove orderBy from either query (keep where/limit only) and note both succeed regardless of casing — only orderBy is affected.
Expected behavior
The orderBy: { <cube_name>: { <measure_or_dimension>: asc | desc } } argument should order results by the given field regardless of what case the cube's name: starts with, the same way it does under the legacy planner (CUBEJS_TESSERACT_SQL_PLANNER=false), which handles both of the schemas below correctly.
Minimally reproducible Cube Schema
cubes:
- name: lowercaseOrderssql: > SELECT 1 AS id, 'completed' AS status UNION ALL SELECT 2 AS id, 'completed' AS status UNION ALL SELECT 3 AS id, 'processing' AS statusdimensions:
- name: idsql: idtype: numberprimary_key: true
- name: statussql: statustype: stringmeasures:
- name: counttype: count
- name: UppercaseOrderssql: > SELECT 1 AS id, 'completed' AS status UNION ALL SELECT 2 AS id, 'completed' AS status UNION ALL SELECT 3 AS id, 'processing' AS statusdimensions:
- name: idsql: idtype: numberprimary_key: true
- name: statussql: statustype: stringmeasures:
- name: counttype: count
Version
Cube image: cubejs/cube:latest, resolved to v1.7.30
SQL planner: Tesseract (default; bug does not reproduce with CUBEJS_TESSERACT_SQL_PLANNER=false, consistent with the legacy planner's cube-name resolution tolerating the mismatched capitalization)
Data source: reproduced against MSSQL (CUBEJS_DB_TYPE=mssql); noted since Tesseract engine generating Postgresql dialect queries for MSSQL DB #9567 shows other Tesseract/MSSQL-specific issues, but this bug is not MSSQL-specific — the root cause is in the shared GraphQL query-building layer (getJsonQuery), not the planner or SQL generation, and it also reproduces with the synthetic inline-SQL cube above.
The per-cube orderBy syntax (cube { someCube(orderBy: {...}) { ... } }) already has the correct guarded capitalization check in the same file (not independently verified against a live server here, but should be a viable workaround based on the code) in place of the root cube(orderBy: {...}) form.
Since Tesseract became the default SQL planner as of Cube Core v1.7 GA, this latent bug likely affects any GraphQL API consumer using root-level orderBy who hasn't explicitly opted into the (deprecated) legacy planner.
Describe the bug
A GraphQL query using the root
orderByargument on thecube(...)field fails under the Tesseract SQL planner withTesseractUserError: Cannot resolve: <CapitalizedCubeName>whenever the cube'sname:starts with a lowercase letter — reproduces for snake_case and camelCase names alike (e.g.orders,lowercaseOrders). The identical cube works fine inorderByif itsname:happens to start with an uppercase letter (e.g.UppercaseOrders), even though GraphQL's own schema generation still exposes it as a lowercase-first field (uppercaseOrders) in the selection set.wherefilters, and the alternate per-cubeorderBysyntax (cube { someCube(orderBy: {...}) }, as opposed to the rootcube(orderBy: {...})form), both work correctly regardless of casing — only the root-levelorderByis affected.Root cause
This traces to
getJsonQueryinpackages/cubejs-api-gateway/src/graphql.ts, where the rootorderByhandler unconditionally capitalizes the cube name with no check for whether the cube already exists under its real (as-declared) name:For a cube named
lowercaseOrders, this produces the order path"LowercaseOrders.count"— a string that doesn't match any real cube name, since the actual cube islowercaseOrders.This same file already has the correct, guarded pattern in three other places just a few lines away, which the root
orderByhandler should be using instead:getMemberType—metaConfig.find(cube => cube.config.name === cubeName || cube.config.name === capitalize(cubeName))whereArgToQueryFilters(rootwhere) —const normalizedKey = cubeExists ? key : capitalize(key);orderBy(thecube { someCube(orderBy: {...}) }form, in the samegetJsonQueryfunction) —const cubeName = cubeExists ? cubeNode.name.value : capitalize(cubeNode.name.value);Only the root
orderByblock skips the existence check and always capitalizes.This also explains why this reads as a "Tesseract bug" without being one in origin:
getJsonQueryfeeds the same (incorrect) order path to whichever planner is active. The legacy planner's cube-name resolution appears to tolerate the mismatch (likely a case-insensitive or otherwise more forgiving lookup); Tesseract's resolver (resolve_cube_nameinrust/cube/cubesqlplanner/cubesqlplanner/src/planner/symbols/common/symbol_path.rs) does an exact, case-sensitivecube_evaluator.cube_exists(name)check with no fallback, so it's the first place this pre-existing bug produces a hard failure instead of silently resolving anyway.Suggested fix
Mirror the guarded pattern already used elsewhere in the same file:
To Reproduce
name:) withCUBEJS_TESSERACT_SQL_PLANNERunset (default/Tesseract).{ cube(orderBy: { lowercaseOrders: { count: desc } }, limit: 5) { lowercaseOrders { count } } }{"errors":[{"message":"TesseractUserError: Cannot resolve: LowercaseOrders","locations":[{"line":1,"column":3}],"path":["cube"]}],"data":null,"extensions":{}}{ cube(orderBy: { uppercaseOrders: { count: desc } }, limit: 5) { uppercaseOrders { count } } }UppercaseOrdersand GraphQL only exposes it as the lowercase-first fielduppercaseOrders.orderByfrom either query (keepwhere/limitonly) and note both succeed regardless of casing — onlyorderByis affected.Expected behavior
The
orderBy: { <cube_name>: { <measure_or_dimension>: asc | desc } }argument should order results by the given field regardless of what case the cube'sname:starts with, the same way it does under the legacy planner (CUBEJS_TESSERACT_SQL_PLANNER=false), which handles both of the schemas below correctly.Minimally reproducible Cube Schema
Version
cubejs/cube:latest, resolved tov1.7.30CUBEJS_TESSERACT_SQL_PLANNER=false, consistent with the legacy planner's cube-name resolution tolerating the mismatched capitalization)CUBEJS_DB_TYPE=mssql); noted since Tesseract engine generating Postgresql dialect queries for MSSQL DB #9567 shows other Tesseract/MSSQL-specific issues, but this bug is not MSSQL-specific — the root cause is in the shared GraphQL query-building layer (getJsonQuery), not the planner or SQL generation, and it also reproduces with the synthetic inline-SQL cube above.Additional context
orderBy, older engine, different root cause per PR Fix graphql non capital cube name issue #5680) and Tesseract engine generating Postgresql dialect queries for MSSQL DB #9567 (Tesseract generating the wrong SQL dialect for MSSQL) — flagging both since they touch the sameorderBy/Tesseract/MSSQL surface area, though neither matches this exact bug.orderBysyntax (cube { someCube(orderBy: {...}) { ... } }) already has the correct guarded capitalization check in the same file (not independently verified against a live server here, but should be a viable workaround based on the code) in place of the rootcube(orderBy: {...})form.orderBywho hasn't explicitly opted into the (deprecated) legacy planner.