vitessio/vitess · error
VT09018
VT09018
Error message
cannot add '%s' expression to a table/vindex
What it means
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.
Source
Thrown at go/vt/vtgate/planbuilder/operators/table.go:126
func (to *Table) GetColNames() []*sqlparser.ColName {
return to.Columns
}
func (to *Table) AddCol(col *sqlparser.ColName) {
to.Columns = append(to.Columns, col)
}
func (to *Table) TablesUsed(in []string) []string {
if to.QTable == nil || to.VTable == nil || sqlparser.SystemSchema(to.QTable.Table.Qualifier.String()) {
return in
}
return append(in, QualifiedString(to.VTable.Keyspace, to.VTable.Name.String()))
}
func addColumn(ctx *plancontext.PlanningContext, op ColNameColumns, e sqlparser.Expr) int {
col, ok := e.(*sqlparser.ColName)
if !ok {
panic(vterrors.VT09018(fmt.Sprintf("cannot add '%s' expression to a table/vindex", sqlparser.String(e))))
}
sqlparser.RemoveKeyspaceInCol(col)
cols := op.GetColNames()
colAsExpr := func(c *sqlparser.ColName) sqlparser.Expr { return c }
if offset, found := canReuseColumn(ctx, cols, e, colAsExpr); found {
return offset
}
offset := len(cols)
op.AddCol(col)
return offset
}
func (to *Table) ShortDescription() string {
if to.QTable == nil {
return "dual"
}
var tbl string
if to.VTable != nil {View on GitHub (pinned to 01a25a7d17)
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.
Example fix
// before: pushing a function expression to the table operator SELECT SHA1(col) FROM t // after: wrap in a derived table so only a column reaches the table SELECT d.h FROM (SELECT SHA1(col) AS h FROM t) AS d
Defensive patterns
Strategy: validation
Validate before calling
if _, ok := expr.(*sqlparser.ColName); !ok {
// only plain columns can be added to a table/vindex; handle at an outer projection
return errors.New("non-column expression cannot be pushed to table")
} Type guard
func isPlainColumn(e sqlparser.Expr) bool {
_, ok := e.(*sqlparser.ColName)
return ok
} Try / catch
// Panics are internal invariants; prefer pre-validation. If catching in a planner boundary:
defer func() {
if r := recover(); r != nil {
vte, ok := r.(error)
if ok && vterrors.Code(vte) == vtrpcpb.Code_VT09018 { // unwrap/handle
_ = vte
}
}
}() Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- VT13001
- VT13001
- ReadFile cannot be called on read-write backup
- AddFile cannot be called on read-only backup
- EndBackup cannot be called on read-only backup
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/7b298a77b4c675e6.
Report an issue: GitHub.