vitessio/vitess · error

unsupported constraint: %v

Error message

unsupported constraint: %v

What it means

VStreamer cannot evaluate arbitrary MySQL functions when filtering binlog events, since event filtering happens in Go on rows from the binlog stream. Only the VStreamer-specific in_keyrange() function is allowed as a function constraint in a WHERE clause; anything else is rejected at plan-build time.

Source

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

				plan.whereExprsToPushDown = append(plan.whereExprsToPushDown, expr)
				continue
			}
			resolved, err := plan.getEvalResultForLiteral(expr.Right)
			if err != nil {
				return err
			}
			plan.Filters = append(plan.Filters, Filter{
				Opcode: opcode,
				ColNum: colnum,
				Value:  resolved.Value(plan.env.CollationEnv().DefaultConnectionCharset()),
			})
			// Add it to the expressions that get pushed down to mysqld.
			plan.whereExprsToPushDown = append(plan.whereExprsToPushDown, expr)
		case *sqlparser.FuncExpr:
			// We cannot filter binlog events in VStreamer using MySQL functions so
			// we only allow the in_keyrange() function, which is VStreamer specific.
			if !expr.Name.EqualString("in_keyrange") {
				return fmt.Errorf("unsupported constraint: %v", sqlparser.String(expr))
			}
			if err := plan.analyzeInKeyRange(vschema, expr.Exprs); err != nil {
				return err
			}
		case *sqlparser.IsExpr:
			qualifiedName, ok := expr.Left.(*sqlparser.ColName)
			if !ok {
				return fmt.Errorf("unexpected: %v", sqlparser.String(expr))
			}
			if !qualifiedName.Qualifier.IsEmpty() {
				return fmt.Errorf("unsupported qualifier for column: %v", sqlparser.String(qualifiedName))
			}
			colnum, err := findColumn(plan.Table, qualifiedName.Name)
			if err != nil {
				return err
			}
			switch expr.Right {
			case sqlparser.IsNullOp:

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Remove the function call and filter on a bare column, e.g. ts >= '2024-01-01' instead of DATE(ts) = '2024-01-01'
  2. Use in_keyrange() if the goal is keyspace-range based filtering, which is the only supported function
  3. Filter unconditionally (no WHERE) in the Filter and apply function-based filtering in your own event callback
  4. If pushdown to MySQL is acceptable, verify the expression is a simple comparison so it lands in whereExprsToPushDown instead

Example fix

// before
filter := "DATE(ts) = '2024-01-01'"
// after
filter := "ts >= '2024-01-01'"
Defensive patterns

Strategy: validation

Validate before calling

func validateFilter(expr string) error {
	parsed, err := sqlparser.Parse("select * from t where " + expr)
	if err != nil { return err }
	where := parsed.(*sqlparser.Select).Where.Expr
	for _, c := range splitAndExpression(nil, where) {
		if fn, ok := c.(*sqlparser.FuncExpr); ok && !fn.Name.EqualString("in_keyrange") {
			return fmt.Errorf("function not allowed in vstream filter: %v", sqlparser.String(fn))
		}
	}
	return nil
}

Try / catch

err := buildPlan(filter)
if err != nil && strings.Contains(err.Error(), "unsupported constraint") {
	// fall back to client-side filtering of unfiltered stream
}

Prevention

When it happens

Trigger: Calling Stream/StreamRows with a filter whose WHERE condition contains a MySQL function other than in_keyrange(), e.g. WHERE DATE(ts) = '2024-01-01' or UPPER(name) = 'X'.

Common situations: Users copy a query with SQL functions into a vstream Filter rules' filter field, or an ORM-generated filter includes function calls; also happens after upgrading filters that previously ran client-side.

Related errors


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