Web console: tidy up stage UI (#13615)

* show the right info

* sort indicator

* nicer marker

* move error icon
This commit is contained in:
Vadim Ogievetsky 2022-12-22 12:52:16 -08:00 committed by GitHub
parent 78ae0b7533
commit 8773d619a2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 69 additions and 29 deletions

View File

@ -65,9 +65,11 @@ export class WorkbenchQueryPart {
const matchInsertReplaceIndex = queryFragment.match(/(?:INSERT|REPLACE)\s+INTO/i)?.index;
if (typeof matchInsertReplaceIndex !== 'number') return;
const matchEnd = queryFragment.match(/\b(?:SELECT|WITH)\b|$/i);
const queryStartingWithInsertOrReplace = queryFragment.substring(matchInsertReplaceIndex);
const matchEnd = queryStartingWithInsertOrReplace.match(/\b(?:SELECT|WITH)\b|$/i);
const fragmentQuery = SqlQuery.maybeParse(
queryFragment.substring(matchInsertReplaceIndex, matchEnd?.index) + ' SELECT * FROM t',
queryStartingWithInsertOrReplace.substring(0, matchEnd?.index) + ' SELECT * FROM t',
);
if (!fragmentQuery) return;

View File

@ -507,6 +507,19 @@ describe('WorkbenchQuery', () => {
expect(workbenchQuery.getIngestDatasource()).toEqual('trips2');
expect(workbenchQuery.changeEngine('sql-native').getIngestDatasource()).toBeUndefined();
});
it('works with REPLACE (unparsable with comment at start)', () => {
const sql = sane`
-- Hello world SELECT
REPLACE INTO trips2 OVERWRITE ALL
WITH kttm_data AS (SELECT *
`;
const workbenchQuery = WorkbenchQuery.blank().changeQueryString(sql);
expect(workbenchQuery.getIngestDatasource()).toEqual('trips2');
expect(workbenchQuery.changeEngine('sql-native').getIngestDatasource()).toBeUndefined();
});
});
describe('#extractCteHelpers', () => {

View File

@ -666,11 +666,9 @@ END AS "time_span"`,
switch (v?.type) {
case 'range': {
const dimensions = v.dimensions || [];
const dimensions: string[] = v.dimensions || [];
const formatEdge = (values: string[]) =>
values
.map((x, i) => formatRangeDimensionValue(dimensions[i] || `d${i}`, x))
.join('; ');
dimensions.map((d, i) => formatRangeDimensionValue(d, values[i])).join('; ');
return (
<TableClickableCell

View File

@ -268,6 +268,22 @@ export class ColumnTree extends React.PureComponent<ColumnTreeProps, ColumnTreeS
);
}}
/>
<MenuItem
icon={IconNames.FULLSCREEN}
text={`SELECT MIN(__time), MAX(__time) FROM ${tableName}`}
onClick={() => {
onQueryChange(
getQueryOnTable()
.changeSelectExpressions([
F.min(C('__time')).as('min_time'),
F.max(C('__time')).as('max_time'),
])
.changeGroupByExpressions([])
.changeWhereExpression(getWhere(true)),
true,
);
}}
/>
{parsedQuery && parsedQuery.getFirstTableName() !== tableName && (
<MenuItem
icon={IconNames.EXCHANGE}

View File

@ -41,6 +41,17 @@
opacity: 0.6;
}
.sort-marker {
font-style: italic;
opacity: 0.6;
}
.error-warning {
position: absolute;
top: 7px;
right: 3px;
}
.counter-spacer {
position: relative;
height: 5px;

View File

@ -402,6 +402,7 @@ ${title} uncompressed size: ${formatBytesCompact(
const shuffleRows = stages.getTotalCounterForStage(stage, 'shuffle', 'rows');
const sortProgress = stages.getSortProgressForStage(stage);
const showSortedPercent = 0 < sortProgress && sortProgress < 1;
const title = stages.getStageCounterTitle(stage, 'shuffle');
return (
<div
@ -413,10 +414,9 @@ ${title} uncompressed size: ${formatBytesCompact(
stages.getTotalCounterForStage(stage, 'shuffle', 'bytes'),
)} ${NOT_SIZE_ON_DISK}`}
>
<BracedText text={shuffleRows ? formatRows(shuffleRows) : ''} braces={rowsValues} /> &nbsp;{' '}
{0 < sortProgress && sortProgress < 1 && (
<> &nbsp;{` ${formatPercent(sortProgress)} sorted`}</>
)}
{Boolean(shuffleRows) && <BracedText text={formatRows(shuffleRows)} braces={rowsValues} />}
{Boolean(shuffleRows && showSortedPercent) && <>&nbsp; : &nbsp;</>}
{showSortedPercent && `${formatPercent(sortProgress)} sorted`}
</div>
);
}
@ -449,28 +449,28 @@ ${title} uncompressed size: ${formatBytesCompact(
<span className="stage">{`Stage${stage.stageNumber}`}</span>
</div>
<div>{stage.definition.processor.type}</div>
{stage.sort && <div className="sort-marker">(with sort)</div>}
{(myError || warnings > 0) && (
<div>
<div className="error-warning">
{myError && (
<>
<Tooltip2
content={
<div>
{(error.error.errorCode ? `${error.error.errorCode}: ` : '') +
error.error.errorMessage}
</div>
}
>
<Button
minimal
small
icon={IconNames.ERROR}
intent={Intent.DANGER}
onClick={onErrorClick}
/>
</Tooltip2>{' '}
</>
<Tooltip2
content={
<div>
{(error.error.errorCode ? `${error.error.errorCode}: ` : '') +
error.error.errorMessage}
</div>
}
>
<Button
minimal
small
icon={IconNames.ERROR}
intent={Intent.DANGER}
onClick={onErrorClick}
/>
</Tooltip2>
)}
{myError && warnings > 0 && ' '}
{warnings > 0 && (
<Tooltip2
content={