Web console: update DQT to version 0.17 (#13323)

* update to DQT 17

* update licenses

* after npm i
This commit is contained in:
Vadim Ogievetsky 2022-11-07 17:47:11 -08:00 committed by GitHub
parent d1a4de022a
commit f6aca21e82
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 322 additions and 420 deletions

View File

@ -5683,7 +5683,7 @@ license_category: binary
module: web-console
license_name: Apache License version 2.0
copyright: Imply Data
version: 0.16.1
version: 0.17.1
---

View File

@ -16,7 +16,7 @@
* limitations under the License.
*/
import { SqlTableRef } from 'druid-query-toolkit';
import { T } from 'druid-query-toolkit';
import * as playwright from 'playwright-chromium';
import { DatasourcesOverview } from './component/datasources/overview';
@ -167,7 +167,7 @@ async function validateDatasourceStatus(page: playwright.Page, datasourceName: s
async function validateQuery(page: playwright.Page, datasourceName: string) {
const queryOverview = new QueryOverview(page, UNIFIED_CONSOLE_URL);
const query = `SELECT * FROM ${SqlTableRef.create(datasourceName)} ORDER BY __time`;
const query = `SELECT * FROM ${T(datasourceName)} ORDER BY __time`;
const results = await queryOverview.runQuery(query);
expect(results).toBeDefined();
expect(results.length).toBeGreaterThan(0);

View File

@ -22,7 +22,7 @@
"d3-axis": "^2.1.0",
"d3-scale": "^3.3.0",
"d3-selection": "^2.0.0",
"druid-query-toolkit": "^0.16.1",
"druid-query-toolkit": "^0.17.1",
"file-saver": "^2.0.2",
"follow-redirects": "^1.14.7",
"fontsource-open-sans": "^3.0.9",
@ -8967,9 +8967,9 @@
}
},
"node_modules/druid-query-toolkit": {
"version": "0.16.1",
"resolved": "https://registry.npmjs.org/druid-query-toolkit/-/druid-query-toolkit-0.16.1.tgz",
"integrity": "sha512-HiovpGh1m2QTvexBX1AvQAz5ONc9sChazL0hsXl0qEvHcIQ69pksJZWXZOStBI2T9fJAXQpSUVB4DUSdE6UXmQ==",
"version": "0.17.1",
"resolved": "https://registry.npmjs.org/druid-query-toolkit/-/druid-query-toolkit-0.17.1.tgz",
"integrity": "sha512-uXybPbUgY5fKMRSduPKSTLHQ8zhfvGT2/jWl+fPIXruFjFnZTDp8YPkJtYq1LzSCvEfgKLw1vwBtNhyUY/CdVg==",
"dependencies": {
"tslib": "^2.3.1"
},
@ -34591,9 +34591,9 @@
}
},
"druid-query-toolkit": {
"version": "0.16.1",
"resolved": "https://registry.npmjs.org/druid-query-toolkit/-/druid-query-toolkit-0.16.1.tgz",
"integrity": "sha512-HiovpGh1m2QTvexBX1AvQAz5ONc9sChazL0hsXl0qEvHcIQ69pksJZWXZOStBI2T9fJAXQpSUVB4DUSdE6UXmQ==",
"version": "0.17.1",
"resolved": "https://registry.npmjs.org/druid-query-toolkit/-/druid-query-toolkit-0.17.1.tgz",
"integrity": "sha512-uXybPbUgY5fKMRSduPKSTLHQ8zhfvGT2/jWl+fPIXruFjFnZTDp8YPkJtYq1LzSCvEfgKLw1vwBtNhyUY/CdVg==",
"requires": {
"tslib": "^2.3.1"
}

View File

@ -79,7 +79,7 @@
"d3-axis": "^2.1.0",
"d3-scale": "^3.3.0",
"d3-selection": "^2.0.0",
"druid-query-toolkit": "^0.16.1",
"druid-query-toolkit": "^0.17.1",
"file-saver": "^2.0.2",
"follow-redirects": "^1.14.7",
"fontsource-open-sans": "^3.0.9",

View File

@ -19,13 +19,14 @@
import { Menu, MenuItem } from '@blueprintjs/core';
import { IconNames } from '@blueprintjs/icons';
import {
C,
Column,
L,
SqlComparison,
SqlExpression,
SqlLiteral,
SqlQuery,
SqlRecord,
SqlRef,
trimString,
} from 'druid-query-toolkit';
import React from 'react';
@ -36,7 +37,7 @@ function sqlLiteralForColumnValue(column: Column, value: unknown): SqlLiteral |
if (column.sqlType === 'TIMESTAMP') {
const asDate = new Date(value as any);
if (!isNaN(asDate.valueOf())) {
return SqlLiteral.create(asDate);
return L(asDate);
}
}
@ -112,14 +113,14 @@ export function CellFilterMenu(props: CellFilterMenuProps) {
let ex: SqlExpression | undefined;
let having = false;
if (query.hasStarInSelect()) {
ex = SqlRef.column(column.name);
ex = C(column.name);
} else {
const selectValue = query.getSelectExpressionForIndex(headerIndex);
if (selectValue) {
const outputName = selectValue.getOutputName();
having = query.isAggregateSelectIndex(headerIndex);
if (having && outputName) {
ex = SqlRef.column(outputName);
ex = C(outputName);
} else {
ex = selectValue.getUnderlyingExpression();
}
@ -133,11 +134,11 @@ export function CellFilterMenu(props: CellFilterMenuProps) {
icon={IconNames.FILTER}
text={`${having ? 'Having' : 'Filter on'}: ${prettyPrintSql(clause)}`}
onClick={() => {
const column = clause.getUsedColumns()[0];
const columnName = clause.getUsedColumnNames()[0];
onQueryAction(
having
? q => q.removeFromHaving(column).addHaving(clause)
: q => q.removeColumnFromWhere(column).addWhere(clause),
? q => q.removeFromHaving(columnName).addHaving(clause)
: q => q.removeColumnFromWhere(columnName).addWhere(clause),
);
}}
/>
@ -151,7 +152,7 @@ export function CellFilterMenu(props: CellFilterMenuProps) {
const currentClauses =
currentFilterExpression
?.decomposeViaAnd()
?.filter(ex => String(ex.getUsedColumns()) === column.name) || [];
?.filter(ex => String(ex.getUsedColumnNames()) === column.name) || [];
const updatedClause =
currentClauses.length === 1 && val ? addToClause(currentClauses[0], val) : undefined;
@ -160,7 +161,7 @@ export function CellFilterMenu(props: CellFilterMenuProps) {
const jsonColumn = column.nativeType === 'COMPLEX<json>';
return (
<Menu>
{ex?.getFirstColumn() && val && !jsonColumn && (
{ex?.getFirstColumnName() && val && !jsonColumn && (
<>
{updatedClause && filterOnMenuItem(updatedClause)}
{filterOnMenuItem(ex.equal(val))}
@ -177,7 +178,7 @@ export function CellFilterMenu(props: CellFilterMenuProps) {
</Menu>
);
} else {
const ref = SqlRef.column(column.name);
const ref = C(column.name);
const stringValue = stringifyValue(value);
const trimmedValue = trimString(stringValue, 50);
return (

View File

@ -66,8 +66,8 @@ export const RecordTablePane = React.memo(function RecordTablePane(props: Record
if (!parsedQuery || !parsedQuery.isRealOutputColumnAtSelectIndex(headerIndex)) return false;
return (
parsedQuery.getEffectiveWhereExpression().containsColumn(header) ||
parsedQuery.getEffectiveHavingExpression().containsColumn(header)
parsedQuery.getEffectiveWhereExpression().containsColumnName(header) ||
parsedQuery.getEffectiveHavingExpression().containsColumnName(header)
);
}

View File

@ -16,7 +16,7 @@
* limitations under the License.
*/
import { SqlLiteral } from 'druid-query-toolkit';
import { L } from 'druid-query-toolkit';
import React from 'react';
import ReactTable from 'react-table';
@ -44,7 +44,7 @@ export const DatasourceColumnsTable = React.memo(function DatasourceColumnsTable
processQuery: async (datasourceId: string) => {
return await queryDruidSql<ColumnMetadata>({
query: `SELECT COLUMN_NAME, DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = 'druid' AND TABLE_NAME = ${SqlLiteral.create(datasourceId)}`,
WHERE TABLE_SCHEMA = 'druid' AND TABLE_NAME = ${L(datasourceId)}`,
});
},
});

View File

@ -16,7 +16,7 @@
* limitations under the License.
*/
import { QueryResult, QueryRunner, SqlTableRef } from 'druid-query-toolkit';
import { QueryResult, QueryRunner, T } from 'druid-query-toolkit';
import React from 'react';
import { Loader, RecordTablePane } from '../../../components';
@ -42,7 +42,7 @@ export const DatasourcePreviewPane = React.memo(function DatasourcePreviewPane(
let result: QueryResult;
try {
result = await queryRunner.runQuery({
query: `SELECT * FROM ${SqlTableRef.create(datasource)}`,
query: `SELECT * FROM ${T(datasource)}`,
extraQueryContext: { sqlOuterLimit: 100 },
cancelToken,
});

View File

@ -16,7 +16,7 @@
* limitations under the License.
*/
import { SqlRef } from 'druid-query-toolkit';
import { N } from 'druid-query-toolkit';
import React from 'react';
import ReactTable from 'react-table';
@ -43,7 +43,7 @@ export const LookupValuesTable = React.memo(function LookupValuesTable(
const [entriesState] = useQueryManager<string, LookupRow[]>({
processQuery: async (lookupId: string) => {
return await queryDruidSql<LookupRow>({
query: `SELECT "k", "v" FROM ${SqlRef.column(lookupId, 'lookup')} LIMIT 5000`,
query: `SELECT "k", "v" FROM ${N('lookup').table(lookupId)} LIMIT 5000`,
});
},
initQuery: props.lookupId,

View File

@ -17,12 +17,14 @@
*/
import {
C,
F,
filterMap,
L,
SqlExpression,
SqlFunction,
SqlLiteral,
SqlQuery,
SqlRef,
SqlStar,
} from 'druid-query-toolkit';
import * as JSONBig from 'json-bigint-native';
@ -120,9 +122,9 @@ export function summarizeExternalConfig(externalConfig: ExternalConfig): string
export function externalConfigToTableExpression(config: ExternalConfig): SqlExpression {
return SqlExpression.parse(`TABLE(
EXTERN(
${SqlLiteral.create(JSONBig.stringify(config.inputSource))},
${SqlLiteral.create(JSONBig.stringify(config.inputFormat))},
${SqlLiteral.create(JSONBig.stringify(config.signature))}
${L(JSONBig.stringify(config.inputSource))},
${L(JSONBig.stringify(config.inputFormat))},
${L(JSONBig.stringify(config.signature))}
)
)`);
}
@ -135,11 +137,8 @@ export function externalConfigToInitDimensions(
return (timeExpression ? [timeExpression.as('__time')] : [])
.concat(
filterMap(config.signature, ({ name }, i) => {
if (timeExpression && timeExpression.containsColumn(name)) return;
return SqlRef.column(name).applyIf(
isArrays[i],
ex => SqlFunction.simple('MV_TO_ARRAY', [ex]).as(name) as any,
);
if (timeExpression && timeExpression.containsColumnName(name)) return;
return C(name).applyIf(isArrays[i], ex => F('MV_TO_ARRAY', ex).as(name) as any);
}),
)
.slice(0, MULTI_STAGE_QUERY_MAX_COLUMNS);

View File

@ -23,8 +23,8 @@ import {
SqlPartitionedByClause,
SqlQuery,
SqlReplaceClause,
SqlTableRef,
SqlWithPart,
T,
} from 'druid-query-toolkit';
import { filterMap, oneOf } from '../../utils';
@ -150,10 +150,10 @@ export function fitIngestQueryPattern(query: SqlQuery): IngestQueryPattern {
let overwriteWhere: SqlExpression | undefined;
if (query.insertClause) {
mode = 'insert';
destinationTableName = query.insertClause.table.getTable();
destinationTableName = query.insertClause.table.getName();
} else if (query.replaceClause) {
mode = 'replace';
destinationTableName = query.replaceClause.table.getTable();
destinationTableName = query.replaceClause.table.getName();
overwriteWhere = query.replaceClause.whereClause?.expression;
} else {
throw new Error(`Must have an INSERT or REPLACE clause`);
@ -251,7 +251,7 @@ export function ingestQueryPatternToQuery(
partitionedBy,
clusteredBy,
} = ingestQueryPattern;
return SqlQuery.from(SqlTableRef.create(mainExternalName))
return SqlQuery.from(T(mainExternalName))
.applyIf(!preview, q =>
mode === 'insert'
? q.changeInsertIntoTable(destinationTableName)

View File

@ -16,7 +16,7 @@
* limitations under the License.
*/
import { SqlExpression, SqlQuery, SqlTableRef, SqlValues, SqlWithQuery } from 'druid-query-toolkit';
import { SqlExpression, SqlQuery, SqlValues, SqlWithQuery, T } from 'druid-query-toolkit';
import Hjson from 'hjson';
import * as JSONBig from 'json-bigint-native';
@ -71,7 +71,7 @@ export class WorkbenchQueryPart {
);
if (!fragmentQuery) return;
return fragmentQuery.getIngestTable()?.getTable();
return fragmentQuery.getIngestTable()?.getName();
}
public readonly id: string;
@ -179,7 +179,7 @@ export class WorkbenchQueryPart {
public getIngestDatasource(): string | undefined {
const { queryString, parsedQuery } = this;
if (parsedQuery) {
return parsedQuery.getIngestTable()?.getTable();
return parsedQuery.getIngestTable()?.getName();
}
if (this.isJsonLike()) return;
@ -241,7 +241,7 @@ export class WorkbenchQueryPart {
public toWithPart(): string {
const { queryName, queryString } = this;
return `${SqlTableRef.create(queryName || 'q')} AS (\n${queryString}\n)`;
return `${T(queryName || 'q')} AS (\n${queryString}\n)`;
}
public duplicate(): WorkbenchQueryPart {

View File

@ -17,16 +17,16 @@
*/
import {
C,
F,
SqlClusteredByClause,
SqlExpression,
SqlFunction,
SqlLiteral,
SqlOrderByClause,
SqlOrderByExpression,
SqlPartitionedByClause,
SqlQuery,
SqlRef,
SqlTableRef,
SqlTable,
} from 'druid-query-toolkit';
import Hjson from 'hjson';
import * as JSONBig from 'json-bigint-native';
@ -201,10 +201,7 @@ export class WorkbenchQuery {
let partitionedByExpression = partitionedByClause.expression;
if (partitionedByExpression) {
if (partitionedByExpression instanceof SqlLiteral) {
partitionedByExpression = SqlFunction.floor(
SqlRef.column('__time'),
partitionedByExpression,
);
partitionedByExpression = F.floor(C('__time'), partitionedByExpression);
}
orderByExpressions.push(SqlOrderByExpression.create(partitionedByExpression));
}
@ -451,8 +448,8 @@ export class WorkbenchQuery {
const parsedQuery = this.getParsedQuery();
if (parsedQuery) {
const fromExpression = parsedQuery.getFirstFromExpression();
if (fromExpression instanceof SqlTableRef) {
const firstTable = fromExpression.getTable();
if (fromExpression instanceof SqlTable) {
const firstTable = fromExpression.getName();
ret = ret.changeQueryParts(
this.queryParts.map(queryPart =>
queryPart.queryName === firstTable ? queryPart.addPreviewLimit() : queryPart,

View File

@ -17,7 +17,7 @@
*/
import { AxiosResponse, CancelToken } from 'axios';
import { SqlLiteral } from 'druid-query-toolkit';
import { L } from 'druid-query-toolkit';
import { Execution, QueryContext } from '../../druid-models';
import { Api } from '../../singletons';
@ -227,7 +227,7 @@ export async function updateExecutionWithDatasourceExistsIfNeeded(
COUNT(*) AS num_segments,
COUNT(*) FILTER (WHERE is_published = 1 AND is_available = 0) AS loading_segments
FROM sys.segments
WHERE datasource = ${SqlLiteral.create(execution.destination.dataSource)} AND is_overshadowed = 0`,
WHERE datasource = ${L(execution.destination.dataSource)} AND is_overshadowed = 0`,
});
const numSegments: number = deepGet(segmentCheck, '0.num_segments') || 0;

View File

@ -16,7 +16,7 @@
* limitations under the License.
*/
import { RefName, SqlExpression, SqlLiteral, SqlRef, SqlTableRef } from 'druid-query-toolkit';
import { C, L, RefName, SqlExpression, T } from 'druid-query-toolkit';
import * as JSONBig from 'json-bigint-native';
import {
@ -38,10 +38,10 @@ export function getSpecDatasourceName(spec: IngestionSpec): string {
function convertFilter(filter: any): SqlExpression {
switch (filter.type) {
case 'selector':
return SqlRef.column(filter.dimension).equal(filter.value);
return C(filter.dimension).equal(filter.value);
case 'in':
return SqlRef.column(filter.dimension).in(filter.values);
return C(filter.dimension).in(filter.values);
case 'not':
return convertFilter(filter.field).not();
@ -57,7 +57,7 @@ function convertFilter(filter: any): SqlExpression {
}
}
const SOURCE_REF = SqlTableRef.create('source');
const SOURCE_TABLE = T('source');
export function convertSpecToSql(spec: any): QueryWithContext {
if (!oneOf(spec.type, 'index_parallel', 'index', 'index_hadoop')) {
@ -108,8 +108,8 @@ export function convertSpecToSql(spec: any): QueryWithContext {
}
let timeExpression: string;
const column = timestampSpec.column || 'timestamp';
const columnRef = SqlRef.column(column);
const timestampColumnName = timestampSpec.column || 'timestamp';
const timestampColumn = C(timestampColumnName);
const format = timestampSpec.format || 'auto';
const timeTransform = transforms.find(t => t.name === '__time');
if (timeTransform) {
@ -117,46 +117,44 @@ export function convertSpecToSql(spec: any): QueryWithContext {
} else {
switch (format) {
case 'auto':
columns.unshift({ name: column, type: 'string' });
timeExpression = `CASE WHEN CAST(${columnRef} AS BIGINT) > 0 THEN MILLIS_TO_TIMESTAMP(CAST(${columnRef} AS BIGINT)) ELSE TIME_PARSE(${columnRef}) END`;
columns.unshift({ name: timestampColumnName, type: 'string' });
timeExpression = `CASE WHEN CAST(${timestampColumn} AS BIGINT) > 0 THEN MILLIS_TO_TIMESTAMP(CAST(${timestampColumn} AS BIGINT)) ELSE TIME_PARSE(${timestampColumn}) END`;
break;
case 'iso':
columns.unshift({ name: column, type: 'string' });
timeExpression = `TIME_PARSE(${columnRef})`;
columns.unshift({ name: timestampColumnName, type: 'string' });
timeExpression = `TIME_PARSE(${timestampColumn})`;
break;
case 'posix':
columns.unshift({ name: column, type: 'long' });
timeExpression = `MILLIS_TO_TIMESTAMP(${columnRef} * 1000)`;
columns.unshift({ name: timestampColumnName, type: 'long' });
timeExpression = `MILLIS_TO_TIMESTAMP(${timestampColumn} * 1000)`;
break;
case 'millis':
columns.unshift({ name: column, type: 'long' });
timeExpression = `MILLIS_TO_TIMESTAMP(${columnRef})`;
columns.unshift({ name: timestampColumnName, type: 'long' });
timeExpression = `MILLIS_TO_TIMESTAMP(${timestampColumn})`;
break;
case 'micro':
columns.unshift({ name: column, type: 'long' });
timeExpression = `MILLIS_TO_TIMESTAMP(${columnRef} / 1000)`;
columns.unshift({ name: timestampColumnName, type: 'long' });
timeExpression = `MILLIS_TO_TIMESTAMP(${timestampColumn} / 1000)`;
break;
case 'nano':
columns.unshift({ name: column, type: 'long' });
timeExpression = `MILLIS_TO_TIMESTAMP(${columnRef} / 1000000)`;
columns.unshift({ name: timestampColumnName, type: 'long' });
timeExpression = `MILLIS_TO_TIMESTAMP(${timestampColumn} / 1000000)`;
break;
default:
columns.unshift({ name: column, type: 'string' });
timeExpression = `TIME_PARSE(${columnRef}, ${SqlLiteral.create(format)})`;
columns.unshift({ name: timestampColumnName, type: 'string' });
timeExpression = `TIME_PARSE(${timestampColumn}, ${L(format)})`;
break;
}
}
if (timestampSpec.missingValue) {
timeExpression = `COALESCE(${timeExpression}, TIME_PARSE(${SqlLiteral.create(
timestampSpec.missingValue,
)}))`;
timeExpression = `COALESCE(${timeExpression}, TIME_PARSE(${L(timestampSpec.missingValue)}))`;
}
timeExpression = convertQueryGranularity(
@ -180,17 +178,17 @@ export function convertSpecToSql(spec: any): QueryWithContext {
if (typeof dataSource !== 'string') throw new Error(`spec.dataSchema.dataSource is not a string`);
if (deepGet(spec, 'spec.ioConfig.appendToExisting')) {
lines.push(`INSERT INTO ${SqlTableRef.create(dataSource)}`);
lines.push(`INSERT INTO ${T(dataSource)}`);
} else {
const overwrite = deepGet(spec, 'spec.ioConfig.dropExisting')
? 'WHERE ' +
SqlExpression.fromTimeRefAndInterval(
SqlRef.column('__time'),
SqlExpression.fromTimeExpressionAndInterval(
C('__time'),
deepGet(spec, 'spec.dataSchema.granularitySpec.intervals'),
)
: 'ALL';
lines.push(`REPLACE INTO ${SqlTableRef.create(dataSource)} OVERWRITE ${overwrite}`);
lines.push(`REPLACE INTO ${T(dataSource)} OVERWRITE ${overwrite}`);
}
let inputSource: any;
@ -220,27 +218,24 @@ export function convertSpecToSql(spec: any): QueryWithContext {
if (inputSource.type === 'druid') {
lines.push(
`WITH ${SOURCE_REF} AS (`,
`WITH ${SOURCE_TABLE} AS (`,
` SELECT *`,
` FROM ${SqlTableRef.create(inputSource.dataSource)}`,
` WHERE ${SqlExpression.fromTimeRefAndInterval(
SqlRef.column('__time'),
inputSource.interval,
)}`,
` FROM ${T(inputSource.dataSource)}`,
` WHERE ${SqlExpression.fromTimeExpressionAndInterval(C('__time'), inputSource.interval)}`,
')',
);
} else {
lines.push(
`WITH ${SOURCE_REF} AS (SELECT * FROM TABLE(`,
`WITH ${SOURCE_TABLE} AS (SELECT * FROM TABLE(`,
` EXTERN(`,
` ${SqlLiteral.create(JSONBig.stringify(inputSource))},`,
` ${L(JSONBig.stringify(inputSource))},`,
);
const inputFormat = deepGet(spec, 'spec.ioConfig.inputFormat');
if (!inputFormat) throw new Error(`spec.ioConfig.inputFormat is not defined`);
lines.push(
` ${SqlLiteral.create(JSONBig.stringify(inputFormat))},`,
` ${SqlLiteral.create(JSONBig.stringify(columns))}`,
` ${L(JSONBig.stringify(inputFormat))},`,
` ${L(JSONBig.stringify(columns))}`,
` )`,
`))`,
);
@ -264,7 +259,7 @@ export function convertSpecToSql(spec: any): QueryWithContext {
const relevantTransform = transforms.find(t => t.name === dimensionName);
return ` ${
relevantTransform ? `REWRITE_[${relevantTransform.expression}]_TO_SQL AS ` : ''
}${SqlRef.column(dimensionName)},${
}${C(dimensionName)},${
relevantTransform ? ` --:ISSUE: Transform for dimension could not be converted` : ''
}`;
}),
@ -282,7 +277,7 @@ export function convertSpecToSql(spec: any): QueryWithContext {
.replace(/,(\s+--)/, '$1');
lines.push(selectExpressions.join('\n'));
lines.push(`FROM ${SOURCE_REF}`);
lines.push(`FROM ${SOURCE_TABLE}`);
const filter = deepGet(spec, 'spec.dataSchema.transformSpec.filter');
if (filter) {
@ -316,7 +311,7 @@ export function convertSpecToSql(spec: any): QueryWithContext {
partitionsSpec.partitionDimensions ||
(partitionsSpec.partitionDimension ? [partitionsSpec.partitionDimension] : undefined);
if (Array.isArray(partitionDimensions)) {
lines.push(`CLUSTERED BY ${partitionDimensions.map(d => SqlRef.column(d)).join(', ')}`);
lines.push(`CLUSTERED BY ${partitionDimensions.map(d => C(d)).join(', ')}`);
}
return {
@ -397,56 +392,56 @@ function metricSpecToSqlExpression(metricSpec: MetricSpec): string | undefined {
}
if (!metricSpec.fieldName) return;
const ref = SqlRef.column(metricSpec.fieldName);
const column = C(metricSpec.fieldName);
switch (metricSpec.type) {
case 'longSum':
case 'floatSum':
case 'doubleSum':
return `SUM(${ref})`;
return `SUM(${column})`;
case 'longMin':
case 'floatMin':
case 'doubleMin':
return `MIN(${ref})`;
return `MIN(${column})`;
case 'longMax':
case 'floatMax':
case 'doubleMax':
return `MAX(${ref})`;
return `MAX(${column})`;
case 'doubleFirst':
case 'floatFirst':
case 'longFirst':
return `EARLIEST(${ref})`;
return `EARLIEST(${column})`;
case 'stringFirst':
return `EARLIEST(${ref}, ${SqlLiteral.create(metricSpec.maxStringBytes || 128)})`;
return `EARLIEST(${column}, ${L(metricSpec.maxStringBytes || 128)})`;
case 'doubleLast':
case 'floatLast':
case 'longLast':
return `LATEST(${ref})`;
return `LATEST(${column})`;
case 'stringLast':
return `LATEST(${ref}, ${SqlLiteral.create(metricSpec.maxStringBytes || 128)})`;
return `LATEST(${column}, ${L(metricSpec.maxStringBytes || 128)})`;
case 'thetaSketch':
return `APPROX_COUNT_DISTINCT_DS_THETA(${ref}${extraArgs([metricSpec.size, 16384])})`;
return `APPROX_COUNT_DISTINCT_DS_THETA(${column}${extraArgs([metricSpec.size, 16384])})`;
case 'HLLSketchBuild':
case 'HLLSketchMerge':
return `APPROX_COUNT_DISTINCT_DS_HLL(${ref}${extraArgs(
return `APPROX_COUNT_DISTINCT_DS_HLL(${column}${extraArgs(
[metricSpec.lgK, 12],
[metricSpec.tgtHllType, 'HLL_4'],
)})`;
case 'quantilesDoublesSketch':
// For consistency with the above this should be APPROX_QUANTILE_DS but that requires a post agg so it does not work quite right.
return `DS_QUANTILES_SKETCH(${ref}${extraArgs([metricSpec.k, 128])})`;
return `DS_QUANTILES_SKETCH(${column}${extraArgs([metricSpec.k, 128])})`;
case 'hyperUnique':
return `APPROX_COUNT_DISTINCT_BUILTIN(${ref})`;
return `APPROX_COUNT_DISTINCT_BUILTIN(${column})`;
default:
// The following things are (knowingly) not supported:
@ -464,5 +459,5 @@ function extraArgs(...thingAndDefaults: [any, any?][]): string {
}
if (!thingAndDefaults.length) return '';
return ', ' + thingAndDefaults.map(([x, def]) => SqlLiteral.create(x ?? def)).join(', ');
return ', ' + thingAndDefaults.map(([x, def]) => L(x ?? def)).join(', ');
}

View File

@ -18,7 +18,7 @@
import { IconName } from '@blueprintjs/core';
import { IconNames } from '@blueprintjs/icons';
import { SqlExpression, SqlFunction, SqlLiteral, SqlRef } from 'druid-query-toolkit';
import { C, F, SqlExpression } from 'druid-query-toolkit';
import { Filter } from 'react-table';
import { addOrUpdate, caseInsensitiveContains } from '../utils';
@ -142,24 +142,21 @@ export function sqlQueryCustomTableFilter(filter: Filter): SqlExpression | undef
const modeAndNeedle = parseFilterModeAndNeedle(filter);
if (!modeAndNeedle) return;
const { mode, needle } = modeAndNeedle;
const column = SqlRef.column(filter.id);
const needleLiteral = SqlLiteral.create(needle);
const column = C(filter.id);
switch (mode) {
case '=':
return column.equal(needleLiteral);
return column.equal(needle);
case '!=':
return column.unequal(needleLiteral);
return column.unequal(needle);
case '<=':
return column.lessThanOrEqual(needleLiteral);
return column.lessThanOrEqual(needle);
case '>=':
return column.greaterThanOrEqual(needleLiteral);
return column.greaterThanOrEqual(needle);
default:
return SqlFunction.simple('LOWER', [column]).like(
SqlLiteral.create(`%${needle.toLowerCase()}%`),
);
return F('LOWER', column).like(`%${needle.toLowerCase()}%`);
}
}

View File

@ -17,7 +17,7 @@
*/
import axios, { AxiosResponse } from 'axios';
import { SqlRef } from 'druid-query-toolkit';
import { C } from 'druid-query-toolkit';
import { Api } from '../singletons';
@ -309,6 +309,6 @@ export interface QueryExplanation {
export function formatSignature(queryExplanation: QueryExplanation): string {
return queryExplanation.signature
.map(({ name, type }) => `${SqlRef.columnWithoutQuotes(name)}::${type}`)
.map(({ name, type }) => `${C.optionalQuotes(name)}::${type}`)
.join(', ');
}

View File

@ -16,12 +16,12 @@
* limitations under the License.
*/
import { SqlBase, SqlLiteral, SqlQuery } from 'druid-query-toolkit';
import { L, SqlBase, SqlQuery } from 'druid-query-toolkit';
export const EMPTY_LITERAL = SqlLiteral.create('');
export const EMPTY_LITERAL = L('');
const CRAZY_STRING = '$.X.@.X.$';
const DOT_DOT_DOT_LITERAL = SqlLiteral.create('...');
const DOT_DOT_DOT_LITERAL = L('...');
export function prettyPrintSql(b: SqlBase): string {
return b
@ -44,14 +44,7 @@ export interface RowColumn {
}
export function findEmptyLiteralPosition(query: SqlQuery): RowColumn | undefined {
const subQueryString = query
.walk(b => {
if (b === EMPTY_LITERAL) {
return SqlLiteral.create(CRAZY_STRING);
}
return b;
})
.toString();
const subQueryString = query.walk(b => (b === EMPTY_LITERAL ? L(CRAZY_STRING) : b)).toString();
const crazyIndex = subQueryString.indexOf(CRAZY_STRING);
if (crazyIndex < 0) return;

View File

@ -17,18 +17,18 @@
*/
import {
C,
Column,
F,
L,
LiteralValue,
QueryResult,
RefName,
SqlAlias,
SqlColumnList,
SqlExpression,
SqlFunction,
SqlLiteral,
SqlQuery,
SqlRecord,
SqlRef,
SqlValues,
} from 'druid-query-toolkit';
@ -50,13 +50,13 @@ export function sampleDataToQuery(sample: QueryResult): SqlQuery {
SqlRecord.create(
row.map((r, i) => {
if (header[i].nativeType === 'COMPLEX<json>') {
return SqlLiteral.create(JSON.stringify(r));
return L(JSON.stringify(r));
} else if (Array.isArray(r)) {
arrayIndexes[i] = true;
return SqlLiteral.create(r.join(SAMPLE_ARRAY_SEPARATOR));
return L(r.join(SAMPLE_ARRAY_SEPARATOR));
} else {
// Avoid actually using NULL literals as they create havc in the VALUES type system and throw errors.
return SqlLiteral.create(r == null ? nullForColumn(header[i]) : r);
// Avoid actually using NULL literals as they create havoc in the VALUES type system and throw errors.
return L(r == null ? nullForColumn(header[i]) : r);
}
}),
),
@ -67,11 +67,11 @@ export function sampleDataToQuery(sample: QueryResult): SqlQuery {
}),
).changeSelectExpressions(
header.map((h, i) => {
let ex: SqlExpression = SqlRef.column(`c${i}`);
let ex: SqlExpression = C(`c${i}`);
if (h.nativeType === 'COMPLEX<json>') {
ex = SqlFunction.simple('PARSE_JSON', [ex]);
ex = F('PARSE_JSON', ex);
} else if (arrayIndexes[i]) {
ex = SqlFunction.simple('STRING_TO_MV', [ex, SqlLiteral.create(SAMPLE_ARRAY_SEPARATOR)]);
ex = F('STRING_TO_MV', ex, SAMPLE_ARRAY_SEPARATOR);
} else if (h.sqlType) {
ex = ex.cast(h.sqlType);
}

View File

@ -16,7 +16,7 @@
* limitations under the License.
*/
import { SqlExpression, SqlFunction, SqlLiteral, SqlRef, SqlStar } from 'druid-query-toolkit';
import { SqlColumn, SqlExpression, SqlFunction, SqlLiteral, SqlStar } from 'druid-query-toolkit';
export function timeFormatToSql(timeFormat: string): SqlExpression | undefined {
switch (timeFormat) {
@ -54,7 +54,7 @@ export function convertToGroupByExpression(ex: SqlExpression): SqlExpression | u
if (interestingArgs.length !== 1) return;
const newEx = interestingArgs[0];
if (newEx instanceof SqlRef) return newEx;
if (newEx instanceof SqlColumn) return newEx;
return newEx.as((ex.getOutputName() || 'grouped').replace(/^[a-z]+_/i, ''));
}

View File

@ -19,7 +19,7 @@
import { FormGroup, InputGroup, Intent, MenuItem, Switch } from '@blueprintjs/core';
import { IconNames } from '@blueprintjs/icons';
import classNames from 'classnames';
import { SqlQuery, SqlTableRef } from 'druid-query-toolkit';
import { SqlQuery, T } from 'druid-query-toolkit';
import React from 'react';
import ReactTable, { Filter } from 'react-table';
@ -830,8 +830,7 @@ ORDER BY 1`;
goToActions.push({
icon: IconNames.APPLICATION,
title: 'Query with SQL',
onAction: () =>
goToQuery({ queryString: SqlQuery.create(SqlTableRef.create(datasource)).toString() }),
onAction: () => goToQuery({ queryString: SqlQuery.create(T(datasource)).toString() }),
});
}

View File

@ -19,7 +19,7 @@
import { Button, ButtonGroup, Intent, Label, MenuItem, Switch } from '@blueprintjs/core';
import { IconNames } from '@blueprintjs/icons';
import classNames from 'classnames';
import { SqlComparison, SqlExpression, SqlLiteral, SqlRef } from 'druid-query-toolkit';
import { C, L, SqlComparison, SqlExpression } from 'druid-query-toolkit';
import * as JSONBig from 'json-bigint-native';
import React from 'react';
import ReactTable, { Filter } from 'react-table';
@ -125,7 +125,7 @@ const tableColumns: Record<CapabilitiesMode, string[]> = {
};
function formatRangeDimensionValue(dimension: any, value: any): string {
return `${SqlRef.column(String(dimension))}=${SqlLiteral.create(String(value))}`;
return `${C(String(dimension))}=${L(String(value))}`;
}
export interface SegmentsViewProps {
@ -281,20 +281,23 @@ END AS "time_span"`,
// Creates filters like `shard_spec LIKE '%"type":"numbered"%'`
const modeAndNeedle = parseFilterModeAndNeedle(f);
if (!modeAndNeedle) return;
const shardSpecRef = SqlRef.column('shard_spec');
const shardSpecColumn = C('shard_spec');
switch (modeAndNeedle.mode) {
case '=':
return SqlComparison.like(shardSpecRef, `%"type":"${modeAndNeedle.needle}"%`);
return SqlComparison.like(shardSpecColumn, `%"type":"${modeAndNeedle.needle}"%`);
case '!=':
return SqlComparison.notLike(shardSpecRef, `%"type":"${modeAndNeedle.needle}"%`);
return SqlComparison.notLike(
shardSpecColumn,
`%"type":"${modeAndNeedle.needle}"%`,
);
default:
return SqlComparison.like(shardSpecRef, `%"type":"${modeAndNeedle.needle}%`);
return SqlComparison.like(shardSpecColumn, `%"type":"${modeAndNeedle.needle}%`);
}
} else if (f.id.startsWith('is_')) {
if (f.value === 'all') return;
return SqlRef.column(f.id).equal(f.value === 'true' ? 1 : 0);
return C(f.id).equal(f.value === 'true' ? 1 : 0);
} else {
return sqlQueryCustomTableFilter(f);
}
@ -335,7 +338,7 @@ END AS "time_span"`,
queryParts.push(
'ORDER BY ' +
sorted
.map((sort: any) => `${SqlRef.column(sort.id)} ${sort.desc ? 'DESC' : 'ASC'}`)
.map((sort: any) => `${C(sort.id)} ${sort.desc ? 'DESC' : 'ASC'}`)
.join(', '),
);
}
@ -352,7 +355,7 @@ END AS "time_span"`,
queryParts.push(
'ORDER BY ' +
sorted
.map((sort: any) => `${SqlRef.column(sort.id)} ${sort.desc ? 'DESC' : 'ASC'}`)
.map((sort: any) => `${C(sort.id)} ${sort.desc ? 'DESC' : 'ASC'}`)
.join(', '),
);
}

View File

@ -19,7 +19,7 @@
import { Button, FormGroup, Menu, MenuItem } from '@blueprintjs/core';
import { IconNames } from '@blueprintjs/icons';
import { Popover2 } from '@blueprintjs/popover2';
import { QueryResult, SqlExpression, SqlFunction } from 'druid-query-toolkit';
import { F, QueryResult, SqlExpression } from 'druid-query-toolkit';
import React from 'react';
import { possibleDruidFormatForValues, TIME_COLUMN } from '../../../druid-models';
@ -49,7 +49,7 @@ export const ColumnActions = React.memo(function ExpressionEditor(props: ColumnA
const expression = queryResult.sqlQuery?.getSelectExpressionForIndex(headerIndex);
if (sqlQuery.getEffectiveWhereExpression().containsColumn(header)) {
if (sqlQuery.getEffectiveWhereExpression().containsColumnName(header)) {
removeFilterButton = (
<Button
icon={IconNames.FILTER_REMOVE}
@ -147,7 +147,6 @@ export const ColumnActions = React.memo(function ExpressionEditor(props: ColumnA
};
const underlyingSelectExpression = expression.getUnderlyingExpression();
convertButton = (
<Popover2
content={
@ -159,9 +158,7 @@ export const ColumnActions = React.memo(function ExpressionEditor(props: ColumnA
text="Convert to SUM(...)"
onClick={() => {
convertToAggregate([
SqlFunction.simple('SUM', [underlyingSelectExpression]).as(
`sum_${header}`,
),
F('SUM', underlyingSelectExpression).as(`sum_${header}`),
]);
}}
/>
@ -169,9 +166,7 @@ export const ColumnActions = React.memo(function ExpressionEditor(props: ColumnA
text="Convert to MIN(...)"
onClick={() => {
convertToAggregate([
SqlFunction.simple('MIN', [underlyingSelectExpression]).as(
`min_${header}`,
),
F('MIN', underlyingSelectExpression).as(`min_${header}`),
]);
}}
/>
@ -179,9 +174,7 @@ export const ColumnActions = React.memo(function ExpressionEditor(props: ColumnA
text="Convert to MAX(...)"
onClick={() => {
convertToAggregate([
SqlFunction.simple('MAX', [underlyingSelectExpression]).as(
`max_${header}`,
),
F('MAX', underlyingSelectExpression).as(`max_${header}`),
]);
}}
/>
@ -189,15 +182,9 @@ export const ColumnActions = React.memo(function ExpressionEditor(props: ColumnA
text="Convert to SUM(...), MIN(...), and MAX(...)"
onClick={() => {
convertToAggregate([
SqlFunction.simple('SUM', [underlyingSelectExpression]).as(
`sum_${header}`,
),
SqlFunction.simple('MIN', [underlyingSelectExpression]).as(
`min_${header}`,
),
SqlFunction.simple('MAX', [underlyingSelectExpression]).as(
`max_${header}`,
),
F('SUM', underlyingSelectExpression).as(`sum_${header}`),
F('MIN', underlyingSelectExpression).as(`min_${header}`),
F('MAX', underlyingSelectExpression).as(`max_${header}`),
]);
}}
/>
@ -207,9 +194,9 @@ export const ColumnActions = React.memo(function ExpressionEditor(props: ColumnA
text="Convert to APPROX_COUNT_DISTINCT_DS_HLL(...)"
onClick={() => {
convertToAggregate([
SqlFunction.simple('APPROX_COUNT_DISTINCT_DS_HLL', [
underlyingSelectExpression,
]).as(`unique_${header}`),
F('APPROX_COUNT_DISTINCT_DS_HLL', underlyingSelectExpression).as(
`unique_${header}`,
),
]);
}}
/>
@ -217,9 +204,9 @@ export const ColumnActions = React.memo(function ExpressionEditor(props: ColumnA
text="Convert to APPROX_COUNT_DISTINCT_DS_THETA(...)"
onClick={() => {
convertToAggregate([
SqlFunction.simple('APPROX_COUNT_DISTINCT_DS_THETA', [
underlyingSelectExpression,
]).as(`unique_${header}`),
F('APPROX_COUNT_DISTINCT_DS_THETA', underlyingSelectExpression).as(
`unique_${header}`,
),
]);
}}
/>
@ -227,9 +214,9 @@ export const ColumnActions = React.memo(function ExpressionEditor(props: ColumnA
text="Convert to APPROX_COUNT_DISTINCT_BUILTIN(...)"
onClick={() => {
convertToAggregate([
SqlFunction.simple('APPROX_COUNT_DISTINCT_BUILTIN', [
underlyingSelectExpression,
]).as(`unique_${header}`),
F('APPROX_COUNT_DISTINCT_BUILTIN', underlyingSelectExpression).as(
`unique_${header}`,
),
]);
}}
/>

View File

@ -19,7 +19,7 @@
import { Button, Classes, Dialog, Intent } from '@blueprintjs/core';
import { IconNames } from '@blueprintjs/icons';
import classNames from 'classnames';
import { SqlTableRef } from 'druid-query-toolkit';
import { T } from 'druid-query-toolkit';
import React, { useState } from 'react';
import { Execution, QueryWithContext } from '../../../druid-models';
@ -117,7 +117,7 @@ export const IngestionProgressDialog = React.memo(function IngestionProgressDial
onClick={() => {
if (!insertResultState.data) return;
goToQuery({
queryString: `SELECT * FROM ${SqlTableRef.create(
queryString: `SELECT * FROM ${T(
insertResultState.data.getIngestDatasource()!,
)}`,
});

View File

@ -18,7 +18,7 @@
import { Icon } from '@blueprintjs/core';
import classNames from 'classnames';
import { Column, QueryResult, SqlRef } from 'druid-query-toolkit';
import { Column, QueryResult, SqlColumn } from 'druid-query-toolkit';
import React from 'react';
import { columnToIcon } from '../../../../../utils';
@ -60,7 +60,7 @@ export const ExpressionEntry = function ExpressionEntry(props: ExpressionEntryPr
{expression.getOutputName() || 'EXPR?'}
<span className="type-name">{` :: ${column.nativeType}`}</span>
</div>
{!(expression instanceof SqlRef) && (
{!(expression instanceof SqlColumn) && (
<div className="expression">
{expression.getUnderlyingExpression().prettify({ keywordCasing: 'preserve' }).toString()}
</div>

View File

@ -76,8 +76,8 @@ export const PreviewTable = React.memo(function PreviewTable(props: PreviewTable
if (!parsedQuery || !parsedQuery.isRealOutputColumnAtSelectIndex(headerIndex)) return false;
return (
parsedQuery.getEffectiveWhereExpression().containsColumn(header) ||
parsedQuery.getEffectiveHavingExpression().containsColumn(header)
parsedQuery.getEffectiveWhereExpression().containsColumnName(header) ||
parsedQuery.getEffectiveHavingExpression().containsColumnName(header)
);
}

View File

@ -19,7 +19,7 @@
import { Button, Callout, Intent, Tag } from '@blueprintjs/core';
import { IconNames } from '@blueprintjs/icons';
import { CancelToken } from 'axios';
import { QueryResult, SqlExpression, SqlFunction, SqlQuery } from 'druid-query-toolkit';
import { F, QueryResult, SqlExpression, SqlFunction, SqlQuery } from 'druid-query-toolkit';
import React, { useEffect } from 'react';
import { Execution } from '../../../../druid-models';
@ -47,8 +47,8 @@ function expressionCastToString(ex: SqlExpression): SqlExpression {
return CAST_AS_VARCHAR_TEMPLATE.fillPlaceholders([ex]);
}
function countDistinct(ex: SqlExpression): SqlExpression {
return SqlFunction.simple('APPROX_COUNT_DISTINCT_DS_HLL', [ex]);
function approxCountDistinct(ex: SqlExpression): SqlExpression {
return F('APPROX_COUNT_DISTINCT_DS_HLL', ex);
}
function within(a: number, b: number, percent: number): boolean {
@ -159,13 +159,13 @@ export const RollupAnalysisPane = React.memo(function RollupAnalysisPane(
const expressionPairs: SqlExpression[] = deep
? pairs(expressions.length).map(([i, j]) =>
countDistinct(SqlFunction.simple('CONCAT', [groupedAsStrings[i], groupedAsStrings[j]])),
approxCountDistinct(F('CONCAT', groupedAsStrings[i], groupedAsStrings[j])),
)
: [];
const queryString = seedQuery
.changeSelectExpressions(groupedAsStrings.map(countDistinct).concat(expressionPairs))
.addSelect(countDistinct(SqlFunction.simple('CONCAT', groupedAsStrings)), {
.changeSelectExpressions(groupedAsStrings.map(approxCountDistinct).concat(expressionPairs))
.addSelect(approxCountDistinct(SqlFunction.simple('CONCAT', groupedAsStrings)), {
insertIndex: 0,
})
.addSelect(SqlFunction.COUNT_STAR, { insertIndex: 0 })

View File

@ -32,14 +32,7 @@ import { IconNames } from '@blueprintjs/icons';
import { Popover2 } from '@blueprintjs/popover2';
import classNames from 'classnames';
import { select, selectAll } from 'd3-selection';
import {
QueryResult,
QueryRunner,
SqlExpression,
SqlFunction,
SqlQuery,
SqlRef,
} from 'druid-query-toolkit';
import { C, F, QueryResult, QueryRunner, SqlExpression, SqlQuery } from 'druid-query-toolkit';
import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
import { ClearableInput, LearnMore, Loader } from '../../../components';
@ -361,10 +354,9 @@ export const SchemaStep = function SchemaStep(props: SchemaStepProps) {
...ingestQueryPattern,
dimensions: without(ingestQueryPattern.dimensions, countExpression),
metrics: [
(countExpression
? SqlFunction.simple('SUM', [countExpression.getUnderlyingExpression()])
: SqlFunction.COUNT_STAR
).as(countExpression?.getOutputName() || 'count'),
(countExpression ? F.sum(countExpression.getUnderlyingExpression()) : F.count()).as(
countExpression?.getOutputName() || 'count',
),
],
});
}
@ -460,8 +452,8 @@ export const SchemaStep = function SchemaStep(props: SchemaStepProps) {
const unusedColumns = ingestQueryPattern
? ingestQueryPattern.mainExternalConfig.signature.filter(
({ name }) =>
!ingestQueryPattern.dimensions.some(d => d.containsColumn(name)) &&
!ingestQueryPattern.metrics?.some(m => m.containsColumn(name)),
!ingestQueryPattern.dimensions.some(d => d.containsColumnName(name)) &&
!ingestQueryPattern.metrics?.some(m => m.containsColumnName(name)),
)
: [];
@ -714,7 +706,7 @@ export const SchemaStep = function SchemaStep(props: SchemaStepProps) {
onClick={() => {
handleQueryAction(q =>
q.addSelect(
SqlRef.column(column.name),
C(column.name),
ingestQueryPattern.metrics
? { insertIndex: 'last-grouping', addToGroupBy: 'end' }
: {},

View File

@ -107,7 +107,7 @@ export const SqlDataLoaderView = React.memo(function SqlDataLoaderView(
onDone={async () => {
const ingestDatasource = SqlQuery.parse(content.queryString)
.getIngestTable()
?.getTable();
?.getName();
if (!ingestDatasource) {
AppToaster.show({ message: `Must have an ingest datasource`, intent: Intent.DANGER });

View File

@ -18,12 +18,12 @@
import { MenuItem } from '@blueprintjs/core';
import { IconNames } from '@blueprintjs/icons';
import { SqlExpression, SqlFunction, SqlLiteral, SqlQuery, SqlRef } from 'druid-query-toolkit';
import { C, F, L, SqlExpression, SqlQuery } from 'druid-query-toolkit';
import React from 'react';
import { prettyPrintSql } from '../../../../../utils';
const NINE_THOUSAND = SqlLiteral.create(9000);
const NINE_THOUSAND = L(9000);
export interface NumberMenuItemsProps {
table: string;
@ -36,7 +36,6 @@ export interface NumberMenuItemsProps {
export const NumberMenuItems = React.memo(function NumberMenuItems(props: NumberMenuItemsProps) {
function renderFilterMenu(): JSX.Element {
const { columnName, parsedQuery, onQueryChange } = props;
const ref = SqlRef.column(columnName);
function filterMenuItem(clause: SqlExpression) {
return (
@ -49,17 +48,18 @@ export const NumberMenuItems = React.memo(function NumberMenuItems(props: Number
);
}
const column = C(columnName);
return (
<MenuItem icon={IconNames.FILTER} text="Filter">
{filterMenuItem(ref.greaterThan(NINE_THOUSAND))}
{filterMenuItem(ref.lessThanOrEqual(NINE_THOUSAND))}
{filterMenuItem(column.greaterThan(NINE_THOUSAND))}
{filterMenuItem(column.lessThanOrEqual(NINE_THOUSAND))}
</MenuItem>
);
}
function renderRemoveFilter(): JSX.Element | undefined {
const { columnName, parsedQuery, onQueryChange } = props;
if (!parsedQuery.getEffectiveWhereExpression().containsColumn(columnName)) return;
if (!parsedQuery.getEffectiveWhereExpression().containsColumnName(columnName)) return;
return (
<MenuItem
@ -75,7 +75,6 @@ export const NumberMenuItems = React.memo(function NumberMenuItems(props: Number
function renderGroupByMenu(): JSX.Element | undefined {
const { columnName, parsedQuery, onQueryChange } = props;
if (!parsedQuery.hasGroupBy()) return;
const ref = SqlRef.column(columnName);
function groupByMenuItem(ex: SqlExpression, alias?: string) {
return (
@ -94,13 +93,11 @@ export const NumberMenuItems = React.memo(function NumberMenuItems(props: Number
);
}
const column = C(columnName);
return (
<MenuItem icon={IconNames.GROUP_OBJECTS} text="Group by">
{groupByMenuItem(ref)}
{groupByMenuItem(
SqlFunction.simple('TRUNC', [ref, SqlLiteral.create(-1)]),
`${columnName}_truncated`,
)}
{groupByMenuItem(column)}
{groupByMenuItem(F('TRUNC', column, -1), `${columnName}_truncated`)}
</MenuItem>
);
}
@ -124,7 +121,6 @@ export const NumberMenuItems = React.memo(function NumberMenuItems(props: Number
function renderAggregateMenu(): JSX.Element | undefined {
const { columnName, parsedQuery, onQueryChange } = props;
if (!parsedQuery.hasGroupBy()) return;
const ref = SqlRef.column(columnName);
function aggregateMenuItem(ex: SqlExpression, alias: string) {
return (
@ -137,17 +133,15 @@ export const NumberMenuItems = React.memo(function NumberMenuItems(props: Number
);
}
const column = C(columnName);
return (
<MenuItem icon={IconNames.FUNCTION} text="Aggregate">
{aggregateMenuItem(SqlFunction.simple('SUM', [ref]), `sum_${columnName}`)}
{aggregateMenuItem(SqlFunction.simple('MIN', [ref]), `min_${columnName}`)}
{aggregateMenuItem(SqlFunction.simple('MAX', [ref]), `max_${columnName}`)}
{aggregateMenuItem(SqlFunction.simple('AVG', [ref]), `avg_${columnName}`)}
{aggregateMenuItem(
SqlFunction.simple('APPROX_QUANTILE', [ref, SqlLiteral.create(0.98)]),
`p98_${columnName}`,
)}
{aggregateMenuItem(SqlFunction.simple('LATEST', [ref]), `latest_${columnName}`)}
{aggregateMenuItem(F('SUM', column), `sum_${columnName}`)}
{aggregateMenuItem(F('MIN', column), `min_${columnName}`)}
{aggregateMenuItem(F('MAX', column), `max_${columnName}`)}
{aggregateMenuItem(F('AVG', column), `avg_${columnName}`)}
{aggregateMenuItem(F('APPROX_QUANTILE', column, 0.98), `p98_${columnName}`)}
{aggregateMenuItem(F('LATEST', column), `latest_${columnName}`)}
</MenuItem>
);
}

View File

@ -18,15 +18,7 @@
import { MenuItem } from '@blueprintjs/core';
import { IconNames } from '@blueprintjs/icons';
import {
SqlExpression,
SqlFunction,
SqlJoinPart,
SqlLiteral,
SqlQuery,
SqlRef,
SqlTableRef,
} from 'druid-query-toolkit';
import { C, F, N, SqlExpression, SqlJoinPart, SqlQuery, T } from 'druid-query-toolkit';
import React from 'react';
import { EMPTY_LITERAL, prettyPrintSql } from '../../../../../utils';
@ -43,7 +35,6 @@ export interface StringMenuItemsProps {
export const StringMenuItems = React.memo(function StringMenuItems(props: StringMenuItemsProps) {
function renderFilterMenu(): JSX.Element | undefined {
const { columnName, parsedQuery, onQueryChange } = props;
const ref = SqlRef.column(columnName);
function filterMenuItem(clause: SqlExpression, run = true) {
return (
@ -56,19 +47,20 @@ export const StringMenuItems = React.memo(function StringMenuItems(props: String
);
}
const column = C(columnName);
return (
<MenuItem icon={IconNames.FILTER} text="Filter">
{filterMenuItem(ref.isNotNull())}
{filterMenuItem(ref.equal(EMPTY_LITERAL), false)}
{filterMenuItem(ref.like(EMPTY_LITERAL), false)}
{filterMenuItem(SqlFunction.simple('REGEXP_LIKE', [ref, EMPTY_LITERAL]), false)}
{filterMenuItem(column.isNotNull())}
{filterMenuItem(column.equal(EMPTY_LITERAL), false)}
{filterMenuItem(column.like(EMPTY_LITERAL), false)}
{filterMenuItem(F('REGEXP_LIKE', column, EMPTY_LITERAL), false)}
</MenuItem>
);
}
function renderRemoveFilter(): JSX.Element | undefined {
const { columnName, parsedQuery, onQueryChange } = props;
if (!parsedQuery.getEffectiveWhereExpression().containsColumn(columnName)) return;
if (!parsedQuery.getEffectiveWhereExpression().containsColumnName(columnName)) return;
return (
<MenuItem
@ -118,24 +110,12 @@ export const StringMenuItems = React.memo(function StringMenuItems(props: String
);
}
const column = C(columnName);
return (
<MenuItem icon={IconNames.GROUP_OBJECTS} text="Group by">
{groupByMenuItem(SqlRef.column(columnName))}
{groupByMenuItem(
SqlFunction.simple('SUBSTRING', [
SqlRef.column(columnName),
SqlLiteral.create(1),
SqlLiteral.create(2),
]),
`${columnName}_substring`,
)}
{groupByMenuItem(
SqlFunction.simple('REGEXP_EXTRACT', [
SqlRef.column(columnName),
SqlLiteral.create('(\\d+)'),
]),
`${columnName}_extract`,
)}
{groupByMenuItem(column)}
{groupByMenuItem(F('SUBSTRING', column, 1, 2), `${columnName}_substring`)}
{groupByMenuItem(F('REGEXP_EXTRACT', column, '(\\d+)'), `${columnName}_extract`)}
</MenuItem>
);
}
@ -143,7 +123,6 @@ export const StringMenuItems = React.memo(function StringMenuItems(props: String
function renderAggregateMenu(): JSX.Element | undefined {
const { columnName, parsedQuery, onQueryChange } = props;
if (!parsedQuery.hasGroupBy()) return;
const ref = SqlRef.column(columnName);
function aggregateMenuItem(ex: SqlExpression, alias: string, run = true) {
return (
@ -156,18 +135,16 @@ export const StringMenuItems = React.memo(function StringMenuItems(props: String
);
}
const column = C(columnName);
return (
<MenuItem icon={IconNames.FUNCTION} text="Aggregate">
{aggregateMenuItem(SqlFunction.countDistinct(ref), `dist_${columnName}`)}
{aggregateMenuItem(F.countDistinct(column), `dist_${columnName}`)}
{aggregateMenuItem(
SqlFunction.COUNT_STAR.addWhereExpression(ref.equal(EMPTY_LITERAL)),
F.count().addWhereExpression(column.equal(EMPTY_LITERAL)),
`filtered_dist_${columnName}`,
false,
)}
{aggregateMenuItem(
SqlFunction.simple('LATEST', [ref, SqlLiteral.create(100)]),
`latest_${columnName}`,
)}
{aggregateMenuItem(F('LATEST', column, 100), `latest_${columnName}`)}
</MenuItem>
);
}
@ -175,6 +152,8 @@ export const StringMenuItems = React.memo(function StringMenuItems(props: String
function renderJoinMenu(): JSX.Element | undefined {
const { schema, table, columnName, parsedQuery, onQueryChange } = props;
if (schema !== 'lookup' || !parsedQuery) return;
const firstTableName = parsedQuery.getFirstTableName();
if (!firstTableName) return;
const { originalTableColumn, lookupColumn } = getJoinColumns(parsedQuery, table);
@ -188,13 +167,15 @@ export const StringMenuItems = React.memo(function StringMenuItems(props: String
parsedQuery.addJoin(
SqlJoinPart.create(
'LEFT',
SqlTableRef.create(table, schema),
SqlRef.column(columnName, table, 'lookup').equal(
SqlRef.column(
lookupColumn === columnName ? originalTableColumn : 'XXX',
parsedQuery.getFirstTableName(),
N(schema).table(table),
N('lookup')
.table(table)
.column(columnName)
.equal(
T(firstTableName).column(
lookupColumn === columnName ? originalTableColumn : 'XXX',
),
),
),
),
),
false,
@ -209,13 +190,15 @@ export const StringMenuItems = React.memo(function StringMenuItems(props: String
parsedQuery.addJoin(
SqlJoinPart.create(
'INNER',
SqlTableRef.create(table, schema),
SqlRef.column(columnName, table, 'lookup').equal(
SqlRef.column(
lookupColumn === columnName ? originalTableColumn : 'XXX',
parsedQuery.getFirstTableName(),
N(schema).table(table),
N('lookup')
.table(table)
.column(columnName)
.equal(
T(firstTableName).column(
lookupColumn === columnName ? originalTableColumn : 'XXX',
),
),
),
),
),
false,

View File

@ -18,7 +18,7 @@
import { MenuDivider, MenuItem } from '@blueprintjs/core';
import { IconNames } from '@blueprintjs/icons';
import { SqlExpression, SqlFunction, SqlLiteral, SqlQuery, SqlRef } from 'druid-query-toolkit';
import { C, F, SqlExpression, SqlQuery } from 'druid-query-toolkit';
import React from 'react';
import { prettyPrintSql } from '../../../../../utils';
@ -42,12 +42,12 @@ const BETWEEN: SqlExpression = SqlExpression.parse(`(? <= ? AND ? < ?)`);
// ------------------------------------
function fillWithColumn(b: SqlExpression, columnName: string): SqlExpression {
return b.fillPlaceholders([SqlRef.column(columnName)]);
return b.fillPlaceholders([C(columnName)]);
}
function fillWithColumnStartEnd(columnName: string, start: Date, end: Date): SqlExpression {
const ref = SqlRef.column(columnName);
return BETWEEN.fillPlaceholders([SqlLiteral.create(start), ref, ref, SqlLiteral.create(end)])!;
const column = C(columnName);
return BETWEEN.fillPlaceholders([start, column, column, end])!;
}
// ------------------------------------
@ -160,7 +160,7 @@ export const TimeMenuItems = React.memo(function TimeMenuItems(props: TimeMenuIt
function renderRemoveFilter(): JSX.Element | undefined {
const { columnName, parsedQuery, onQueryChange } = props;
if (!parsedQuery.getEffectiveWhereExpression().containsColumn(columnName)) return;
if (!parsedQuery.getEffectiveWhereExpression().containsColumnName(columnName)) return;
return (
<MenuItem
@ -192,7 +192,6 @@ export const TimeMenuItems = React.memo(function TimeMenuItems(props: TimeMenuIt
function renderGroupByMenu(): JSX.Element | undefined {
const { columnName, parsedQuery, onQueryChange } = props;
if (!parsedQuery.hasGroupBy()) return;
const ref = SqlRef.column(columnName);
function groupByMenuItem(ex: SqlExpression, alias: string) {
return (
@ -211,41 +210,18 @@ export const TimeMenuItems = React.memo(function TimeMenuItems(props: TimeMenuIt
);
}
const column = C(columnName);
return (
<MenuItem icon={IconNames.GROUP_OBJECTS} text="Group by">
{groupByMenuItem(
SqlFunction.simple('TIME_FLOOR', [ref, SqlLiteral.create('PT1H')]),
`${columnName}_by_hour`,
)}
{groupByMenuItem(
SqlFunction.simple('TIME_FLOOR', [ref, SqlLiteral.create('P1D')]),
`${columnName}_by_day`,
)}
{groupByMenuItem(
SqlFunction.simple('TIME_FLOOR', [ref, SqlLiteral.create('P1M')]),
`${columnName}_by_month`,
)}
{groupByMenuItem(
SqlFunction.simple('TIME_FLOOR', [ref, SqlLiteral.create('P1Y')]),
`${columnName}_by_year`,
)}
{groupByMenuItem(F.timeFloor(column, 'PT1H'), `${columnName}_by_hour`)}
{groupByMenuItem(F.timeFloor(column, 'P1D'), `${columnName}_by_day`)}
{groupByMenuItem(F.timeFloor(column, 'P1M'), `${columnName}_by_month`)}
{groupByMenuItem(F.timeFloor(column, 'P1Y'), `${columnName}_by_year`)}
<MenuDivider />
{groupByMenuItem(
SqlFunction.simple('TIME_EXTRACT', [ref, SqlLiteral.create('HOUR')]),
`hour_of_${columnName}`,
)}
{groupByMenuItem(
SqlFunction.simple('TIME_EXTRACT', [ref, SqlLiteral.create('DAY')]),
`day_of_${columnName}`,
)}
{groupByMenuItem(
SqlFunction.simple('TIME_EXTRACT', [ref, SqlLiteral.create('MONTH')]),
`month_of_${columnName}`,
)}
{groupByMenuItem(
SqlFunction.simple('TIME_EXTRACT', [ref, SqlLiteral.create('YEAR')]),
`year_of_${columnName}`,
)}
{groupByMenuItem(F('TIME_EXTRACT', column, 'HOUR'), `hour_of_${columnName}`)}
{groupByMenuItem(F('TIME_EXTRACT', column, 'DAY'), `day_of_${columnName}`)}
{groupByMenuItem(F('TIME_EXTRACT', column, 'MONTH'), `month_of_${columnName}`)}
{groupByMenuItem(F('TIME_EXTRACT', column, 'YEAR'), `year_of_${columnName}`)}
</MenuItem>
);
}
@ -253,7 +229,6 @@ export const TimeMenuItems = React.memo(function TimeMenuItems(props: TimeMenuIt
function renderAggregateMenu(): JSX.Element | undefined {
const { columnName, parsedQuery, onQueryChange } = props;
if (!parsedQuery.hasGroupBy()) return;
const ref = SqlRef.column(columnName);
function aggregateMenuItem(ex: SqlExpression, alias: string) {
return (
@ -266,10 +241,11 @@ export const TimeMenuItems = React.memo(function TimeMenuItems(props: TimeMenuIt
);
}
const column = C(columnName);
return (
<MenuItem icon={IconNames.FUNCTION} text="Aggregate">
{aggregateMenuItem(SqlFunction.simple('MAX', [ref]), `max_${columnName}`)}
{aggregateMenuItem(SqlFunction.simple('MIN', [ref]), `min_${columnName}`)}
{aggregateMenuItem(F.max(column), `max_${columnName}`)}
{aggregateMenuItem(F.min(column), `min_${columnName}`)}
</MenuItem>
);
}

View File

@ -20,13 +20,16 @@ import { HTMLSelect, Menu, MenuItem, Position, Tree, TreeNodeInfo } from '@bluep
import { IconNames } from '@blueprintjs/icons';
import { Popover2 } from '@blueprintjs/popover2';
import {
C,
F,
N,
SqlColumn,
SqlComparison,
SqlExpression,
SqlFunction,
SqlJoinPart,
SqlQuery,
SqlRef,
SqlTableRef,
SqlTable,
T,
} from 'druid-query-toolkit';
import React, { ChangeEvent } from 'react';
@ -44,12 +47,12 @@ import { NumberMenuItems, StringMenuItems, TimeMenuItems } from './column-tree-m
import './column-tree.scss';
const COUNT_STAR = SqlFunction.COUNT_STAR.as('Count');
const COUNT_STAR = F.count().as('Count');
function getCountExpression(columnNames: string[]): SqlExpression {
for (const columnName of columnNames) {
if (columnName === 'count' || columnName === '__count') {
return SqlFunction.simple('SUM', [SqlRef.column(columnName)]).as('Count');
return F.sum(C(columnName)).as('Count');
}
}
return COUNT_STAR;
@ -96,22 +99,22 @@ function handleColumnShow(options: HandleColumnClickOptions): void {
where = parsedQuery.getWhereExpression();
aggregates = parsedQuery.getAggregateSelectExpressions();
} else if (columnSchema === 'druid') {
from = SqlTableRef.create(columnTable);
from = T(columnTable);
where = defaultWhere;
} else {
from = SqlTableRef.create(columnTable, columnSchema);
from = N(columnSchema).table(columnTable);
}
if (!aggregates.length) {
aggregates.push(COUNT_STAR);
}
const columnRef = SqlRef.column(columnName);
const column = C(columnName);
let query: SqlQuery;
if (columnSchema === 'druid' && columnType === 'TIMESTAMP') {
query = TIME_QUERY.fillPlaceholders([columnRef, from]) as SqlQuery;
query = TIME_QUERY.fillPlaceholders([column, from]) as SqlQuery;
} else {
query = STRING_QUERY.fillPlaceholders([columnRef, from]) as SqlQuery;
query = STRING_QUERY.fillPlaceholders([column, from]) as SqlQuery;
}
let newSelectExpressions = query.selectExpressions;
@ -152,11 +155,11 @@ export function getJoinColumns(parsedQuery: SqlQuery, _table: string) {
const firstOnExpression = parsedQuery.fromClause.joinParts.first().onExpression;
if (firstOnExpression instanceof SqlComparison && firstOnExpression.op === '=') {
const { lhs, rhs } = firstOnExpression;
if (lhs instanceof SqlRef && lhs.getNamespace() === 'lookup') {
lookupColumn = lhs.getColumn();
if (lhs instanceof SqlColumn && lhs.getNamespaceName() === 'lookup') {
lookupColumn = lhs.getName();
}
if (rhs instanceof SqlRef) {
originalTableColumn = rhs.getColumn();
if (rhs instanceof SqlColumn) {
originalTableColumn = rhs.getName();
}
}
}
@ -199,7 +202,7 @@ export class ColumnTree extends React.PureComponent<ColumnTreeProps, ColumnTreeS
<Deferred
content={() => {
const parsedQuery = props.getParsedQuery();
const tableRef = SqlTableRef.create(tableName);
const tableRef = T(tableName);
const prettyTableRef = prettyPrintSql(tableRef);
const countExpression = getCountExpression(
metadata.map(child => child.COLUMN_NAME),
@ -207,7 +210,7 @@ export class ColumnTree extends React.PureComponent<ColumnTreeProps, ColumnTreeS
const getQueryOnTable = () => {
return SqlQuery.create(
SqlTableRef.create(
SqlTable.create(
tableName,
schemaName === 'druid' ? undefined : schemaName,
),
@ -235,7 +238,7 @@ export class ColumnTree extends React.PureComponent<ColumnTreeProps, ColumnTreeS
.changeSelectExpressions(
metadata
.map(child => child.COLUMN_NAME)
.map(columnName => SqlRef.column(columnName)),
.map(columnName => C(columnName)),
)
.changeWhereExpression(getWhere()),
true,
@ -297,13 +300,16 @@ export class ColumnTree extends React.PureComponent<ColumnTreeProps, ColumnTreeS
.addJoin(
SqlJoinPart.create(
'LEFT',
SqlTableRef.create(tableName, schemaName),
SqlRef.column(lookupColumn, tableName, 'lookup').equal(
SqlRef.column(
originalTableColumn,
parsedQuery.getFirstTableName(),
N(schemaName).table(tableName),
N('lookup')
.table(tableName)
.column(lookupColumn)
.equal(
SqlColumn.create(
originalTableColumn,
parsedQuery.getFirstTableName(),
),
),
),
),
),
false,
@ -322,13 +328,16 @@ export class ColumnTree extends React.PureComponent<ColumnTreeProps, ColumnTreeS
parsedQuery.addJoin(
SqlJoinPart.create(
'INNER',
SqlTableRef.create(tableName, schemaName),
SqlRef.column(lookupColumn, tableName, 'lookup').equal(
SqlRef.column(
originalTableColumn,
parsedQuery.getFirstTableName(),
N(schemaName).table(tableName),
N('lookup')
.table(tableName)
.column(lookupColumn)
.equal(
SqlColumn.create(
originalTableColumn,
parsedQuery.getFirstTableName(),
),
),
),
),
),
false,

View File

@ -21,7 +21,7 @@ import { ResizeSensor2 } from '@blueprintjs/popover2';
import type { Ace } from 'ace-builds';
import ace from 'ace-builds';
import classNames from 'classnames';
import { SqlRef, SqlTableRef } from 'druid-query-toolkit';
import { C, T } from 'druid-query-toolkit';
import escape from 'lodash.escape';
import React from 'react';
import AceEditor from 'react-ace';
@ -164,7 +164,7 @@ export class FlexibleQueryInput extends React.PureComponent<
) {
const completions = ([] as any[]).concat(
uniq(columnMetadata.map(d => d.TABLE_SCHEMA)).map(v => ({
value: SqlTableRef.create(v).toString(),
value: String(T(v)),
score: 10,
meta: 'schema',
})),
@ -173,7 +173,7 @@ export class FlexibleQueryInput extends React.PureComponent<
.filter(d => (currentSchema ? d.TABLE_SCHEMA === currentSchema : true))
.map(d => d.TABLE_NAME),
).map(v => ({
value: SqlTableRef.create(v).toString(),
value: String(T(v)),
score: 49,
meta: 'datasource',
})),
@ -186,7 +186,7 @@ export class FlexibleQueryInput extends React.PureComponent<
)
.map(d => d.COLUMN_NAME),
).map(v => ({
value: SqlRef.column(v).toString(),
value: String(C(v)),
score: 50,
meta: 'column',
})),

View File

@ -16,7 +16,7 @@
* limitations under the License.
*/
import { SqlTableRef } from 'druid-query-toolkit';
import { T } from 'druid-query-toolkit';
import React from 'react';
import { Execution, WorkbenchQuery } from '../../../druid-models';
@ -47,7 +47,7 @@ export const IngestSuccessPane = React.memo(function IngestSuccessPane(
? stages.getTotalCounterForStage(lastStage, 'input0', 'rows') // Assume input0 since we know the segmentGenerator will only ever have one stage input
: -1;
const table = SqlTableRef.create(datasource);
const table = T(datasource);
const warnings = stages?.getWarningCount() || 0;

View File

@ -18,7 +18,7 @@
import { Button, Callout, FormGroup, Icon, Intent, Tag } from '@blueprintjs/core';
import { IconNames } from '@blueprintjs/icons';
import { SqlExpression, SqlRef } from 'druid-query-toolkit';
import { C, SqlExpression } from 'druid-query-toolkit';
import React, { useState } from 'react';
import { AutoForm, CenterMessage, LearnMore, Loader } from '../../../components';
@ -135,7 +135,7 @@ export const InputFormatStep = React.memo(function InputFormatStep(props: InputF
return {
column,
timeExpression: formatSql.fillPlaceholders([SqlRef.column(column)]),
timeExpression: formatSql.fillPlaceholders([C(column)]),
};
})[0];
}

View File

@ -21,7 +21,7 @@ import { IconName, IconNames } from '@blueprintjs/icons';
import { Popover2 } from '@blueprintjs/popover2';
import classNames from 'classnames';
import copy from 'copy-to-clipboard';
import { SqlTableRef } from 'druid-query-toolkit';
import { T } from 'druid-query-toolkit';
import React, { useState } from 'react';
import { Loader } from '../../../components';
@ -182,10 +182,8 @@ LIMIT 100`,
w.datasource !== WorkbenchQuery.INLINE_DATASOURCE_MARKER && (
<MenuItem
icon={IconNames.APPLICATION}
text={`SELECT * FROM ${SqlTableRef.create(w.datasource)}`}
onClick={() =>
onChangeQuery(`SELECT * FROM ${SqlTableRef.create(w.datasource)}`)
}
text={`SELECT * FROM ${T(w.datasource)}`}
onClick={() => onChangeQuery(`SELECT * FROM ${T(w.datasource)}`)}
/>
)}
<MenuItem

View File

@ -21,14 +21,15 @@ import { IconNames } from '@blueprintjs/icons';
import { Popover2 } from '@blueprintjs/popover2';
import classNames from 'classnames';
import {
C,
Column,
F,
QueryResult,
SqlAlias,
SqlExpression,
SqlFunction,
SqlLiteral,
SqlQuery,
SqlRef,
SqlStar,
} from 'druid-query-toolkit';
import * as JSONBig from 'json-bigint-native';
@ -65,10 +66,6 @@ import './result-table-pane.scss';
const CAST_TARGETS: string[] = ['VARCHAR', 'BIGINT', 'DOUBLE'];
function jsonValue(ex: SqlExpression, path: string): SqlExpression {
return SqlExpression.parse(`JSON_VALUE(${ex}, ${SqlLiteral.create(path)})`);
}
function getJsonPaths(jsons: Record<string, any>[]): string[] {
return ['$.'].concat(computeFlattenExprsForData(jsons, 'include-arrays', true));
}
@ -118,15 +115,15 @@ export const ResultTablePane = React.memo(function ResultTablePane(props: Result
if (!parsedQuery || !parsedQuery.isRealOutputColumnAtSelectIndex(headerIndex)) return false;
return (
parsedQuery.getEffectiveWhereExpression().containsColumn(header) ||
parsedQuery.getEffectiveHavingExpression().containsColumn(header)
parsedQuery.getEffectiveWhereExpression().containsColumnName(header) ||
parsedQuery.getEffectiveHavingExpression().containsColumnName(header)
);
}
function getHeaderMenu(column: Column, headerIndex: number) {
const header = column.name;
const type = column.sqlType || column.nativeType;
const ref = SqlRef.column(header);
const ref = C(header);
const prettyRef = prettyPrintSql(ref);
const menuItems: JSX.Element[] = [];
@ -136,7 +133,7 @@ export const ResultTablePane = React.memo(function ResultTablePane(props: Result
const orderByExpression = parsedQuery.isValidSelectIndex(headerIndex)
? SqlLiteral.index(headerIndex)
: SqlRef.column(header);
: ref;
const descOrderBy = orderByExpression.toOrderByExpression('DESC');
const ascOrderBy = orderByExpression.toOrderByExpression('ASC');
const orderBy = parsedQuery.getOrderByForSelectIndex(headerIndex);
@ -254,7 +251,7 @@ export const ResultTablePane = React.memo(function ResultTablePane(props: Result
if (!selectExpression) return;
onQueryAction(q =>
q.addSelect(
jsonValue(selectExpression.getUnderlyingExpression(), path).as(
F('JSON_VALUE', selectExpression.getUnderlyingExpression(), path).as(
selectExpression.getOutputName() + path.replace(/^\$/, ''),
),
{ insertIndex: headerIndex + 1 },
@ -271,7 +268,7 @@ export const ResultTablePane = React.memo(function ResultTablePane(props: Result
if (parsedQuery.isRealOutputColumnAtSelectIndex(headerIndex)) {
const whereExpression = parsedQuery.getWhereExpression();
if (whereExpression && whereExpression.containsColumn(header)) {
if (whereExpression && whereExpression.containsColumnName(header)) {
menuItems.push(
<MenuItem
key="remove_where"
@ -287,7 +284,7 @@ export const ResultTablePane = React.memo(function ResultTablePane(props: Result
}
const havingExpression = parsedQuery.getHavingExpression();
if (havingExpression && havingExpression.containsColumn(header)) {
if (havingExpression && havingExpression.containsColumnName(header)) {
menuItems.push(
<MenuItem
key="remove_having"
@ -386,31 +383,19 @@ export const ResultTablePane = React.memo(function ResultTablePane(props: Result
<MenuItem
text="Convert to SUM(...)"
onClick={() => {
convertToAggregate(
SqlFunction.simple('SUM', [underlyingSelectExpression]).as(
`sum_${header}`,
),
);
convertToAggregate(F.sum(underlyingSelectExpression).as(`sum_${header}`));
}}
/>
<MenuItem
text="Convert to MIN(...)"
onClick={() => {
convertToAggregate(
SqlFunction.simple('MIN', [underlyingSelectExpression]).as(
`min_${header}`,
),
);
convertToAggregate(F.min(underlyingSelectExpression).as(`min_${header}`));
}}
/>
<MenuItem
text="Convert to MAX(...)"
onClick={() => {
convertToAggregate(
SqlFunction.simple('MAX', [underlyingSelectExpression]).as(
`max_${header}`,
),
);
convertToAggregate(F.max(underlyingSelectExpression).as(`max_${header}`));
}}
/>
</>
@ -419,9 +404,7 @@ export const ResultTablePane = React.memo(function ResultTablePane(props: Result
text="Convert to COUNT(DISTINCT ...)"
onClick={() => {
convertToAggregate(
SqlFunction.decorated('COUNT', 'DISTINCT', [underlyingSelectExpression]).as(
`unique_${header}`,
),
F.countDistinct(underlyingSelectExpression).as(`unique_${header}`),
);
}}
/>
@ -429,9 +412,9 @@ export const ResultTablePane = React.memo(function ResultTablePane(props: Result
text="Convert to APPROX_COUNT_DISTINCT_DS_HLL(...)"
onClick={() => {
convertToAggregate(
SqlFunction.simple('APPROX_COUNT_DISTINCT_DS_HLL', [
underlyingSelectExpression,
]).as(`unique_${header}`),
F('APPROX_COUNT_DISTINCT_DS_HLL', underlyingSelectExpression).as(
`unique_${header}`,
),
);
}}
/>
@ -439,9 +422,9 @@ export const ResultTablePane = React.memo(function ResultTablePane(props: Result
text="Convert to APPROX_COUNT_DISTINCT_DS_THETA(...)"
onClick={() => {
convertToAggregate(
SqlFunction.simple('APPROX_COUNT_DISTINCT_DS_THETA', [
underlyingSelectExpression,
]).as(`unique_${header}`),
F('APPROX_COUNT_DISTINCT_DS_THETA', underlyingSelectExpression).as(
`unique_${header}`,
),
);
}}
/>
@ -496,7 +479,7 @@ export const ResultTablePane = React.memo(function ResultTablePane(props: Result
);
if (!runeMode) {
const orderByExpression = SqlRef.column(header);
const orderByExpression = ref;
const descOrderBy = orderByExpression.toOrderByExpression('DESC');
const ascOrderBy = orderByExpression.toOrderByExpression('ASC');
const descOrderByPretty = prettyPrintSql(descOrderBy);

View File

@ -18,10 +18,10 @@
import { MenuItem } from '@blueprintjs/core';
import { IconNames } from '@blueprintjs/icons';
import { SqlExpression, SqlFunction, SqlLiteral } from 'druid-query-toolkit';
import { F, SqlExpression, SqlFunction } from 'druid-query-toolkit';
import React from 'react';
import { compact, oneOf, tickIcon } from '../../../utils';
import { oneOf, tickIcon } from '../../../utils';
const OPTIONS: { label: string; value: string }[] = [
{ label: 'Second', value: 'PT1S' },
@ -53,16 +53,16 @@ export const TimeFloorMenuItem = function TimeFloorMenuItem(props: TimeFloorMenu
let innerExpression = expression.getUnderlyingExpression();
let currentDuration: string | undefined;
let origin: SqlExpression | undefined;
let timezone: SqlExpression | undefined;
let origin: string | undefined;
let timezone: string | undefined;
if (
innerExpression instanceof SqlFunction &&
oneOf(innerExpression.getEffectiveFunctionName(), 'TIME_FLOOR', 'FLOOR')
) {
const firstArg = innerExpression.getArg(0);
const secondArg = innerExpression.getArgAsString(1)?.toUpperCase();
origin = innerExpression.getArg(2);
timezone = innerExpression.getArg(3);
origin = innerExpression.getArgAsString(2);
timezone = innerExpression.getArgAsString(3);
if (firstArg && secondArg) {
innerExpression = firstArg;
currentDuration = UNIT_TO_DURATION[secondArg] || secondArg;
@ -71,13 +71,9 @@ export const TimeFloorMenuItem = function TimeFloorMenuItem(props: TimeFloorMenu
const changeTimeFloor = (duration: string | undefined) => {
onChange(
(duration
? SqlFunction.simple(
'TIME_FLOOR',
compact([innerExpression, SqlLiteral.create(duration), origin, timezone]),
)
: innerExpression
).as(expression.getOutputName()),
(duration ? F.timeFloor(innerExpression, duration, origin, timezone) : innerExpression).as(
expression.getOutputName(),
),
);
};