mirror of
https://github.com/hyperdxio/hyperdx
synced 2026-04-21 13:37:15 +00:00
Fix heatmap time-bucket SQL alias handling
Co-authored-by: Alex Fedotyev <alex-fedotyev@users.noreply.github.com>
This commit is contained in:
parent
c1c38adb4b
commit
8e75a0a07d
4 changed files with 130 additions and 15 deletions
|
|
@ -14,7 +14,7 @@ exports[`renderChartConfig HAVING clause should not render HAVING clause when no
|
|||
|
||||
exports[`renderChartConfig HAVING clause should render HAVING clause with SQL language 1`] = `"SELECT count(),severity FROM default.logs WHERE (timestamp >= fromUnixTimestamp64Milli(1739318400000) AND timestamp <= fromUnixTimestamp64Milli(1739491200000)) GROUP BY severity HAVING count(*) > 100 SETTINGS optimize_read_in_order = 0, cast_keep_nullable = 1, additional_result_filter = 'x != 2', count_distinct_implementation = 'uniqCombined64', async_insert_busy_timeout_min_ms = 20000"`;
|
||||
|
||||
exports[`renderChartConfig HAVING clause should render HAVING clause with granularity and groupBy 1`] = `"SELECT count(),event_type,toStartOfInterval(toDateTime(timestamp), INTERVAL 5 minute) AS \`__hdx_time_bucket\` FROM default.events WHERE (timestamp >= fromUnixTimestamp64Milli(1739318400000) AND timestamp <= fromUnixTimestamp64Milli(1739491200000)) GROUP BY event_type,toStartOfInterval(toDateTime(timestamp), INTERVAL 5 minute) AS \`__hdx_time_bucket\` HAVING count(*) > 50 ORDER BY toStartOfInterval(toDateTime(timestamp), INTERVAL 5 minute) AS \`__hdx_time_bucket\` SETTINGS optimize_read_in_order = 0, cast_keep_nullable = 1, additional_result_filter = 'x != 2', count_distinct_implementation = 'uniqCombined64', async_insert_busy_timeout_min_ms = 20000"`;
|
||||
exports[`renderChartConfig HAVING clause should render HAVING clause with granularity and groupBy 1`] = `"SELECT count(),event_type,toStartOfInterval(toDateTime(timestamp), INTERVAL 5 minute) AS \`__hdx_time_bucket\` FROM default.events WHERE (timestamp >= fromUnixTimestamp64Milli(1739318400000) AND timestamp <= fromUnixTimestamp64Milli(1739491200000)) GROUP BY event_type,toStartOfInterval(toDateTime(timestamp), INTERVAL 5 minute) HAVING count(*) > 50 ORDER BY toStartOfInterval(toDateTime(timestamp), INTERVAL 5 minute) SETTINGS optimize_read_in_order = 0, cast_keep_nullable = 1, additional_result_filter = 'x != 2', count_distinct_implementation = 'uniqCombined64', async_insert_busy_timeout_min_ms = 20000"`;
|
||||
|
||||
exports[`renderChartConfig HAVING clause should render HAVING clause with multiple conditions 1`] = `
|
||||
"SELECT avg(
|
||||
|
|
@ -588,7 +588,7 @@ exports[`renderChartConfig k8s semantic convention migrations should generate SQ
|
|||
ORDER BY AttributesHash, \`__hdx_time_bucket2\`
|
||||
) SELECT max(
|
||||
toFloat64OrDefault(toString(Rate))
|
||||
) AS \\"Value\\",toStartOfInterval(toDateTime(\`__hdx_time_bucket2\`), INTERVAL 5 minute) AS \`__hdx_time_bucket\` FROM Bucketed WHERE (\`__hdx_time_bucket2\` >= fromUnixTimestamp64Milli(1739318400000) AND \`__hdx_time_bucket2\` <= fromUnixTimestamp64Milli(1765670400000)) GROUP BY toStartOfInterval(toDateTime(\`__hdx_time_bucket2\`), INTERVAL 5 minute) AS \`__hdx_time_bucket\` ORDER BY toStartOfInterval(toDateTime(\`__hdx_time_bucket2\`), INTERVAL 5 minute) AS \`__hdx_time_bucket\` LIMIT 10 SETTINGS optimize_read_in_order = 0, cast_keep_nullable = 1, additional_result_filter = 'x != 2', count_distinct_implementation = 'uniqCombined64', async_insert_busy_timeout_min_ms = 20000"
|
||||
) AS \\"Value\\",toStartOfInterval(toDateTime(\`__hdx_time_bucket2\`), INTERVAL 5 minute) AS \`__hdx_time_bucket\` FROM Bucketed WHERE (\`__hdx_time_bucket2\` >= fromUnixTimestamp64Milli(1739318400000) AND \`__hdx_time_bucket2\` <= fromUnixTimestamp64Milli(1765670400000)) GROUP BY toStartOfInterval(toDateTime(\`__hdx_time_bucket2\`), INTERVAL 5 minute) ORDER BY toStartOfInterval(toDateTime(\`__hdx_time_bucket2\`), INTERVAL 5 minute) LIMIT 10 SETTINGS optimize_read_in_order = 0, cast_keep_nullable = 1, additional_result_filter = 'x != 2', count_distinct_implementation = 'uniqCombined64', async_insert_busy_timeout_min_ms = 20000"
|
||||
`;
|
||||
|
||||
exports[`renderChartConfig k8s semantic convention migrations should generate SQL with metricNameSql for k8s.pod.cpu.utilization gauge metric 1`] = `
|
||||
|
|
@ -621,7 +621,7 @@ exports[`renderChartConfig k8s semantic convention migrations should generate SQ
|
|||
ORDER BY AttributesHash, __hdx_time_bucket2
|
||||
) SELECT avg(
|
||||
toFloat64OrDefault(toString(LastValue))
|
||||
),toStartOfInterval(toDateTime(__hdx_time_bucket2), INTERVAL 1 minute) AS \`__hdx_time_bucket\` FROM Bucketed WHERE (__hdx_time_bucket2 >= fromUnixTimestamp64Milli(1739318400000) AND __hdx_time_bucket2 <= fromUnixTimestamp64Milli(1765670400000)) GROUP BY toStartOfInterval(toDateTime(__hdx_time_bucket2), INTERVAL 1 minute) AS \`__hdx_time_bucket\` ORDER BY toStartOfInterval(toDateTime(__hdx_time_bucket2), INTERVAL 1 minute) AS \`__hdx_time_bucket\` LIMIT 10 SETTINGS short_circuit_function_evaluation = 'force_enable', optimize_read_in_order = 0, cast_keep_nullable = 1, additional_result_filter = 'x != 2', count_distinct_implementation = 'uniqCombined64', async_insert_busy_timeout_min_ms = 20000"
|
||||
),toStartOfInterval(toDateTime(__hdx_time_bucket2), INTERVAL 1 minute) AS \`__hdx_time_bucket\` FROM Bucketed WHERE (__hdx_time_bucket2 >= fromUnixTimestamp64Milli(1739318400000) AND __hdx_time_bucket2 <= fromUnixTimestamp64Milli(1765670400000)) GROUP BY toStartOfInterval(toDateTime(__hdx_time_bucket2), INTERVAL 1 minute) ORDER BY toStartOfInterval(toDateTime(__hdx_time_bucket2), INTERVAL 1 minute) LIMIT 10 SETTINGS short_circuit_function_evaluation = 'force_enable', optimize_read_in_order = 0, cast_keep_nullable = 1, additional_result_filter = 'x != 2', count_distinct_implementation = 'uniqCombined64', async_insert_busy_timeout_min_ms = 20000"
|
||||
`;
|
||||
|
||||
exports[`renderChartConfig k8s semantic convention migrations should handle metrics without metricNameSql (backward compatibility) 1`] = `
|
||||
|
|
@ -654,7 +654,7 @@ exports[`renderChartConfig k8s semantic convention migrations should handle metr
|
|||
ORDER BY AttributesHash, __hdx_time_bucket2
|
||||
) SELECT avg(
|
||||
toFloat64OrDefault(toString(LastValue))
|
||||
),toStartOfInterval(toDateTime(__hdx_time_bucket2), INTERVAL 1 minute) AS \`__hdx_time_bucket\` FROM Bucketed WHERE (__hdx_time_bucket2 >= fromUnixTimestamp64Milli(1739318400000) AND __hdx_time_bucket2 <= fromUnixTimestamp64Milli(1765670400000)) GROUP BY toStartOfInterval(toDateTime(__hdx_time_bucket2), INTERVAL 1 minute) AS \`__hdx_time_bucket\` ORDER BY toStartOfInterval(toDateTime(__hdx_time_bucket2), INTERVAL 1 minute) AS \`__hdx_time_bucket\` LIMIT 10 SETTINGS short_circuit_function_evaluation = 'force_enable', optimize_read_in_order = 0, cast_keep_nullable = 1, additional_result_filter = 'x != 2', count_distinct_implementation = 'uniqCombined64', async_insert_busy_timeout_min_ms = 20000"
|
||||
),toStartOfInterval(toDateTime(__hdx_time_bucket2), INTERVAL 1 minute) AS \`__hdx_time_bucket\` FROM Bucketed WHERE (__hdx_time_bucket2 >= fromUnixTimestamp64Milli(1739318400000) AND __hdx_time_bucket2 <= fromUnixTimestamp64Milli(1765670400000)) GROUP BY toStartOfInterval(toDateTime(__hdx_time_bucket2), INTERVAL 1 minute) ORDER BY toStartOfInterval(toDateTime(__hdx_time_bucket2), INTERVAL 1 minute) LIMIT 10 SETTINGS short_circuit_function_evaluation = 'force_enable', optimize_read_in_order = 0, cast_keep_nullable = 1, additional_result_filter = 'x != 2', count_distinct_implementation = 'uniqCombined64', async_insert_busy_timeout_min_ms = 20000"
|
||||
`;
|
||||
|
||||
exports[`renderChartConfig sample-weighted aggregations should handle complex sampleWeightExpression like SpanAttributes map access 1`] = `"SELECT sum(greatest(toUInt64OrZero(toString(SpanAttributes['SampleRate'])), 1)) FROM default.otel_traces WHERE (Timestamp >= fromUnixTimestamp64Milli(1739318400000) AND Timestamp <= fromUnixTimestamp64Milli(1739491200000)) SETTINGS optimize_read_in_order = 0, cast_keep_nullable = 1, additional_result_filter = 'x != 2', count_distinct_implementation = 'uniqCombined64', async_insert_busy_timeout_min_ms = 20000"`;
|
||||
|
|
@ -719,7 +719,7 @@ exports[`renderChartConfig should generate sql for a single gauge metric 1`] = `
|
|||
FROM Source
|
||||
GROUP BY AttributesHash, __hdx_time_bucket2
|
||||
ORDER BY AttributesHash, __hdx_time_bucket2
|
||||
) SELECT quantile(0.95)(toFloat64OrDefault(toString(LastValue))),toStartOfInterval(toDateTime(__hdx_time_bucket2), INTERVAL 1 minute) AS \`__hdx_time_bucket\` FROM Bucketed WHERE (__hdx_time_bucket2 >= fromUnixTimestamp64Milli(1739318400000) AND __hdx_time_bucket2 <= fromUnixTimestamp64Milli(1765670400000)) GROUP BY toStartOfInterval(toDateTime(__hdx_time_bucket2), INTERVAL 1 minute) AS \`__hdx_time_bucket\` ORDER BY toStartOfInterval(toDateTime(__hdx_time_bucket2), INTERVAL 1 minute) AS \`__hdx_time_bucket\` LIMIT 10 SETTINGS short_circuit_function_evaluation = 'force_enable', optimize_read_in_order = 0, cast_keep_nullable = 1, additional_result_filter = 'x != 2', count_distinct_implementation = 'uniqCombined64', async_insert_busy_timeout_min_ms = 20000"
|
||||
) SELECT quantile(0.95)(toFloat64OrDefault(toString(LastValue))),toStartOfInterval(toDateTime(__hdx_time_bucket2), INTERVAL 1 minute) AS \`__hdx_time_bucket\` FROM Bucketed WHERE (__hdx_time_bucket2 >= fromUnixTimestamp64Milli(1739318400000) AND __hdx_time_bucket2 <= fromUnixTimestamp64Milli(1765670400000)) GROUP BY toStartOfInterval(toDateTime(__hdx_time_bucket2), INTERVAL 1 minute) ORDER BY toStartOfInterval(toDateTime(__hdx_time_bucket2), INTERVAL 1 minute) LIMIT 10 SETTINGS short_circuit_function_evaluation = 'force_enable', optimize_read_in_order = 0, cast_keep_nullable = 1, additional_result_filter = 'x != 2', count_distinct_implementation = 'uniqCombined64', async_insert_busy_timeout_min_ms = 20000"
|
||||
`;
|
||||
|
||||
exports[`renderChartConfig should generate sql for a single gauge metric with a delta() function applied 1`] = `
|
||||
|
|
@ -752,7 +752,7 @@ exports[`renderChartConfig should generate sql for a single gauge metric with a
|
|||
ORDER BY AttributesHash, __hdx_time_bucket2
|
||||
) SELECT max(
|
||||
toFloat64OrDefault(toString(LastValue))
|
||||
),toStartOfInterval(toDateTime(__hdx_time_bucket2), INTERVAL 1 minute) AS \`__hdx_time_bucket\` FROM Bucketed WHERE (__hdx_time_bucket2 >= fromUnixTimestamp64Milli(1739318400000) AND __hdx_time_bucket2 <= fromUnixTimestamp64Milli(1765670400000)) GROUP BY toStartOfInterval(toDateTime(__hdx_time_bucket2), INTERVAL 1 minute) AS \`__hdx_time_bucket\` ORDER BY toStartOfInterval(toDateTime(__hdx_time_bucket2), INTERVAL 1 minute) AS \`__hdx_time_bucket\` LIMIT 10 SETTINGS short_circuit_function_evaluation = 'force_enable', optimize_read_in_order = 0, cast_keep_nullable = 1, additional_result_filter = 'x != 2', count_distinct_implementation = 'uniqCombined64', async_insert_busy_timeout_min_ms = 20000"
|
||||
),toStartOfInterval(toDateTime(__hdx_time_bucket2), INTERVAL 1 minute) AS \`__hdx_time_bucket\` FROM Bucketed WHERE (__hdx_time_bucket2 >= fromUnixTimestamp64Milli(1739318400000) AND __hdx_time_bucket2 <= fromUnixTimestamp64Milli(1765670400000)) GROUP BY toStartOfInterval(toDateTime(__hdx_time_bucket2), INTERVAL 1 minute) ORDER BY toStartOfInterval(toDateTime(__hdx_time_bucket2), INTERVAL 1 minute) LIMIT 10 SETTINGS short_circuit_function_evaluation = 'force_enable', optimize_read_in_order = 0, cast_keep_nullable = 1, additional_result_filter = 'x != 2', count_distinct_implementation = 'uniqCombined64', async_insert_busy_timeout_min_ms = 20000"
|
||||
`;
|
||||
|
||||
exports[`renderChartConfig should generate sql for a single sum metric 1`] = `
|
||||
|
|
@ -798,7 +798,7 @@ exports[`renderChartConfig should generate sql for a single sum metric 1`] = `
|
|||
ORDER BY AttributesHash, \`__hdx_time_bucket2\`
|
||||
) SELECT avg(
|
||||
toFloat64OrDefault(toString(Rate))
|
||||
) AS \\"Value\\",toStartOfInterval(toDateTime(\`__hdx_time_bucket2\`), INTERVAL 5 minute) AS \`__hdx_time_bucket\` FROM Bucketed WHERE (\`__hdx_time_bucket2\` >= fromUnixTimestamp64Milli(1739318400000) AND \`__hdx_time_bucket2\` <= fromUnixTimestamp64Milli(1765670400000)) GROUP BY toStartOfInterval(toDateTime(\`__hdx_time_bucket2\`), INTERVAL 5 minute) AS \`__hdx_time_bucket\` ORDER BY toStartOfInterval(toDateTime(\`__hdx_time_bucket2\`), INTERVAL 5 minute) AS \`__hdx_time_bucket\` LIMIT 10 SETTINGS optimize_read_in_order = 0, cast_keep_nullable = 1, additional_result_filter = 'x != 2', count_distinct_implementation = 'uniqCombined64', async_insert_busy_timeout_min_ms = 20000"
|
||||
) AS \\"Value\\",toStartOfInterval(toDateTime(\`__hdx_time_bucket2\`), INTERVAL 5 minute) AS \`__hdx_time_bucket\` FROM Bucketed WHERE (\`__hdx_time_bucket2\` >= fromUnixTimestamp64Milli(1739318400000) AND \`__hdx_time_bucket2\` <= fromUnixTimestamp64Milli(1765670400000)) GROUP BY toStartOfInterval(toDateTime(\`__hdx_time_bucket2\`), INTERVAL 5 minute) ORDER BY toStartOfInterval(toDateTime(\`__hdx_time_bucket2\`), INTERVAL 5 minute) LIMIT 10 SETTINGS optimize_read_in_order = 0, cast_keep_nullable = 1, additional_result_filter = 'x != 2', count_distinct_implementation = 'uniqCombined64', async_insert_busy_timeout_min_ms = 20000"
|
||||
`;
|
||||
|
||||
exports[`renderChartConfig should not generate invalid SQL when primary key wraps toStartOfInterval 1`] = `"SELECT timestamp, cluster_id, service_id FROM default.http_request_logs WHERE (timestamp >= fromUnixTimestamp64Milli(1739319154000) AND timestamp <= fromUnixTimestamp64Milli(1739491954000)) LIMIT 200 OFFSET 0 SETTINGS optimize_read_in_order = 0, cast_keep_nullable = 1, additional_result_filter = 'x != 2', count_distinct_implementation = 'uniqCombined64', async_insert_busy_timeout_min_ms = 20000"`;
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ import {
|
|||
replaceJsonExpressions,
|
||||
splitAndTrimCSV,
|
||||
splitAndTrimWithBracket,
|
||||
stripTrailingAlias,
|
||||
} from '../core/utils';
|
||||
|
||||
describe('utils', () => {
|
||||
|
|
@ -252,6 +253,26 @@ describe('utils', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('stripTrailingAlias', () => {
|
||||
it('removes trailing AS alias from top-level expression', () => {
|
||||
expect(
|
||||
stripTrailingAlias(
|
||||
'toStartOfInterval(toDateTime(TimestampTime), INTERVAL 1 minute) AS `__hdx_time_bucket`',
|
||||
),
|
||||
).toBe('toStartOfInterval(toDateTime(TimestampTime), INTERVAL 1 minute)');
|
||||
});
|
||||
|
||||
it('preserves expressions that have no trailing alias', () => {
|
||||
expect(stripTrailingAlias('TimestampTime')).toBe('TimestampTime');
|
||||
});
|
||||
|
||||
it('does not remove nested aliases inside parentheses', () => {
|
||||
expect(
|
||||
stripTrailingAlias('coalesce((SELECT x AS y), 0) AS final_value'),
|
||||
).toBe('coalesce((SELECT x AS y), 0)');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getFirstOrderingItem', () => {
|
||||
it('should return undefined for undefined input', () => {
|
||||
expect(getFirstOrderingItem(undefined)).toBeUndefined();
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import {
|
|||
optimizeTimestampValueExpression,
|
||||
parseToStartOfFunction,
|
||||
splitAndTrimWithBracket,
|
||||
stripTrailingAlias,
|
||||
} from '@/core/utils';
|
||||
import { isBuilderChartConfig, isRawSqlChartConfig } from '@/guards';
|
||||
import { replaceMacros } from '@/macros';
|
||||
|
|
@ -614,9 +615,30 @@ function timeBucketExpr({
|
|||
timestampValueExpression: string;
|
||||
dateRange?: [Date, Date];
|
||||
alias?: string;
|
||||
}) {
|
||||
const bucketExpression = timeBucketExprSql({
|
||||
interval,
|
||||
timestampValueExpression,
|
||||
dateRange,
|
||||
});
|
||||
return chSql`${bucketExpression} AS \`${{
|
||||
UNSAFE_RAW_SQL: alias,
|
||||
}}\``;
|
||||
}
|
||||
|
||||
function timeBucketExprSql({
|
||||
interval,
|
||||
timestampValueExpression,
|
||||
dateRange,
|
||||
}: {
|
||||
interval: SQLInterval | 'auto';
|
||||
timestampValueExpression: string;
|
||||
dateRange?: [Date, Date];
|
||||
}) {
|
||||
const unsafeTimestampValueExpression = {
|
||||
UNSAFE_RAW_SQL: getFirstTimestampValueExpression(timestampValueExpression),
|
||||
UNSAFE_RAW_SQL: stripTrailingAlias(
|
||||
getFirstTimestampValueExpression(timestampValueExpression),
|
||||
),
|
||||
};
|
||||
const unsafeInterval = {
|
||||
UNSAFE_RAW_SQL:
|
||||
|
|
@ -625,9 +647,7 @@ function timeBucketExpr({
|
|||
: interval,
|
||||
};
|
||||
|
||||
return chSql`toStartOfInterval(toDateTime(${unsafeTimestampValueExpression}), INTERVAL ${unsafeInterval}) AS \`${{
|
||||
UNSAFE_RAW_SQL: alias,
|
||||
}}\``;
|
||||
return chSql`toStartOfInterval(toDateTime(${unsafeTimestampValueExpression}), INTERVAL ${unsafeInterval})`;
|
||||
}
|
||||
|
||||
export async function timeFilterExpr({
|
||||
|
|
@ -680,7 +700,7 @@ export async function timeFilterExpr({
|
|||
|
||||
const whereExprs = await Promise.all(
|
||||
valueExpressions.map(async expr => {
|
||||
const col = expr.trim();
|
||||
const col = stripTrailingAlias(expr.trim());
|
||||
|
||||
// If the expression includes a toStartOf...(...) function, the RHS of the
|
||||
// timestamp comparison must also have the same function
|
||||
|
|
@ -976,7 +996,7 @@ async function renderGroupBy(
|
|||
? await renderSelectList(chartConfig.groupBy, chartConfig, metadata)
|
||||
: [],
|
||||
isUsingGranularity(chartConfig)
|
||||
? timeBucketExpr({
|
||||
? timeBucketExprSql({
|
||||
interval: chartConfig.granularity,
|
||||
timestampValueExpression: chartConfig.timestampValueExpression,
|
||||
dateRange: chartConfig.dateRange,
|
||||
|
|
@ -1016,7 +1036,7 @@ function renderOrderBy(
|
|||
return concatChSql(
|
||||
',',
|
||||
isIncludingTimeBucket
|
||||
? timeBucketExpr({
|
||||
? timeBucketExprSql({
|
||||
interval: chartConfig.granularity,
|
||||
timestampValueExpression: chartConfig.timestampValueExpression,
|
||||
dateRange: chartConfig.dateRange,
|
||||
|
|
|
|||
|
|
@ -96,7 +96,81 @@ export function splitAndTrimWithBracket(input: string): string[] {
|
|||
// If a user specifies a timestampValueExpression with multiple columns,
|
||||
// this will return the first one. We'll want to refine this over time
|
||||
export function getFirstTimestampValueExpression(valueExpression: string) {
|
||||
return splitAndTrimWithBracket(valueExpression)[0];
|
||||
const firstExpression = splitAndTrimWithBracket(valueExpression)[0];
|
||||
return firstExpression
|
||||
? stripTrailingAlias(firstExpression)
|
||||
: firstExpression;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a trailing SQL alias from a single expression, e.g.
|
||||
* `toStartOfInterval(ts, INTERVAL 1 minute) AS bucket` -> `toStartOfInterval(ts, INTERVAL 1 minute)`.
|
||||
* Keeps interior aliases untouched by only stripping the last top-level alias token.
|
||||
*/
|
||||
export function stripTrailingAlias(valueExpression: string): string {
|
||||
const expression = valueExpression.trim();
|
||||
if (!expression) return expression;
|
||||
|
||||
let parenCount = 0;
|
||||
let squareCount = 0;
|
||||
let inSingleQuote = false;
|
||||
let inDoubleQuote = false;
|
||||
let inBacktick = false;
|
||||
let aliasSplitIndex: number | undefined;
|
||||
|
||||
for (let i = 0; i < expression.length; i++) {
|
||||
const c = expression[i];
|
||||
const prev = i > 0 ? expression[i - 1] : '';
|
||||
|
||||
if (c === "'" && !inDoubleQuote && !inBacktick && prev !== '\\') {
|
||||
inSingleQuote = !inSingleQuote;
|
||||
continue;
|
||||
}
|
||||
if (c === '"' && !inSingleQuote && !inBacktick && prev !== '\\') {
|
||||
inDoubleQuote = !inDoubleQuote;
|
||||
continue;
|
||||
}
|
||||
if (c === '`' && !inSingleQuote && !inDoubleQuote && prev !== '\\') {
|
||||
inBacktick = !inBacktick;
|
||||
continue;
|
||||
}
|
||||
if (inSingleQuote || inDoubleQuote || inBacktick) continue;
|
||||
|
||||
if (c === '(') {
|
||||
parenCount++;
|
||||
continue;
|
||||
}
|
||||
if (c === ')') {
|
||||
parenCount--;
|
||||
continue;
|
||||
}
|
||||
if (c === '[') {
|
||||
squareCount++;
|
||||
continue;
|
||||
}
|
||||
if (c === ']') {
|
||||
squareCount--;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (parenCount === 0 && squareCount === 0) {
|
||||
if (/\s/.test(c)) {
|
||||
let j = i;
|
||||
while (j < expression.length && /\s/.test(expression[j])) j++;
|
||||
if (
|
||||
expression.slice(j, j + 2).toUpperCase() === 'AS' &&
|
||||
j + 2 < expression.length &&
|
||||
/\s/.test(expression[j + 2])
|
||||
) {
|
||||
aliasSplitIndex = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return aliasSplitIndex == null
|
||||
? expression
|
||||
: expression.slice(0, aliasSplitIndex).trim();
|
||||
}
|
||||
|
||||
/** Returns true if the given expression is a JSON expression, eg. `col.key.nestedKey` or "json_col"."key" */
|
||||
|
|
|
|||
Loading…
Reference in a new issue