Skip to content

Commit

Permalink
feat: Hive dialect for simple queries
Browse files Browse the repository at this point in the history
  • Loading branch information
paveltiunov committed Jun 16, 2019
1 parent 092234f commit 30d4a30
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 8 deletions.
2 changes: 1 addition & 1 deletion packages/cubejs-hive-driver/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Pure Javascript Thrift HiveServer 2 driver.

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

### License
Expand Down
14 changes: 8 additions & 6 deletions packages/cubejs-hive-driver/driver/HiveDriver.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,12 +126,14 @@ class HiveDriver extends BaseDriver {
async tablesSchema() {
const tables = await this.query(`show tables in ${this.config.dbName}`);

return (await Promise.all(tables.map(async table => {
const columns = await this.query(`describe ${this.config.dbName}.${table.tab_name}`);
return {
[table.tab_name]: columns.map(c => ({ name: c.col_name, type: c.data_type }))
};
}))).reduce((a, b) => ({ ...a, ...b }), {});
return {
[this.config.dbName]: (await Promise.all(tables.map(async table => {
const columns = await this.query(`describe ${this.config.dbName}.${table.tab_name}`);
return {
[table.tab_name]: columns.map(c => ({ name: c.col_name, type: c.data_type }))
};
}))).reduce((a, b) => ({ ...a, ...b }), {})
};
}

async release() {
Expand Down
5 changes: 4 additions & 1 deletion packages/cubejs-schema-compiler/adapter/BaseQuery.js
Original file line number Diff line number Diff line change
Expand Up @@ -1062,7 +1062,10 @@ class BaseQuery {
if (((this.evaluateSymbolContext || {}).renderedReference || {})[measurePath]) {
return this.evaluateSymbolContext.renderedReference[measurePath];
}
if (this.safeEvaluateSymbolContext().ungrouped) {
if (
this.safeEvaluateSymbolContext().ungrouped ||
this.safeEvaluateSymbolContext().ungroupedForWrappingGroupBy
) {
return evaluateSql;
}
if ((this.safeEvaluateSymbolContext().ungroupedAliases || {})[measurePath]) {
Expand Down
95 changes: 95 additions & 0 deletions packages/cubejs-schema-compiler/adapter/HiveQuery.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
const R = require('ramda');

const BaseQuery = require('./BaseQuery');
const BaseFilter = require('./BaseFilter');

const GRANULARITY_TO_INTERVAL = {
date: (date) => `DATE_FORMAT(${date}, 'yyyy-MM-dd 00:00:00.000')`,
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')`,
hour: (date) => `DATE_FORMAT(${date}, 'yyyy-MM-dd HH:00:00.000')`,
month: (date) => `DATE_FORMAT(${date}, 'yyyy-MM-01 00:00:00.000')`,
year: (date) => `DATE_FORMAT(${date}, 'yyyy-01-01 00:00:00.000')`
};

class HiveFilter extends BaseFilter {
likeIgnoreCase(column, not) {
return `${column}${not ? ' NOT' : ''} LIKE CONCAT('%', ?, '%')`;
}
}

class HiveQuery extends BaseQuery {
newFilter(filter) {
return new HiveFilter(this, filter);
}

convertTz(field) {
return `from_utc_timestamp(${field}, '${this.timezone}')`;
}

timeStampCast(value) {
return `from_utc_timestamp(replace(replace(${value}, 'T', ' '), 'Z', ''), 'UTC')`;
}

dateTimeCast(value) {
return `from_utc_timestamp(${value}, 'UTC')`; // TODO
}

// subtractInterval(date, interval) {
// return `DATE_SUB(${date}, INTERVAL ${interval})`; // TODO
// }

// addInterval(date, interval) {
// return `DATE_ADD(${date}, INTERVAL ${interval})`; // TODO
// }

timeGroupedColumn(granularity, dimension) {
return GRANULARITY_TO_INTERVAL[granularity](dimension);
}

escapeColumnName(name) {
return `\`${name}\``;
}

simpleQuery() {
const ungrouped = this.evaluateSymbolSqlWithContext(
() => `${this.commonQuery()} ${this.baseWhere(this.allFilters)}`, {
ungroupedForWrappingGroupBy: true
}
);
const select = this.evaluateSymbolSqlWithContext(
() => this.dimensionsForSelect().map(
d => d.aliasName()
).concat(this.measures.map(m => m.selectColumns())).filter(s => !!s), {
ungroupedAliases: R.fromPairs(this.forSelect().map(m => [m.measure || m.dimension, m.aliasName()]))
}
);
return `SELECT ${select} FROM (${ungrouped}) AS ${this.escapeColumnName('hive_wrapper')}
${this.groupByClause()}${this.baseHaving(this.measureFilters)}${this.orderBy()}${this.groupByDimensionLimit()}`;
}

seriesSql(timeDimension) {
const values = timeDimension.timeSeries().map(
([from, to]) => `select '${from}' f, '${to}' t`
).join(' UNION ALL ');
return `SELECT ${this.timeStampCast('dates.f')} date_from, ${this.timeStampCast('dates.t')} date_to FROM (${values}) AS dates`;
}

groupByClause() {
const dimensionsForSelect = this.dimensionsForSelect();
const dimensionColumns =
R.flatten(dimensionsForSelect.map(
s => s.selectColumns() && s.aliasName()
)).filter(s => !!s);
return dimensionColumns.length ? ` GROUP BY ${dimensionColumns.join(', ')}` : '';
}

getFieldIndex(id) {
const dimension = this.dimensionsForSelect().find(d => d.dimension === id);
if (dimension) {
return super.getFieldIndex(id);
}
return this.escapeColumnName(this.aliasName(id));
}
}

module.exports = HiveQuery;
2 changes: 2 additions & 0 deletions packages/cubejs-schema-compiler/adapter/QueryBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const prestodb = require('./PrestodbQuery');
const vertica = require('./VerticaQuery');
const snowflake = require('./SnowflakeQuery');
const clickhouse = require('./ClickHouseQuery');
const hive = require('./HiveQuery');

const ADAPTERS = {
postgres,
Expand All @@ -22,6 +23,7 @@ const ADAPTERS = {
vertica,
snowflake,
clickhouse,
hive,
};
exports.query = (compilers, dbType, queryOptions) => {
if (!ADAPTERS[dbType]) {
Expand Down

0 comments on commit 30d4a30

Please sign in to comment.