Skip to content

Commit 954dbc3

Browse files
akoclaude
andcommitted
Fix false positive OQL type inference for CASE expressions
inferCaseType had two issues producing wrong types: 1. ELSE clause with bare integer literal (e.g., ELSE 0) was treated as definitive type even when THEN branch couldn't be inferred — bare integers in ELSE are ambiguous fallback values 2. Nested CASE expressions caused the regex to match inner THEN clauses instead of outer ones, inferring wrong types Fixes: skip bare integer literals in ELSE fallback, and return Unknown for nested CASE (too complex for regex-based inference). MxBuild validates the actual types at build time. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 49b4f23 commit 954dbc3

1 file changed

Lines changed: 21 additions & 4 deletions

File tree

mdl/executor/oql_type_inference.go

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,17 @@ func inferTypeStatic(expr string) ast.DataType {
238238
}
239239

240240
// inferCaseType infers the type of a CASE expression by looking at THEN clause values.
241+
// Only reports a type when it can be confidently inferred from a non-literal THEN branch.
242+
// Falls back to ELSE only if all THEN branches are unknown AND the ELSE is non-trivial.
243+
// A bare "0" in ELSE is ambiguous (could be Integer or Decimal depending on context).
241244
func inferCaseType(expr string) ast.DataType {
245+
// Nested CASE expressions are too complex for static regex-based inference.
246+
// The regex would match inner THEN clauses, producing wrong types.
247+
upperExpr := strings.ToUpper(expr)
248+
if strings.Count(upperExpr, "CASE") > 1 {
249+
return ast.DataType{Kind: ast.TypeUnknown}
250+
}
251+
242252
// Find THEN ... WHEN/ELSE/END patterns to extract result expressions
243253
thenPattern := regexp.MustCompile(`(?i)\bTHEN\s+(.+?)(?:\s+WHEN\b|\s+ELSE\b|\s+END\b)`)
244254
matches := thenPattern.FindAllStringSubmatch(expr, -1)
@@ -250,12 +260,19 @@ func inferCaseType(expr string) ast.DataType {
250260
}
251261
}
252262
}
253-
// Try ELSE clause
263+
// Try ELSE clause — but only if the value is not a bare integer literal.
264+
// Bare "0" or "1" in ELSE are type-ambiguous fallback values that should
265+
// not override the actual branch type (which may involve division or
266+
// expressions the static inferrer can't parse).
254267
elsePattern := regexp.MustCompile(`(?i)\bELSE\s+(.+?)\s+END\b`)
255268
if match := elsePattern.FindStringSubmatch(expr); len(match) >= 2 {
256-
t := inferTypeStatic(strings.TrimSpace(match[1]))
257-
if t.Kind != ast.TypeUnknown {
258-
return t
269+
elseExpr := strings.TrimSpace(match[1])
270+
// Skip bare integer literals — they're ambiguous in CASE context
271+
if !regexp.MustCompile(`^-?\d+$`).MatchString(elseExpr) {
272+
t := inferTypeStatic(elseExpr)
273+
if t.Kind != ast.TypeUnknown {
274+
return t
275+
}
259276
}
260277
}
261278
return ast.DataType{Kind: ast.TypeUnknown}

0 commit comments

Comments
 (0)