diff --git a/internal/explain/expressions.go b/internal/explain/expressions.go index 74604df541..f3a3b931b3 100644 --- a/internal/explain/expressions.go +++ b/internal/explain/expressions.go @@ -418,22 +418,20 @@ func explainAliasedExpr(sb *strings.Builder, n *ast.AliasedExpr, depth int) { } case *ast.UnaryExpr: // Handle negated numeric literals - output as Literal instead of Function negate - // For integers, only do this in subquery context (ClickHouse behavior) + // For aliased expressions, ClickHouse always shows negated integers as Literal Int64_-N // For floats (especially inf/nan), always do this if e.Op == "-" { if lit, ok := e.Operand.(*ast.Literal); ok { switch lit.Type { case ast.LiteralInteger: - // Only convert to literal in subquery context - if inSubqueryContext { - switch val := lit.Value.(type) { - case int64: - fmt.Fprintf(sb, "%sLiteral Int64_%d (alias %s)\n", indent, -val, escapeAlias(n.Alias)) - return - case uint64: - fmt.Fprintf(sb, "%sLiteral Int64_-%d (alias %s)\n", indent, val, escapeAlias(n.Alias)) - return - } + // Always convert to literal for aliased expressions + switch val := lit.Value.(type) { + case int64: + fmt.Fprintf(sb, "%sLiteral Int64_%d (alias %s)\n", indent, -val, escapeAlias(n.Alias)) + return + case uint64: + fmt.Fprintf(sb, "%sLiteral Int64_-%d (alias %s)\n", indent, val, escapeAlias(n.Alias)) + return } case ast.LiteralFloat: // Always convert negated floats to literals (especially for -inf, -nan) diff --git a/internal/explain/tables.go b/internal/explain/tables.go index 9347672bfe..065100d274 100644 --- a/internal/explain/tables.go +++ b/internal/explain/tables.go @@ -78,11 +78,53 @@ func formatSampleRatio(sb *strings.Builder, expr ast.Expression) { formatSampleRatioOperand(sb, binExpr.Left) sb.WriteString(" / ") formatSampleRatioOperand(sb, binExpr.Right) + } else if lit, ok := expr.(*ast.Literal); ok && lit.Type == ast.LiteralFloat { + // Convert float to fraction if it's a simple ratio + if v, ok := lit.Value.(float64); ok { + num, den := floatToFraction(v) + if den > 1 { + fmt.Fprintf(sb, "%d / %d", num, den) + return + } + } + formatSampleRatioOperand(sb, expr) } else { formatSampleRatioOperand(sb, expr) } } +// floatToFraction converts a float to a simple fraction (numerator, denominator). +// Returns (num, 1) if no simple fraction representation is found. +func floatToFraction(f float64) (int64, int64) { + // Handle common sample ratios + // Try denominators from 2 to 1000 + for den := int64(2); den <= 1000; den++ { + num := int64(f * float64(den)) + // Check if this gives us back the original value (within floating point tolerance) + if float64(num)/float64(den) == f { + // Find GCD to simplify the fraction + g := gcd(num, den) + return num / g, den / g + } + } + // No simple fraction found, return as integer if possible + if f == float64(int64(f)) { + return int64(f), 1 + } + return 0, 1 +} + +// gcd calculates the greatest common divisor of two integers +func gcd(a, b int64) int64 { + if a < 0 { + a = -a + } + for b != 0 { + a, b = b, a%b + } + return a +} + func formatSampleRatioOperand(sb *strings.Builder, expr ast.Expression) { if lit, ok := expr.(*ast.Literal); ok { switch v := lit.Value.(type) { diff --git a/parser/testdata/00056_view/metadata.json b/parser/testdata/00056_view/metadata.json index 3a2014fe8c..0967ef424b 100644 --- a/parser/testdata/00056_view/metadata.json +++ b/parser/testdata/00056_view/metadata.json @@ -1 +1 @@ -{"explain_todo":{"stmt6":true}} +{} diff --git a/parser/testdata/00276_sample/metadata.json b/parser/testdata/00276_sample/metadata.json index 451eb06303..c19358090d 100644 --- a/parser/testdata/00276_sample/metadata.json +++ b/parser/testdata/00276_sample/metadata.json @@ -1,6 +1,5 @@ { "explain_todo": { - "stmt13": true, "stmt14": true, "stmt15": true, "stmt16": true, @@ -15,7 +14,6 @@ "stmt32": true, "stmt33": true, "stmt34": true, - "stmt39": true, - "stmt9": true + "stmt39": true } } diff --git a/parser/testdata/00578_merge_table_sampling/metadata.json b/parser/testdata/00578_merge_table_sampling/metadata.json index 22c08a6999..92e84e943a 100644 --- a/parser/testdata/00578_merge_table_sampling/metadata.json +++ b/parser/testdata/00578_merge_table_sampling/metadata.json @@ -1 +1,6 @@ -{"explain_todo":{"stmt10":true,"stmt5":true,"stmt8":true,"stmt9":true}} +{ + "explain_todo": { + "stmt8": true, + "stmt9": true + } +} diff --git a/parser/testdata/01430_modify_sample_by_zookeeper_long/metadata.json b/parser/testdata/01430_modify_sample_by_zookeeper_long/metadata.json index aaa7295255..73b86cc257 100644 --- a/parser/testdata/01430_modify_sample_by_zookeeper_long/metadata.json +++ b/parser/testdata/01430_modify_sample_by_zookeeper_long/metadata.json @@ -1,16 +1,9 @@ { "explain_todo": { - "stmt11": true, "stmt12": true, - "stmt13": true, - "stmt16": true, "stmt17": true, "stmt18": true, - "stmt19": true, - "stmt22": true, "stmt25": true, - "stmt6": true, - "stmt7": true, - "stmt8": true + "stmt7": true } }