vitessio/vitess · error

should override

Error message

should override

What it means

CallExpr is an abstract base expression in the evalengine; its eval method is a placeholder that must be overridden by concrete call types (e.g., function calls, stored-procedure-ish nodes). Panicking here means a CallExpr instance that was not subclassed/rewritten into a concrete callable reached the evaluation stage. It signals the expression tree was built or dispatched incorrectly rather than a user-data problem.

Source

Thrown at go/vt/vtgate/evalengine/expr_call.go:32

limitations under the License.
*/

package evalengine

type (
	callable interface {
		IR
		callable() []IR
	}

	CallExpr struct {
		Arguments TupleExpr
		Method    string
	}
)

func (c *CallExpr) eval(*ExpressionEnv) (eval, error) {
	panic("should override")
}

func (c *CallExpr) compile(*compiler) (ctype, error) {
	panic("should override")
}

func (c *CallExpr) callable() []IR {
	return c.Arguments
}

func (c *CallExpr) args(env *ExpressionEnv) ([]eval, error) {
	args := make([]eval, 0, len(c.Arguments))
	for _, arg := range c.Arguments {
		e, err := arg.eval(env)
		if err != nil {
			return nil, err
		}
		args = append(args, e)

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Inspect the query/plan to see which function produced a bare CallExpr and ensure it maps to a concrete evalengine call type
  2. Add the missing mapping/rewrite so the CallExpr is replaced before evaluation
  3. If constructing expressions in tests, instantiate a concrete subclass instead of CallExpr
  4. Report to Vitess maintainers with the query and plan if no local extension exists

Example fix

// before
call := &evalengine.CallExpr{Method: "my_fn"}
v, err := call.eval(env)
// after
call := evalengine.newBuiltinFunc("my_fn", args) // concrete impl overriding eval
v, err := call.eval(env)
Defensive patterns

Strategy: validation

Validate before calling

// Ensure the expression is a concrete call before evaluating
func isConcreteCall(e evalengine.IR) bool {
	_, isBare := e.(*evalengine.CallExpr)
	return !isBare
}

Type guard

type concreteCall interface { evalengine.IR; callable() []evalengine.IR }
func asConcreteCall(e evalengine.IR) (concreteCall, bool) {
	cc, ok := e.(concreteCall)
	return cc, ok && !isBareCallExpr(e)
}

Try / catch

func safeEval(e evalengine.IR, env *evalengine.ExpressionEnv) (v evalengine.eval, err error) {
	defer func() {
		if r := recover(); r != nil {
			err = fmt.Errorf("eval panic: %v", r)
		}
	}()
	return e.eval(env)
}

Prevention

When it happens

Trigger: Evaluating an expression tree where a bare *CallExpr (not one of its concrete wrappers) is present and ExpressionEnv evaluation reaches CallExpr.eval. This occurs if the plan compiler/rewriter failed to replace CallExpr with a concrete implementation before execution.

Common situations: Vitess development where a new builtin/function kind is parsed but not mapped to its concrete evalengine implementation; tests that construct CallExpr directly; inconsistent builds where rewriting rules were skipped.

Related errors


AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01). Data as JSON: /api/errors/68fb94b227850cd7. Report an issue: GitHub.