vitessio/vitess · error

unsupported function: %v

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.

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)

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.