vitessio/vitess · error

only count(*) is supported: %v

Error message

only count(*) is supported: %v

What it means

VReplication's table plan builder only supports the simplest form of COUNT in a filtered replication SELECT: COUNT(*). Any argumented count, e.g. COUNT(col) or COUNT(DISTINCT col), is rejected because the generated materialization logic cannot evaluate per-row counting. The error is returned by analyzeExpr when an aggregate named 'count' is not a *sqlparser.CountStar.

Source

Thrown at go/vt/vttablet/tabletmanager/vreplication/table_plan_builder.go:497

		case "keyspace_id":
			if len(expr.Exprs) != 0 {
				return nil, fmt.Errorf("unsupported multiple keyspace_id expressions: %v", sqlparser.String(expr))
			}

			tpb.sendSelect.AddSelectExpr(&sqlparser.AliasedExpr{Expr: aliased.Expr})
			// The vstreamer responds with "keyspace_id" as the field name for this request.
			cexpr.expr = &sqlparser.ColName{Name: sqlparser.NewIdentifierCI("keyspace_id")}
			return cexpr, nil
		}
	}
	if expr, ok := aliased.Expr.(sqlparser.AggrFunc); ok {
		if sqlparser.IsDistinct(expr) {
			return nil, fmt.Errorf("unsupported distinct expression usage: %v", sqlparser.String(expr))
		}
		switch fname := expr.AggrName(); fname {
		case "count":
			if _, ok := expr.(*sqlparser.CountStar); !ok {
				return nil, fmt.Errorf("only count(*) is supported: %v", sqlparser.String(expr))
			}
			cexpr.operation = opCount
			return cexpr, nil
		case "sum":
			if len(expr.GetArgs()) != 1 {
				return nil, fmt.Errorf("unsupported multiple columns in sum clause: %v", sqlparser.String(expr))
			}
			innerCol, ok := expr.GetArg().(*sqlparser.ColName)
			if !ok {
				return nil, fmt.Errorf("unsupported non-column name in sum clause: %v", sqlparser.String(expr))
			}
			if !innerCol.Qualifier.IsEmpty() {
				return nil, fmt.Errorf("unsupported qualifier for column: %v", sqlparser.String(innerCol))
			}
			cexpr.operation = opSum
			cexpr.expr = innerCol
			tpb.addCol(innerCol.Name)
			cexpr.references[innerCol.Name.String()] = true

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Rewrite the select list to use COUNT(*) instead of COUNT(col) or COUNT(DISTINCT col).
  2. If you need per-column counts, compute them outside vreplication (e.g. via a separate query or aggregation engine).
  3. Remove DISTINCT from the aggregate — distinct usage is rejected separately.
  4. Check that the query you passed to MoveTables/Materialize only uses supported aggregates (count(*) and single-column sum).

Example fix

// before
select count(user_id) as cnt from t
// after
select count(*) as cnt from t
Defensive patterns

Strategy: validation

Validate before calling

// Go: reject argumented COUNT before submitting to the workflow
func validCount(e sqlparser.Expr) bool {
    ag, ok := e.(*sqlparser.AliasedExpr)
    if !ok { return true }
    cs, ok := ag.Expr.(*sqlparser.CountStar)
    return ok && cs != nil
}

Type guard

func isCountStar(n sqlparser.SQLNode) bool { _, ok := n.(*sqlparser.CountStar); return ok }

Prevention

When it happens

Trigger: Running a Materialize / vreplication workflow whose SELECT list contains COUNT(col), COUNT(1), COUNT(DISTINCT x), or COUNT(*) with any non-star argument; analyzeExpr is called via analyzeExprs while building the table plan.

Common situations: Users copying a hand-written analytics query into a Materialize source; upgrading a workflow that previously used a plain count and someone changed it to count a column; tools generating 'SELECT COUNT(id) FROM t' as a convenience.

Related errors


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