Skip to content

Generated ReadXxxByYyy has duplicate columns when an index key column is part of the primary key #206

Description

@oNqNu

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions