Skip to content

Commit 91115d7

Browse files
authored
fix(schema-compiler): Fix incorrect backAlias members collection for FILTER_PARAMS members (#9919)
* fix(schema-compiler): Fix incorrect backAlias members collection for FILTER_PARAMS members * add tests * more tests
1 parent 9a5597b commit 91115d7

File tree

2 files changed

+171
-2
lines changed

2 files changed

+171
-2
lines changed

packages/cubejs-schema-compiler/src/adapter/BaseQuery.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4977,8 +4977,6 @@ export class BaseQuery {
49774977
backAliasMembers(members) {
49784978
const query = this;
49794979

4980-
const buildJoinPath = this.buildJoinPathFn();
4981-
49824980
const aliases = Object.fromEntries(members.flatMap(
49834981
member => {
49844982
const collectedMembers = query.evaluateSymbolSqlWithContext(
@@ -4998,6 +4996,15 @@ export class BaseQuery {
49984996
}
49994997
));
50004998

4999+
// No join/graph might be in place when collecting members from the query with some injected filters,
5000+
// like FILTER_PARAMS or securityContext...
5001+
// So we simply return aliases as is
5002+
if (!this.join || !this.joinGraphPaths) {
5003+
return aliases;
5004+
}
5005+
5006+
const buildJoinPath = this.buildJoinPathFn();
5007+
50015008
/**
50025009
* @type {Record<string, string>}
50035010
*/

packages/cubejs-schema-compiler/test/unit/base-query.test.ts

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2033,6 +2033,168 @@ describe('SQL Generation', () => {
20332033
FROM
20342034
(select * from order where (type = ?)) AS "order" WHERE ("order".type = ?) AND ("order".category = ?)`);
20352035
});
2036+
2037+
it('view referencing cube with FILTER_PARAMS - multiple filters and complex query', async () => {
2038+
/** @type {Compilers} */
2039+
const viewCompiler = prepareYamlCompiler(
2040+
createSchemaYaml({
2041+
cubes: [{
2042+
name: 'Product',
2043+
sql: 'select * from products where {FILTER_PARAMS.Product.category.filter(\'category\')} and {FILTER_PARAMS.Product.status.filter(\'status\')}',
2044+
measures: [
2045+
{
2046+
name: 'count',
2047+
type: 'count',
2048+
},
2049+
{
2050+
name: 'revenue',
2051+
sql: 'price',
2052+
type: 'sum',
2053+
}
2054+
],
2055+
dimensions: [
2056+
{
2057+
name: 'category',
2058+
sql: 'category',
2059+
type: 'string'
2060+
},
2061+
{
2062+
name: 'status',
2063+
sql: 'status',
2064+
type: 'string'
2065+
},
2066+
{
2067+
name: 'name',
2068+
sql: 'name',
2069+
type: 'string'
2070+
}
2071+
]
2072+
}],
2073+
views: [{
2074+
name: 'product_analytics',
2075+
cubes: [{
2076+
join_path: 'Product',
2077+
prefix: true,
2078+
includes: [
2079+
'category',
2080+
'status',
2081+
'name',
2082+
'count',
2083+
'revenue'
2084+
]
2085+
}]
2086+
}]
2087+
})
2088+
);
2089+
2090+
await viewCompiler.compiler.compile();
2091+
const query = new PostgresQuery(viewCompiler, {
2092+
measures: ['product_analytics.Product_count', 'product_analytics.Product_revenue'],
2093+
dimensions: ['product_analytics.Product_name'],
2094+
filters: [
2095+
{
2096+
member: 'product_analytics.Product_category',
2097+
operator: 'equals',
2098+
values: ['electronics'],
2099+
},
2100+
{
2101+
member: 'product_analytics.Product_status',
2102+
operator: 'equals',
2103+
values: ['active'],
2104+
},
2105+
],
2106+
});
2107+
const queryAndParams = query.buildSqlAndParams();
2108+
const queryString = queryAndParams[0];
2109+
2110+
expect(queryString).toContain('select * from products where (category = $1) and (status = $2)');
2111+
expect(queryString).toMatch(/SELECT\s+"product"\.name/);
2112+
expect(queryString).toMatch(/count\(\*\)/);
2113+
expect(queryString).toMatch(/sum\("product"\.price\)/);
2114+
expect(queryString).toContain('WHERE ("product".category = $3) AND ("product".status = $4)');
2115+
expect(queryAndParams[1]).toEqual(['electronics', 'active', 'electronics', 'active']);
2116+
});
2117+
2118+
it('cube with FILTER_PARAMS in measure filters - triggers backAlias collection', async () => {
2119+
/** @type {Compilers} */
2120+
const filterParamsCompiler = prepareYamlCompiler(
2121+
createSchemaYaml({
2122+
cubes: [{
2123+
name: 'Sales',
2124+
sql: 'select * from sales',
2125+
measures: [
2126+
{
2127+
name: 'count',
2128+
type: 'count',
2129+
},
2130+
{
2131+
name: 'filtered_revenue',
2132+
sql: 'amount',
2133+
type: 'sum',
2134+
// This measure filter with FILTER_PARAMS should trigger backAlias collection
2135+
// when evaluating symbols
2136+
filters: [
2137+
{ sql: '{FILTER_PARAMS.Sales.category.filter(\'category\')}' }
2138+
]
2139+
}
2140+
],
2141+
dimensions: [
2142+
{
2143+
name: 'id',
2144+
sql: 'id',
2145+
type: 'number',
2146+
primaryKey: true
2147+
},
2148+
{
2149+
name: 'category',
2150+
sql: 'category',
2151+
type: 'string'
2152+
},
2153+
{
2154+
name: 'region',
2155+
sql: 'region',
2156+
type: 'string'
2157+
}
2158+
]
2159+
}],
2160+
views: [{
2161+
name: 'sales_analytics',
2162+
cubes: [{
2163+
join_path: 'Sales',
2164+
prefix: true,
2165+
includes: [
2166+
'count',
2167+
'filtered_revenue',
2168+
'category',
2169+
'region'
2170+
]
2171+
}]
2172+
}]
2173+
})
2174+
);
2175+
2176+
await filterParamsCompiler.compiler.compile();
2177+
2178+
const query = new PostgresQuery(filterParamsCompiler, {
2179+
measures: ['sales_analytics.Sales_filtered_revenue'],
2180+
dimensions: ['sales_analytics.Sales_region'],
2181+
filters: [
2182+
{
2183+
member: 'sales_analytics.Sales_category',
2184+
operator: 'equals',
2185+
values: ['electronics'],
2186+
},
2187+
],
2188+
});
2189+
2190+
const queryAndParams = query.buildSqlAndParams();
2191+
const queryString = queryAndParams[0];
2192+
2193+
expect(queryString).toContain('CASE WHEN (((category = $1)))');
2194+
expect(queryString).toMatch(/sum.*CASE WHEN/);
2195+
expect(queryString).toContain('WHERE ("sales".category = $2)');
2196+
expect(queryAndParams[1]).toEqual(['electronics', 'electronics']);
2197+
});
20362198
});
20372199

20382200
describe('FILTER_GROUP', () => {

0 commit comments

Comments
 (0)