vitessio/vitess · error

unsupported function

Error message

unsupported function: %v

What it means

The vstreamer plan builder only supports a single aggregate function in a VStreamResults select expression: keyspace_id(). Any other aggregate (COUNT, SUM, MAX, etc.) cannot be computed incrementally from the binlog, so analyzeExpr rejects it with the full SQL text of the aggregate.

Solutions

  1. Remove the aggregate from the select list; stream raw columns instead and aggregate client-side.
  2. If you need the shard's keyspace_id, select only keyspace_id().
  3. Use a VTGate query (not vstreamer) if you truly need server-side aggregation.
  4. If you believe the aggregate should be supported, implement it in analyzeExpr's AggrFunc case of planbuilder.go.

Example fix

// before
select count(*) from t
// after
select keyspace_id() from t  -- or plain columns
select id, name from t
Defensive patterns

Strategy: validation

Validate before calling

// Before calling VStreamResults, ensure every select expr is a plain column or keyspace_id()
func validateVStreamSelect(exprs []string) error {
    for _, e := range exprs {
        if strings.ContainsAny(e, "(*)") && !strings.EqualFold(strings.TrimSpace(e), "keyspace_id()") {
            return fmt.Errorf("aggregate %q not supported; use plain columns or keyspace_id()", e)
        }
    }
    return nil
}

Type guard

func isSupportedAggregate(expr string) bool {
    return strings.EqualFold(strings.TrimSpace(expr), "keyspace_id()")
}

Try / catch

// Go: check the error from analyzeExpr at plan time
if err := plan.AnalyzeExprs(vschema, selExprs); err != nil {
    if strings.Contains(err.Error(), "unsupported function") {
        return nil, fmt.Errorf("vstream select contains unsupported aggregate, revise select list: %w", err)
    }
    return nil, err
}

Prevention

When it happens

Trigger: Calling VStreamResults (or configuring a vstreamer select) with an expression like `select count(*) from t` or `select max(id) from t` — any sqlparser.AggrFunc whose AggrName() is not "keyspace_id".

Common situations: Writing a custom VReplication-style streaming query or materialization flow and accidentally adding a normal SQL aggregate; copying a MySQL query into a vstream select list expecting aggregation to work.

Related errors


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

Appendix: source

Thrown at go/vt/vttablet/tabletserver/vstreamer/planbuilder.go:834

	if !ok {
		return ColExpr{}, fmt.Errorf("unsupported: %v", sqlparser.String(selExpr))
	}
	switch inner := aliased.Expr.(type) {
	case *sqlparser.ColName:
		if !inner.Qualifier.IsEmpty() {
			return ColExpr{}, fmt.Errorf("unsupported qualifier for column: %v", sqlparser.String(inner))
		}
		colnum, err := findColumn(plan.Table, inner.Name)
		if err != nil {
			return ColExpr{}, err
		}
		return ColExpr{
			ColNum: colnum,
			Field:  plan.Table.Fields[colnum],
		}, nil
	case sqlparser.AggrFunc:
		if inner.AggrName() != "keyspace_id" {
			return ColExpr{}, fmt.Errorf("unsupported function: %v", sqlparser.String(inner))
		}
		if len(inner.GetArgs()) != 0 {
			return ColExpr{}, fmt.Errorf("unexpected: %v", sqlparser.String(inner))
		}
		cv, err := vschema.FindColVindex(plan.Table.Name)
		if err != nil {
			return ColExpr{}, err
		}
		vindexColumns, err := buildVindexColumns(plan.Table, cv.Columns)
		if err != nil {
			return ColExpr{}, err
		}
		return ColExpr{
			Field: &querypb.Field{
				Name:    "keyspace_id",
				Type:    sqltypes.VarBinary,
				Charset: collations.CollationBinaryID,
				Flags:   uint32(querypb.MySqlFlag_BINARY_FLAG),

View on GitHub (pinned to 01a25a7d17)