vitessio/vitess · error
primary key column %v is not allowed to reference an aggrega
Error message
primary key column %v is not allowed to reference an aggregate expression
What it means
analyzePK requires each primary key column to map to a plain column expression (opExpr). If the PK column resolves to an aggregate expression in the select filter (e.g. SUM(x) or COUNT(*)), the copied rows could not be uniquely identified, so plan construction is aborted.
Source
Thrown at go/vt/vttablet/tabletmanager/vreplication/table_plan_builder.go:613
for _, col := range cols {
if !col.IsPK {
continue
}
if col.IsGenerated {
// It's possible that a GENERATED column is part of the PRIMARY KEY. That's valid.
// But then, we also know that we don't actually SELECT a GENERATED column, we just skip
// it silently and let it re-materialize by MySQL itself on the target.
continue
}
cexpr := tpb.findCol(sqlparser.NewIdentifierCI(col.Name))
if cexpr == nil {
// TODO(shlomi): at some point in the futue we want to make this check stricter.
// We could be reading a generated column c1 which in turn selects some other column c2.
// We will want t oensure that `c2` is found in select list...
return fmt.Errorf("primary key column %v not found in table's select filter or the TableMap event within the GTID", col)
}
if cexpr.operation != opExpr {
return fmt.Errorf("primary key column %v is not allowed to reference an aggregate expression", col)
}
cexpr.isPK = true
cexpr.dataType = col.DataType
cexpr.columnType = col.ColumnType
tpb.pkCols = append(tpb.pkCols, cexpr)
}
return nil
}
// analyzeExtraSourcePkCols builds tpb.extraSourcePkCols.
// VReplication allows source and target tables to use different unique keys. Normally, both will
// use same PRIMARY KEY. Other times, same other UNIQUE KEY. But it's possible that source and target
// unique keys will only have partial (or empty) shared list of columns.
// To be able to generate UPDATE/DELETE queries correctly, we need to know the identities of the
// source unique key columns, that are not already part of the target unique key columns. We call
// those columns "extra source pk columns". We will use them in the `WHERE` clause.
func (tpb *tablePlanBuilder) analyzeExtraSourcePkCols(colInfos []*ColumnInfo, sourceKeyTargetColumnNames []string) error {
sourceKeyTargetColumnNamesMap := map[string]bool{}View on GitHub (pinned to 01a25a7d17)
Solutions
- Rewrite the filter select so the PK column is selected as a plain column, not via an aggregate or computed expression
- Use a non-aggregate alias distinct from the PK column name for any aggregate you need
- Re-run the workflow after correcting the select expression
Example fix
// before "select max(id) as id, name from t" // after "select id, max(created_at) as latest, name from t"
Defensive patterns
Strategy: validation
Validate before calling
// Ensure PK columns are selected plainly, not via aggregates/expressions -- bad: SELECT max(id) AS id FROM t -- good: SELECT id FROM t
Prevention
- Never alias an aggregate or computed expression to a PK column name
- Keep filter selects to plain column lists for copied tables
- Lint workflow filters before applying them
When it happens
Trigger: A Filter select expression that maps a table's PK column name to an aggregate expression, e.g. 'select max(id) as id from t' where id is the PK; buildFromFields/buildTablePlan call analyzePK and hit cexpr.operation != opExpr.
Common situations: Hand-written filter select expressions with aggregates or computed aliases that shadow PK column names; accidental use of GROUP BY-style selects in MoveTables filters.
Related errors
- primary key column %v not found in table's select filter or
- table %s not found in schema
- more than one target for source table %s: %s and %s
- invalid expression: %v
- failed to find column name for convert using expression: %v,
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/64ac6eb9ecf82033.
Report an issue: GitHub.