diff --git a/internal/codegen/golang/result.go b/internal/codegen/golang/result.go index c5126602da..05ee2a2022 100644 --- a/internal/codegen/golang/result.go +++ b/internal/codegen/golang/result.go @@ -41,15 +41,24 @@ func buildEnums(req *plugin.GenerateRequest, options *opts.Options) []Enum { seen := make(map[string]struct{}, len(enum.Vals)) for i, v := range enum.Vals { value := EnumReplace(v) - if _, found := seen[value]; found || value == "" { + if value == "" { value = fmt.Sprintf("value_%d", i) } + // Dedup on the final constant name, not on EnumReplace(v): + // StructName further collapses characters (e.g. a trailing "_"), + // so distinct EnumReplace outputs like "A" and "A_" (from "A+" + // and "A-") can still map to the same constant name. + name := StructName(enumName+"_"+value, options) + if _, found := seen[name]; found { + value = fmt.Sprintf("value_%d", i) + name = StructName(enumName+"_"+value, options) + } e.Constants = append(e.Constants, Constant{ - Name: StructName(enumName+"_"+value, options), + Name: name, Value: v, Type: e.Name, }) - seen[value] = struct{}{} + seen[name] = struct{}{} } enums = append(enums, e) } diff --git a/internal/codegen/golang/result_test.go b/internal/codegen/golang/result_test.go index 0c58525ec3..94b5c5affa 100644 --- a/internal/codegen/golang/result_test.go +++ b/internal/codegen/golang/result_test.go @@ -3,6 +3,7 @@ package golang import ( "testing" + "github.com/sqlc-dev/sqlc/internal/codegen/golang/opts" "github.com/sqlc-dev/sqlc/internal/metadata" "github.com/sqlc-dev/sqlc/internal/plugin" ) @@ -76,3 +77,42 @@ func TestPutOutColumns_AlwaysTrueWhenQueryHasColumns(t *testing.T) { t.Error("should be true when we have columns") } } + +// TestBuildEnums_DeduplicatesConstantNames covers enum values that differ only +// by characters that get stripped or collapsed when building a Go identifier +// (e.g. "A+" and "A-"), which previously produced duplicate constant names and +// uncompilable output. +func TestBuildEnums_DeduplicatesConstantNames(t *testing.T) { + req := &plugin.GenerateRequest{ + Catalog: &plugin.Catalog{ + DefaultSchema: "public", + Schemas: []*plugin.Schema{ + { + Name: "public", + Enums: []*plugin.Enum{ + { + Name: "blood_group_type", + Vals: []string{"A+", "A-", "B+", "B-", "AB+", "AB-", "O+", "O-"}, + }, + }, + }, + }, + }, + } + + enums := buildEnums(req, &opts.Options{}) + if len(enums) != 1 { + t.Fatalf("expected 1 enum, got %d", len(enums)) + } + if got := len(enums[0].Constants); got != 8 { + t.Fatalf("expected 8 constants, got %d", got) + } + + seen := make(map[string]string, 8) + for _, c := range enums[0].Constants { + if prev, dup := seen[c.Name]; dup { + t.Errorf("duplicate constant name %q for values %q and %q", c.Name, prev, c.Value) + } + seen[c.Name] = c.Value + } +}