{"record":{"id":"7b298a77b4c675e6","repo":"vitessio/vitess","slug":"vt09018","errorCode":"VT09018","errorMessage":"cannot add '%s' expression to a table/vindex","messagePattern":"cannot add '(.+?)' expression to a table/vindex","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"go/vt/vtgate/planbuilder/operators/table.go","lineNumber":126,"sourceCode":"func (to *Table) GetColNames() []*sqlparser.ColName {\n\treturn to.Columns\n}\n\nfunc (to *Table) AddCol(col *sqlparser.ColName) {\n\tto.Columns = append(to.Columns, col)\n}\n\nfunc (to *Table) TablesUsed(in []string) []string {\n\tif to.QTable == nil || to.VTable == nil || sqlparser.SystemSchema(to.QTable.Table.Qualifier.String()) {\n\t\treturn in\n\t}\n\treturn append(in, QualifiedString(to.VTable.Keyspace, to.VTable.Name.String()))\n}\n\nfunc addColumn(ctx *plancontext.PlanningContext, op ColNameColumns, e sqlparser.Expr) int {\n\tcol, ok := e.(*sqlparser.ColName)\n\tif !ok {\n\t\tpanic(vterrors.VT09018(fmt.Sprintf(\"cannot add '%s' expression to a table/vindex\", sqlparser.String(e))))\n\t}\n\tsqlparser.RemoveKeyspaceInCol(col)\n\tcols := op.GetColNames()\n\tcolAsExpr := func(c *sqlparser.ColName) sqlparser.Expr { return c }\n\tif offset, found := canReuseColumn(ctx, cols, e, colAsExpr); found {\n\t\treturn offset\n\t}\n\toffset := len(cols)\n\top.AddCol(col)\n\treturn offset\n}\n\nfunc (to *Table) ShortDescription() string {\n\tif to.QTable == nil {\n\t\treturn \"dual\"\n\t}\n\tvar tbl string\n\tif to.VTable != nil {","sourceCodeStart":108,"sourceCodeEnd":144,"githubUrl":"https://github.com/vitessio/vitess/blob/01a25a7d176f94613b8d59d799f438380a8760e4/go/vt/vtgate/planbuilder/operators/table.go#L108-L144","documentation":"VT09018 is raised when the planner tries to add a non-column expression to an operator that only supports plain column references. addColumn for a table/vindex operator asserts that the expression being added is a *sqlparser.ColName; anything else (function calls, arithmetic, literals, subqueries) cannot be materialized by a table/vindex source, so the planner panics with the expression string embedded.","triggerScenarios":"The planner's addColumn path receives an expression that is not a simple column name while planning column output for a Table/Vindex operator — e.g. a SELECT item or pushed expression like `SELECT func(x) FROM t` reaching the table operator's AddColumn via addColumn instead of being evaluated in an enclosing projection.","commonSituations":"Queries with computed expressions or function calls in the select list that the planner expected to be handled by an outer projection but were pushed to the table/vindex; usually indicates a planner limitation or bug with the specific SQL shape rather than user misconfiguration.","solutions":["Rewrite the query so the non-column expression is computed in a derived table: SELECT e.val FROM (SELECT func(x) AS val ... ) e — keeping the table operator's columns simple.","Check the expression in the panic message ('cannot add %s ...') and simplify it, e.g. avoid complex expressions directly on the table's projected columns.","If the query is standard SQL and should be supported, report it as a planner bug with the minimized query and Vitess version."],"exampleFix":"// before: pushing a function expression to the table operator\nSELECT SHA1(col) FROM t\n\n// after: wrap in a derived table so only a column reaches the table\nSELECT d.h FROM (SELECT SHA1(col) AS h FROM t) AS d","handlingStrategy":"validation","validationCode":"if _, ok := expr.(*sqlparser.ColName); !ok {\n    // only plain columns can be added to a table/vindex; handle at an outer projection\n    return errors.New(\"non-column expression cannot be pushed to table\")\n}","typeGuard":"func isPlainColumn(e sqlparser.Expr) bool {\n    _, ok := e.(*sqlparser.ColName)\n    return ok\n}","tryCatchPattern":"// Panics are internal invariants; prefer pre-validation. If catching in a planner boundary:\ndefer func() {\n    if r := recover(); r != nil {\n        vte, ok := r.(error)\n        if ok && vterrors.Code(vte) == vtrpcpb.Code_VT09018 { // unwrap/handle\n            _ = vte\n        }\n    }\n}()","preventionTips":["Keep computed expressions out of the table operator's column list; evaluate them in a derived table or outer projection.","When adding columns programmatically, assert the expression is *sqlparser.ColName before calling addColumn.","Test queries with functions and arithmetic in the select list against table/vindex operators."],"tags":["planbuilder","unsupported-expression","go","operators"],"backgroundTag":"pushdown-unsupported-expression","analyzedSha":"01a25a7d176f94613b8d59d799f438380a8760e4","analyzedAt":"2026-09-01T17:28:30.605Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}