- If you have streaming ingestion running make sure that your interval range doe not
+ If you have streaming ingestion running make sure that your interval range does not
overlap with intervals where streaming data is being added - otherwise the kill task
will not start.
diff --git a/web-console/src/druid-models/workbench-query/workbench-query.ts b/web-console/src/druid-models/workbench-query/workbench-query.ts
index 6fe223847f6..d7847287fa6 100644
--- a/web-console/src/druid-models/workbench-query/workbench-query.ts
+++ b/web-console/src/druid-models/workbench-query/workbench-query.ts
@@ -17,6 +17,7 @@
*/
import type {
+ QueryParameter,
SqlClusteredByClause,
SqlExpression,
SqlPartitionedByClause,
@@ -66,6 +67,7 @@ interface IngestionLines {
export interface WorkbenchQueryValue {
queryString: string;
queryContext: QueryContext;
+ queryParameters?: QueryParameter[];
engine?: DruidEngine;
lastExecution?: LastExecution;
unlimited?: boolean;
@@ -235,6 +237,7 @@ export class WorkbenchQuery {
public readonly queryString: string;
public readonly queryContext: QueryContext;
+ public readonly queryParameters?: QueryParameter[];
public readonly engine?: DruidEngine;
public readonly lastExecution?: LastExecution;
public readonly unlimited?: boolean;
@@ -251,6 +254,7 @@ export class WorkbenchQuery {
}
this.queryString = queryString;
this.queryContext = value.queryContext;
+ this.queryParameters = value.queryParameters;
// Start back compat code for the engine names that might be coming from local storage
let possibleEngine: string | undefined = value.engine;
@@ -274,6 +278,7 @@ export class WorkbenchQuery {
return {
queryString: this.queryString,
queryContext: this.queryContext,
+ queryParameters: this.queryParameters,
engine: this.engine,
unlimited: this.unlimited,
};
@@ -297,6 +302,10 @@ export class WorkbenchQuery {
return new WorkbenchQuery({ ...this.valueOf(), queryContext });
}
+ public changeQueryParameters(queryParameters: QueryParameter[] | undefined): WorkbenchQuery {
+ return new WorkbenchQuery({ ...this.valueOf(), queryParameters });
+ }
+
public changeEngine(engine: DruidEngine | undefined): WorkbenchQuery {
return new WorkbenchQuery({ ...this.valueOf(), engine });
}
@@ -425,11 +434,12 @@ export class WorkbenchQuery {
let ret: WorkbenchQuery = this;
// Explicitly select MSQ, adjust the context, set maxNumTasks to the lowest possible and add in ingest mode flags
+ const { queryContext } = this;
ret = ret.changeEngine('sql-msq-task').changeQueryContext({
- ...this.queryContext,
+ ...queryContext,
maxNumTasks: 2,
- finalizeAggregations: false,
- groupByEnableMultiValueUnnesting: false,
+ finalizeAggregations: queryContext.finalizeAggregations ?? false,
+ groupByEnableMultiValueUnnesting: queryContext.groupByEnableMultiValueUnnesting ?? false,
});
// Remove everything pertaining to INSERT INTO / REPLACE INTO from the query string
@@ -458,7 +468,7 @@ export class WorkbenchQuery {
prefixLines: number;
cancelQueryId?: string;
} {
- const { queryString, queryContext, unlimited, prefixLines } = this;
+ const { queryString, queryContext, queryParameters, unlimited, prefixLines } = this;
const engine = this.getEffectiveEngine();
if (engine === 'native') {
@@ -544,6 +554,10 @@ export class WorkbenchQuery {
apiQuery.context.groupByEnableMultiValueUnnesting ??= !ingestQuery;
}
+ if (Array.isArray(queryParameters) && queryParameters.length) {
+ apiQuery.parameters = queryParameters;
+ }
+
return {
engine,
query: apiQuery,
diff --git a/web-console/src/utils/index.tsx b/web-console/src/utils/index.tsx
index c27a070b976..e7dc33e7a65 100644
--- a/web-console/src/utils/index.tsx
+++ b/web-console/src/utils/index.tsx
@@ -29,7 +29,6 @@ export * from './local-storage-backed-visibility';
export * from './local-storage-keys';
export * from './object-change';
export * from './query-action';
-export * from './query-cursor';
export * from './query-manager';
export * from './query-state';
export * from './sample-query';
diff --git a/web-console/src/utils/query-cursor.ts b/web-console/src/utils/query-cursor.ts
deleted file mode 100644
index 94bbe179389..00000000000
--- a/web-console/src/utils/query-cursor.ts
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import type { SqlBase, SqlQuery } from '@druid-toolkit/query';
-import { L } from '@druid-toolkit/query';
-
-import type { RowColumn } from './general';
-
-export const EMPTY_LITERAL = L('');
-
-const CRAZY_STRING = '$.X.@.X.$';
-const DOT_DOT_DOT_LITERAL = L('...');
-
-export function prettyPrintSql(b: SqlBase): string {
- return b
- .walk(b => {
- if (b === EMPTY_LITERAL) {
- return DOT_DOT_DOT_LITERAL;
- }
- return b;
- })
- .prettyTrim(50)
- .toString();
-}
-
-export function findEmptyLiteralPosition(query: SqlQuery): RowColumn | undefined {
- const subQueryString = query.walk(b => (b === EMPTY_LITERAL ? L(CRAZY_STRING) : b)).toString();
-
- const crazyIndex = subQueryString.indexOf(CRAZY_STRING);
- if (crazyIndex < 0) return;
-
- const prefix = subQueryString.slice(0, crazyIndex);
- const lines = prefix.split(/\n/g);
- const row = lines.length - 1;
- const lastLine = lines[row];
- return {
- row: row,
- column: lastLine.length,
- };
-}
diff --git a/web-console/src/utils/sql.ts b/web-console/src/utils/sql.ts
index 7404ee31372..61cd4c7ed41 100644
--- a/web-console/src/utils/sql.ts
+++ b/web-console/src/utils/sql.ts
@@ -16,6 +16,7 @@
* limitations under the License.
*/
+import type { SqlBase } from '@druid-toolkit/query';
import {
SqlColumn,
SqlExpression,
@@ -28,6 +29,10 @@ import {
import type { RowColumn } from './general';
import { offsetToRowColumn } from './general';
+export function prettyPrintSql(b: SqlBase): string {
+ return b.prettyTrim(50).toString();
+}
+
export function timeFormatToSql(timeFormat: string): SqlExpression | undefined {
switch (timeFormat) {
case 'auto':
diff --git a/web-console/src/utils/types.ts b/web-console/src/utils/types.ts
index ed192eff6e8..d164d46138b 100644
--- a/web-console/src/utils/types.ts
+++ b/web-console/src/utils/types.ts
@@ -89,6 +89,9 @@ export function dataTypeToIcon(dataType: string): IconName {
case 'COMPLEX':
return IconNames.IP_ADDRESS;
+ case 'COMPLEX':
+ return IconNames.DOUBLE_CHEVRON_RIGHT;
+
case 'NULL':
return IconNames.CIRCLE;
diff --git a/web-console/src/views/datasources-view/datasources-view.tsx b/web-console/src/views/datasources-view/datasources-view.tsx
index 570a55a87bc..75541b82999 100644
--- a/web-console/src/views/datasources-view/datasources-view.tsx
+++ b/web-console/src/views/datasources-view/datasources-view.tsx
@@ -383,8 +383,8 @@ export class DatasourcesView extends React.PureComponent<
return `SELECT
${columns.join(',\n')}
FROM sys.segments
-GROUP BY 1
-ORDER BY 1`;
+GROUP BY datasource
+ORDER BY datasource`;
}
static RUNNING_TASK_SQL = `SELECT
diff --git a/web-console/src/views/explore-view/modules/table-react-module.tsx b/web-console/src/views/explore-view/modules/table-react-module.tsx
index 459e28014a8..dabe6217c9b 100644
--- a/web-console/src/views/explore-view/modules/table-react-module.tsx
+++ b/web-console/src/views/explore-view/modules/table-react-module.tsx
@@ -16,12 +16,11 @@
* limitations under the License.
*/
-import type { SqlOrderByExpression } from '@druid-toolkit/query';
+import type { SqlColumn, SqlOrderByExpression } from '@druid-toolkit/query';
import {
C,
F,
SqlCase,
- SqlColumn,
SqlExpression,
SqlFunction,
SqlLiteral,
@@ -80,22 +79,24 @@ function nullableColumn(column: ExpressionMeta) {
}
function nvl(ex: SqlExpression): SqlExpression {
- return SqlFunction.simple('NVL', [ex, NULL_REPLACEMENT]);
+ return SqlFunction.simple('NVL', [ex.cast('VARCHAR'), NULL_REPLACEMENT]);
}
-function nullif(ex: SqlExpression): SqlExpression {
- return SqlFunction.simple('NULLIF', [ex, NULL_REPLACEMENT]);
+function joinEquals(c1: SqlColumn, c2: SqlColumn, nullable: boolean): SqlExpression {
+ return c1.applyIf(nullable, nvl).equal(c2.applyIf(nullable, nvl));
}
function toGroupByExpression(
splitColumn: ExpressionMeta,
- nvlIfNeeded: boolean,
timeBucket: string,
+ compareShiftDuration?: string,
) {
const { expression, sqlType, name } = splitColumn;
return expression
- .applyIf(sqlType === 'TIMESTAMP', e => SqlFunction.simple('TIME_FLOOR', [e, timeBucket]))
- .applyIf(nvlIfNeeded && nullableColumn(splitColumn), nvl)
+ .applyIf(sqlType === 'TIMESTAMP' && compareShiftDuration, e =>
+ F.timeShift(e, compareShiftDuration!, 1),
+ )
+ .applyIf(sqlType === 'TIMESTAMP', e => F.timeFloor(e, timeBucket))
.as(name);
}
@@ -143,16 +144,6 @@ function toShowColumnExpression(
return ex.as(showColumn.name);
}
-function shiftTime(ex: SqlQuery, period: string): SqlQuery {
- return ex.walk(q => {
- if (q instanceof SqlColumn && q.getName() === '__time') {
- return SqlFunction.simple('TIME_SHIFT', [q, period, 1]);
- } else {
- return q;
- }
- }) as SqlQuery;
-}
-
interface QueryAndHints {
query: SqlQuery;
groupHints: string[];
@@ -327,7 +318,7 @@ function TableModule(props: TableModuleProps) {
const mainQuery = getInitQuery(table, where)
.applyForEach(splitColumns, (q, splitColumn) =>
- q.addSelect(toGroupByExpression(splitColumn, hasCompare, timeBucket), {
+ q.addSelect(toGroupByExpression(splitColumn, timeBucket), {
addToGroupBy: 'end',
}),
)
@@ -381,26 +372,20 @@ function TableModule(props: TableModuleProps) {
`compare${i}`,
getInitQuery(table, where)
.applyForEach(splitColumns, (q, splitColumn) =>
- q.addSelect(toGroupByExpression(splitColumn, true, timeBucket), {
+ q.addSelect(toGroupByExpression(splitColumn, timeBucket, comparePeriod), {
addToGroupBy: 'end',
}),
)
.applyForEach(metrics, (q, metric) =>
q.addSelect(metric.expression.as(metric.name)),
- )
- .apply(q => shiftTime(q, comparePeriod)),
+ ),
),
),
),
)
.changeSelectExpressions(
splitColumns
- .map(splitColumn =>
- main
- .column(splitColumn.name)
- .applyIf(nullableColumn(splitColumn), nullif)
- .as(splitColumn.name),
- )
+ .map(splitColumn => main.column(splitColumn.name).as(splitColumn.name))
.concat(
showColumns.map(showColumn => main.column(showColumn.name).as(showColumn.name)),
metrics.map(metric => main.column(metric.name).as(metric.name)),
@@ -432,7 +417,11 @@ function TableModule(props: TableModuleProps) {
T(`compare${i}`),
SqlExpression.and(
...splitColumns.map(splitColumn =>
- main.column(splitColumn.name).equal(T(`compare${i}`).column(splitColumn.name)),
+ joinEquals(
+ main.column(splitColumn.name),
+ T(`compare${i}`).column(splitColumn.name),
+ nullableColumn(splitColumn),
+ ),
),
),
),
diff --git a/web-console/src/views/workbench-view/column-tree/column-tree-menu/string-menu-items/string-menu-items.tsx b/web-console/src/views/workbench-view/column-tree/column-tree-menu/string-menu-items/string-menu-items.tsx
index db566765d3d..a32db7f8b65 100644
--- a/web-console/src/views/workbench-view/column-tree/column-tree-menu/string-menu-items/string-menu-items.tsx
+++ b/web-console/src/views/workbench-view/column-tree/column-tree-menu/string-menu-items/string-menu-items.tsx
@@ -19,11 +19,11 @@
import { MenuItem } from '@blueprintjs/core';
import { IconNames } from '@blueprintjs/icons';
import type { SqlExpression, SqlQuery } from '@druid-toolkit/query';
-import { C, F, N, SqlJoinPart, T } from '@druid-toolkit/query';
+import { C, F, N, SqlJoinPart, SqlPlaceholder, T } from '@druid-toolkit/query';
import type { JSX } from 'react';
import React from 'react';
-import { EMPTY_LITERAL, prettyPrintSql } from '../../../../../utils';
+import { prettyPrintSql } from '../../../../../utils';
import { getJoinColumns } from '../../column-tree';
export interface StringMenuItemsProps {
@@ -53,9 +53,9 @@ export const StringMenuItems = React.memo(function StringMenuItems(props: String
return (
);
}
@@ -136,7 +136,7 @@ export const StringMenuItems = React.memo(function StringMenuItems(props: String