Summary
When a secondary index is defined on column(s) that are part of the table's primary key, the generated ReadXxxByYyy function contains duplicate column names in its columns slice. Calling such a function fails at runtime because Cloud Spanner rejects a read request that lists the same column more than once.
Environment
- yo version:
v0.7.0 (installed via go install go.mercari.io/yo@v0.7.0)
- Generation mode: from DDL (
yo generate ./schema.sql --from-ddl -o ...)
Reproduction
Minimal schema (the index key ItemId is also part of the primary key):
CREATE TABLE Items (
ParentId STRING(36) NOT NULL,
ItemId STRING(36) NOT NULL,
) PRIMARY KEY (ParentId, ItemId);
CREATE INDEX ItemsByItemId ON Items (ItemId);
Generate:
yo generate ./schema.sql --from-ddl -o ./dto
Actual generated code
func ReadItemsByItemID(ctx context.Context, db YORODB, keys spanner.KeySet) ([]*Item, error) {
var res []*Item
columns := []string{
"ParentId",
"ItemId",
"ItemId", // <-- duplicated (from the index key, already present as a PK column)
}
decoder := newItem_Decoder(columns)
rows := db.ReadUsingIndex(ctx, "Items", "ItemsByItemId", keys, columns)
// ...
}
ItemId appears twice. When this function is called, Spanner returns an error such as:
Duplicate column ... / column listed more than once
Expected
Each column should appear at most once in the columns slice, e.g.:
columns := []string{
"ParentId",
"ItemId",
}
For comparison, when the index key is not part of the primary key (e.g. PRIMARY KEY (ParentId) with CREATE INDEX ... ON Items (ItemId)), the generated code is correct (no duplicate), which confirms the problem is specific to index keys overlapping PK columns.
Root cause
In templates/index.go.tpl, the columns slice is built by concatenating primary-key fields, index key fields, and storing fields without de-duplication:
columns := []string{
{{- range .Type.PrimaryKeyFields }}
"{{ colname .Col }}",
{{- end }}
{{- range .Fields }}
"{{ colname .Col }}",
{{- end }}
{{- range .StoringFields }}
"{{ colname .Col }}",
{{- end }}
}
When a column appears in both .Type.PrimaryKeyFields and .Fields (or .StoringFields), it is emitted more than once.
Suggested fix
De-duplicate the column list, e.g. skip index/storing fields that are already included as primary-key columns (or de-duplicate the final list). Happy to open a PR if this direction sounds good.
Summary
When a secondary index is defined on column(s) that are part of the table's primary key, the generated
ReadXxxByYyyfunction contains duplicate column names in itscolumnsslice. Calling such a function fails at runtime because Cloud Spanner rejects a read request that lists the same column more than once.Environment
v0.7.0(installed viago install go.mercari.io/yo@v0.7.0)yo generate ./schema.sql --from-ddl -o ...)Reproduction
Minimal schema (the index key
ItemIdis also part of the primary key):Generate:
Actual generated code
ItemIdappears twice. When this function is called, Spanner returns an error such as:Expected
Each column should appear at most once in the
columnsslice, e.g.:For comparison, when the index key is not part of the primary key (e.g.
PRIMARY KEY (ParentId)withCREATE INDEX ... ON Items (ItemId)), the generated code is correct (no duplicate), which confirms the problem is specific to index keys overlapping PK columns.Root cause
In
templates/index.go.tpl, thecolumnsslice is built by concatenating primary-key fields, index key fields, and storing fields without de-duplication:When a column appears in both
.Type.PrimaryKeyFieldsand.Fields(or.StoringFields), it is emitted more than once.Suggested fix
De-duplicate the column list, e.g. skip index/storing fields that are already included as primary-key columns (or de-duplicate the final list). Happy to open a PR if this direction sounds good.