@cubejs-backend/dremio-driver 1.7.46 → 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 CHANGED
@@ -3,6 +3,12 @@
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
+
6
12
  ## [1.7.46](https://github.com/cube-js/cube/compare/v1.7.45...v1.7.46) (2026-09-26)
7
13
 
8
14
  **Note:** Version bump only for package @cubejs-backend/dremio-driver
@@ -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 ` ILIKE (${column}${not ? ' NOT' : ''}, CONCAT('${p}', ${this.allocateParam(param)}, '${s}'))`;
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.46",
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.46",
25
- "@cubejs-backend/schema-compiler": "1.7.46",
26
- "@cubejs-backend/shared": "1.7.46",
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.46",
31
- "@cubejs-backend/testing-shared": "1.7.46",
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": "727e8b0ef3dd7f5b024f9d0b62b1c720539c0aa5"
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
+ });