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
- Remove the function call and filter on a bare column, e.g. ts >= '2024-01-01' instead of DATE(ts) = '2024-01-01'
- Use in_keyrange() if the goal is keyspace-range based filtering, which is the only supported function
- Filter unconditionally (no WHERE) in the Filter and apply function-based filtering in your own event callback
- 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
- Keep vstream filter WHERE clauses to simple column comparisons
- Use in_keyrange() for keyspace-range filtering needs
- Apply function-based transformations in your event callback, not the filter
- Review the vstreamer planbuilder docs for supported operators
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
- can't parse binlog event: invalid data: %#v
- can't parse FORMAT_DESCRIPTION_EVENT: %v, event data: %#v
- got a real event before FORMAT_DESCRIPTION_EVENT: %#v
- can't strip checksum from binlog event: %v, event data: %#v
- unexpected statement type %s in row-based replication: %q
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/7bec0210f1261867.
Report an issue: GitHub.