Skip to content

Commit 30d4a30

Browse files
committed
feat: Hive dialect for simple queries
1 parent 092234f commit 30d4a30

File tree

5 files changed

+110
-8
lines changed

5 files changed

+110
-8
lines changed

packages/cubejs-hive-driver/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Pure Javascript Thrift HiveServer 2 driver.
88

99
1. Download Hive Thrift definition for your version from https://github.com/apache/hive/blob/master/service-rpc/if/TCLIService.thrift.
1010
2. Install Apache Thrift on your machine.
11-
3. Run `thrift --gen js:node -o HIVE_<VERSION>`.
11+
3. Run `$ thrift --gen js:node -o HIVE_<VERSION> TCLIService.thrift`.
1212
4. Copy generated files to the idl directory of this repository.
1313

1414
### License

packages/cubejs-hive-driver/driver/HiveDriver.js

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -126,12 +126,14 @@ class HiveDriver extends BaseDriver {
126126
async tablesSchema() {
127127
const tables = await this.query(`show tables in ${this.config.dbName}`);
128128

129-
return (await Promise.all(tables.map(async table => {
130-
const columns = await this.query(`describe ${this.config.dbName}.${table.tab_name}`);
131-
return {
132-
[table.tab_name]: columns.map(c => ({ name: c.col_name, type: c.data_type }))
133-
};
134-
}))).reduce((a, b) => ({ ...a, ...b }), {});
129+
return {
130+
[this.config.dbName]: (await Promise.all(tables.map(async table => {
131+
const columns = await this.query(`describe ${this.config.dbName}.${table.tab_name}`);
132+
return {
133+
[table.tab_name]: columns.map(c => ({ name: c.col_name, type: c.data_type }))
134+
};
135+
}))).reduce((a, b) => ({ ...a, ...b }), {})
136+
};
135137
}
136138

137139
async release() {

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1062,7 +1062,10 @@ class BaseQuery {
10621062
if (((this.evaluateSymbolContext || {}).renderedReference || {})[measurePath]) {
10631063
return this.evaluateSymbolContext.renderedReference[measurePath];
10641064
}
1065-
if (this.safeEvaluateSymbolContext().ungrouped) {
1065+
if (
1066+
this.safeEvaluateSymbolContext().ungrouped ||
1067+
this.safeEvaluateSymbolContext().ungroupedForWrappingGroupBy
1068+
) {
10661069
return evaluateSql;
10671070
}
10681071
if ((this.safeEvaluateSymbolContext().ungroupedAliases || {})[measurePath]) {
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
const R = require('ramda');
2+
3+
const BaseQuery = require('./BaseQuery');
4+
const BaseFilter = require('./BaseFilter');
5+
6+
const GRANULARITY_TO_INTERVAL = {
7+
date: (date) => `DATE_FORMAT(${date}, 'yyyy-MM-dd 00:00:00.000')`,
8+
week: (date) => `DATE_FORMAT(from_unixtime(unix_timestamp('1900-01-01 00:00:00') + floor((unix_timestamp(${date}) - unix_timestamp('1900-01-01 00:00:00')) / (60 * 60 * 24 * 7)) * (60 * 60 * 24 * 7)), 'yyyy-MM-dd 00:00:00.000')`,
9+
hour: (date) => `DATE_FORMAT(${date}, 'yyyy-MM-dd HH:00:00.000')`,
10+
month: (date) => `DATE_FORMAT(${date}, 'yyyy-MM-01 00:00:00.000')`,
11+
year: (date) => `DATE_FORMAT(${date}, 'yyyy-01-01 00:00:00.000')`
12+
};
13+
14+
class HiveFilter extends BaseFilter {
15+
likeIgnoreCase(column, not) {
16+
return `${column}${not ? ' NOT' : ''} LIKE CONCAT('%', ?, '%')`;
17+
}
18+
}
19+
20+
class HiveQuery extends BaseQuery {
21+
newFilter(filter) {
22+
return new HiveFilter(this, filter);
23+
}
24+
25+
convertTz(field) {
26+
return `from_utc_timestamp(${field}, '${this.timezone}')`;
27+
}
28+
29+
timeStampCast(value) {
30+
return `from_utc_timestamp(replace(replace(${value}, 'T', ' '), 'Z', ''), 'UTC')`;
31+
}
32+
33+
dateTimeCast(value) {
34+
return `from_utc_timestamp(${value}, 'UTC')`; // TODO
35+
}
36+
37+
// subtractInterval(date, interval) {
38+
// return `DATE_SUB(${date}, INTERVAL ${interval})`; // TODO
39+
// }
40+
41+
// addInterval(date, interval) {
42+
// return `DATE_ADD(${date}, INTERVAL ${interval})`; // TODO
43+
// }
44+
45+
timeGroupedColumn(granularity, dimension) {
46+
return GRANULARITY_TO_INTERVAL[granularity](dimension);
47+
}
48+
49+
escapeColumnName(name) {
50+
return `\`${name}\``;
51+
}
52+
53+
simpleQuery() {
54+
const ungrouped = this.evaluateSymbolSqlWithContext(
55+
() => `${this.commonQuery()} ${this.baseWhere(this.allFilters)}`, {
56+
ungroupedForWrappingGroupBy: true
57+
}
58+
);
59+
const select = this.evaluateSymbolSqlWithContext(
60+
() => this.dimensionsForSelect().map(
61+
d => d.aliasName()
62+
).concat(this.measures.map(m => m.selectColumns())).filter(s => !!s), {
63+
ungroupedAliases: R.fromPairs(this.forSelect().map(m => [m.measure || m.dimension, m.aliasName()]))
64+
}
65+
);
66+
return `SELECT ${select} FROM (${ungrouped}) AS ${this.escapeColumnName('hive_wrapper')}
67+
${this.groupByClause()}${this.baseHaving(this.measureFilters)}${this.orderBy()}${this.groupByDimensionLimit()}`;
68+
}
69+
70+
seriesSql(timeDimension) {
71+
const values = timeDimension.timeSeries().map(
72+
([from, to]) => `select '${from}' f, '${to}' t`
73+
).join(' UNION ALL ');
74+
return `SELECT ${this.timeStampCast('dates.f')} date_from, ${this.timeStampCast('dates.t')} date_to FROM (${values}) AS dates`;
75+
}
76+
77+
groupByClause() {
78+
const dimensionsForSelect = this.dimensionsForSelect();
79+
const dimensionColumns =
80+
R.flatten(dimensionsForSelect.map(
81+
s => s.selectColumns() && s.aliasName()
82+
)).filter(s => !!s);
83+
return dimensionColumns.length ? ` GROUP BY ${dimensionColumns.join(', ')}` : '';
84+
}
85+
86+
getFieldIndex(id) {
87+
const dimension = this.dimensionsForSelect().find(d => d.dimension === id);
88+
if (dimension) {
89+
return super.getFieldIndex(id);
90+
}
91+
return this.escapeColumnName(this.aliasName(id));
92+
}
93+
}
94+
95+
module.exports = HiveQuery;

packages/cubejs-schema-compiler/adapter/QueryBuilder.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ const prestodb = require('./PrestodbQuery');
88
const vertica = require('./VerticaQuery');
99
const snowflake = require('./SnowflakeQuery');
1010
const clickhouse = require('./ClickHouseQuery');
11+
const hive = require('./HiveQuery');
1112

1213
const ADAPTERS = {
1314
postgres,
@@ -22,6 +23,7 @@ const ADAPTERS = {
2223
vertica,
2324
snowflake,
2425
clickhouse,
26+
hive,
2527
};
2628
exports.query = (compilers, dbType, queryOptions) => {
2729
if (!ADAPTERS[dbType]) {

0 commit comments

Comments
 (0)