{"record":{"id":"f202f3bad6b56c06","repo":"vitessio/vitess","slug":"unreachable-f202f3","errorCode":null,"errorMessage":"unreachable","messagePattern":"unreachable","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"go/vt/vtgate/evalengine/compare.go","lineNumber":169,"sourceCode":"\t\t\t// least compare something and to handle equality checks.\n\t\t\treturn strings.Compare(l.string, r.string)\n\t\t}\n\t\treturn 0\n\t}\n\tif l.set < r.set {\n\t\treturn -1\n\t}\n\treturn 1\n}\n\nfunc compareDateAndString(l, r eval) int {\n\tif tt, ok := l.(*evalTemporal); ok {\n\t\treturn tt.dt.Compare(r.(*evalBytes).toDateBestEffort())\n\t}\n\tif tt, ok := r.(*evalTemporal); ok {\n\t\treturn l.(*evalBytes).toDateBestEffort().Compare(tt.dt)\n\t}\n\tpanic(\"unreachable\")\n}\n\n// More on string collations coercibility on MySQL documentation:\n//   - https://dev.mysql.com/doc/refman/8.0/en/charset-collation-coercibility.html\nfunc compareStrings(l, r eval, env *collations.Environment) (int, error) {\n\tl, r, col, err := mergeAndCoerceCollations(l, r, env)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tcollation := colldata.Lookup(col.Collation)\n\tif collation == nil {\n\t\treturn 0, vterrors.Errorf(vtrpcpb.Code_UNKNOWN, \"cannot compare strings, collation is unknown or unsupported (collation ID: %d)\", col.Collation)\n\t}\n\treturn collation.Collate(l.ToRawBytes(), r.ToRawBytes(), false), nil\n}\n\nfunc compareJSON(l, r eval) (int, error) {\n\tlj, err := argToJSON(l)","sourceCodeStart":151,"sourceCodeEnd":187,"githubUrl":"https://github.com/vitessio/vitess/blob/01a25a7d176f94613b8d59d799f438380a8760e4/go/vt/vtgate/evalengine/compare.go#L151-L187","documentation":"compareDateAndString compares a temporal value (DATE/DATETIME/TIMESTAMP/TIME) with a string by best-effort parsing the string to a date. The final panic('unreachable') asserts one side is an evalTemporal; the caller (evalCompare dispatch) must guarantee that. It fires if the function is invoked with two non-temporal operands — an internal dispatch invariant, not a user-facing SQL error.","triggerScenarios":"A refactor or new comparison path in evalCompare that routes a comparison to compareDateAndString without checking that at least one operand is *evalTemporal, e.g. comparing two strings or two numerics through the temporal branch by mistake.","commonSituations":"Hitting this after modifying the evalengine's comparison dispatch (compare.go) or adding a new eval type that gets misrouted; end users only see it as a Vitess crash from a specific query.","solutions":["Check the call site that dispatches to compareDateAndString and ensure it only routes temporal-vs-string comparisons","Fix the dispatch table in evalCompare (compare.go) so non-temporal pairs go to the numeric/string comparators","File a Vitess bug with the query that produced the panic if it reproduces on unmodified code"],"exampleFix":"// before\nreturn compareDateAndString(l, r)\n// after\nif isTemporal(l) || isTemporal(r) {\n\treturn compareDateAndString(l, r)\n}\nreturn compareStrings(l, r, env)","handlingStrategy":"type-guard","validationCode":"// Go: before calling compareDateAndString\nif _, ok := l.(*evalTemporal); !ok {\n\tif _, ok := r.(*evalTemporal); !ok {\n\t\t// neither side temporal; use compareStrings instead\n\t}\n}","typeGuard":"func isTemporalEval(e eval) bool {\n\t_, ok := e.(*evalTemporal)\n\treturn ok\n}","tryCatchPattern":"func safeCompareDateAndString(l, r eval) (result int) {\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tresult = strings.Compare(l.String(), r.String())\n\t\t}\n\t}()\n\treturn compareDateAndString(l, r)\n}","preventionTips":["Route comparisons through a single dispatch function that type-switches once","Add tests comparing every eval type pair against every other","Prefer returning vterrors over panics at dispatch boundaries"],"tags":["go","panic","evalengine","comparison","internal-invariant"],"backgroundTag":"unreachable-code-panic","analyzedSha":"01a25a7d176f94613b8d59d799f438380a8760e4","analyzedAt":"2026-09-01T17:28:30.605Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}