@cubejs-backend/dremio-driver 1.7.45 → 1.7.47
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/driver/DremioQuery.js +6 -1
- package/package.json +7 -7
- package/test/unit/DremioQueryTemplates.test.ts +86 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,16 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [1.7.47](https://github.com/cube-js/cube/compare/v1.7.46...v1.7.47) (2026-09-29)
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
- **pinot-driver,dremio-driver,druid-driver:** interpret LIKE wildcard escaping ([#11811](https://github.com/cube-js/cube/issues/11811)) ([b608adc](https://github.com/cube-js/cube/commit/b608adcb64d2eb608809c32e472276a96e2af7ad))
|
|
11
|
+
|
|
12
|
+
## [1.7.46](https://github.com/cube-js/cube/compare/v1.7.45...v1.7.46) (2026-09-26)
|
|
13
|
+
|
|
14
|
+
**Note:** Version bump only for package @cubejs-backend/dremio-driver
|
|
15
|
+
|
|
6
16
|
## [1.7.45](https://github.com/cube-js/cube/compare/v1.7.44...v1.7.45) (2026-09-25)
|
|
7
17
|
|
|
8
18
|
**Note:** Version bump only for package @cubejs-backend/dremio-driver
|
package/driver/DremioQuery.js
CHANGED
|
@@ -13,10 +13,13 @@ const GRANULARITY_TO_INTERVAL = {
|
|
|
13
13
|
};
|
|
14
14
|
|
|
15
15
|
class DremioFilter extends BaseFilter {
|
|
16
|
+
// Dremio's `ILIKE(expr, pattern)` is a function and takes no escape argument,
|
|
17
|
+
// so case folding goes on LOWER(...) and matching on LIKE, which takes one.
|
|
18
|
+
// Dremio has no default escape character, so the clause is not optional.
|
|
16
19
|
likeIgnoreCase(column, not, param, type) {
|
|
17
20
|
const p = (!type || type === 'contains' || type === 'ends') ? '%' : '';
|
|
18
21
|
const s = (!type || type === 'contains' || type === 'starts') ? '%' : '';
|
|
19
|
-
return `
|
|
22
|
+
return `LOWER(${column})${not ? ' NOT' : ''} LIKE LOWER(CONCAT('${p}', ${this.allocateParam(param)}, '${s}')) ESCAPE '\\'`;
|
|
20
23
|
}
|
|
21
24
|
|
|
22
25
|
castParameter() {
|
|
@@ -167,6 +170,8 @@ class DremioQuery extends BaseQuery {
|
|
|
167
170
|
templates.expressions.interval_single_date_part = 'CAST({{ num }} as INTERVAL {{ date_part }})';
|
|
168
171
|
templates.expressions.like = '{{ expr }} {% if negated %}NOT {% endif %}LIKE {{ pattern }}{% if default_escape %} ESCAPE \'\\\'{% endif %}';
|
|
169
172
|
delete templates.expressions.ilike;
|
|
173
|
+
// No infix ILIKE and no default escape char - see DremioFilter.likeIgnoreCase.
|
|
174
|
+
templates.tesseract.ilike = 'LOWER({{ expr }}) {% if negated %}NOT {% endif %}LIKE LOWER({{ pattern }}) ESCAPE \'\\\'';
|
|
170
175
|
delete templates.functions.WIDTH_BUCKET;
|
|
171
176
|
templates.quotes.identifiers = '"';
|
|
172
177
|
return templates;
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@cubejs-backend/dremio-driver",
|
|
3
3
|
"description": "Cube Dremio driver",
|
|
4
4
|
"author": "Cube Dev, Inc.",
|
|
5
|
-
"version": "1.7.
|
|
5
|
+
"version": "1.7.47",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
8
|
"url": "https://github.com/cube-js/cube.git",
|
|
@@ -21,14 +21,14 @@
|
|
|
21
21
|
"integration:dremio": "NODE_OPTIONS=--experimental-vm-modules jest --verbose dist/test"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@cubejs-backend/base-driver": "1.7.
|
|
25
|
-
"@cubejs-backend/schema-compiler": "1.7.
|
|
26
|
-
"@cubejs-backend/shared": "1.7.
|
|
24
|
+
"@cubejs-backend/base-driver": "1.7.47",
|
|
25
|
+
"@cubejs-backend/schema-compiler": "1.7.47",
|
|
26
|
+
"@cubejs-backend/shared": "1.7.47",
|
|
27
27
|
"axios": "^1.8.3"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
|
-
"@cubejs-backend/linter": "1.7.
|
|
31
|
-
"@cubejs-backend/testing-shared": "1.7.
|
|
30
|
+
"@cubejs-backend/linter": "1.7.47",
|
|
31
|
+
"@cubejs-backend/testing-shared": "1.7.47",
|
|
32
32
|
"@types/jest": "^29",
|
|
33
33
|
"@types/node": "^22",
|
|
34
34
|
"jest": "^29"
|
|
@@ -37,5 +37,5 @@
|
|
|
37
37
|
"publishConfig": {
|
|
38
38
|
"access": "public"
|
|
39
39
|
},
|
|
40
|
-
"gitHead": "
|
|
40
|
+
"gitHead": "0708ffddcb82f8be56acf65cde4e68b9670042a3"
|
|
41
41
|
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { prepareCompiler as originalPrepareCompiler } from '@cubejs-backend/schema-compiler';
|
|
2
|
+
|
|
3
|
+
const DremioQuery = require('../../../driver/DremioQuery');
|
|
4
|
+
|
|
5
|
+
const prepareCompiler = (content: string) => originalPrepareCompiler({
|
|
6
|
+
localPath: () => __dirname,
|
|
7
|
+
dataSchemaFiles: () => Promise.resolve([{ fileName: 'main.js', content }]),
|
|
8
|
+
}, { adapter: 'postgres' });
|
|
9
|
+
|
|
10
|
+
const MODEL = `
|
|
11
|
+
cube('orders', {
|
|
12
|
+
sql_table: 'orders',
|
|
13
|
+
|
|
14
|
+
measures: {
|
|
15
|
+
count: {
|
|
16
|
+
type: 'count',
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
|
|
20
|
+
dimensions: {
|
|
21
|
+
id: {
|
|
22
|
+
sql: 'id',
|
|
23
|
+
type: 'number',
|
|
24
|
+
primary_key: true,
|
|
25
|
+
},
|
|
26
|
+
status: {
|
|
27
|
+
sql: 'status',
|
|
28
|
+
type: 'string',
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
`;
|
|
33
|
+
|
|
34
|
+
const buildFilter = async (operator: string) => {
|
|
35
|
+
const { compiler, joinGraph, cubeEvaluator } = prepareCompiler(MODEL);
|
|
36
|
+
|
|
37
|
+
await compiler.compile();
|
|
38
|
+
|
|
39
|
+
const query = new DremioQuery({ joinGraph, cubeEvaluator, compiler }, {
|
|
40
|
+
measures: ['orders.count'],
|
|
41
|
+
filters: [{ member: 'orders.status', operator, values: ['%'] }],
|
|
42
|
+
useNativeSqlPlanner: true,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const [sql, params] = query.buildSqlAndParams();
|
|
46
|
+
|
|
47
|
+
return { sql: sql.replace(/\s+/g, ' '), params };
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// Dremio has no default LIKE escape character, so escaping the value is only
|
|
51
|
+
// meaningful if the clause that interprets it is attached to the predicate -
|
|
52
|
+
// which is why these pin the whole predicate.
|
|
53
|
+
/* eslint-disable quotes -- double quotes keep the expected SQL readable */
|
|
54
|
+
const PREDICATES: [string, string][] = [
|
|
55
|
+
['contains', "LOWER(\"orders\".status) LIKE LOWER('%' || ?|| '%') ESCAPE '\\'"],
|
|
56
|
+
['notContains', "LOWER(\"orders\".status) NOT LIKE LOWER('%' || ?|| '%') ESCAPE '\\'"],
|
|
57
|
+
['startsWith', "LOWER(\"orders\".status) LIKE LOWER(?|| '%') ESCAPE '\\'"],
|
|
58
|
+
['endsWith', "LOWER(\"orders\".status) LIKE LOWER('%' || ?) ESCAPE '\\'"],
|
|
59
|
+
];
|
|
60
|
+
/* eslint-enable quotes */
|
|
61
|
+
|
|
62
|
+
// Building a query loads the native planner, which on a cold run - the unit job
|
|
63
|
+
// starts one right after installing - takes longer than jest's default budget.
|
|
64
|
+
const COLD_START_TIMEOUT = 60 * 1000;
|
|
65
|
+
|
|
66
|
+
describe('DremioQuery SQL templates', () => {
|
|
67
|
+
it.each(PREDICATES)(
|
|
68
|
+
'escapes and interprets LIKE wildcards for %s',
|
|
69
|
+
async (operator, predicate) => {
|
|
70
|
+
const { sql, params } = await buildFilter(operator);
|
|
71
|
+
|
|
72
|
+
expect(params).toEqual(['\\%']);
|
|
73
|
+
expect(sql).toContain(predicate);
|
|
74
|
+
},
|
|
75
|
+
COLD_START_TIMEOUT
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
// Dremio's ILIKE is a function taking no escape argument, so it cannot be used
|
|
79
|
+
// and still say how the value was escaped. The predicates above pin the
|
|
80
|
+
// replacement; this pins the operator staying gone.
|
|
81
|
+
it('does not render ILIKE', async () => {
|
|
82
|
+
const { sql } = await buildFilter('contains');
|
|
83
|
+
|
|
84
|
+
expect(sql).not.toMatch(/ILIKE/i);
|
|
85
|
+
}, COLD_START_TIMEOUT);
|
|
86
|
+
});
|