diff --git a/runtime/builder/builder.go b/runtime/builder/builder.go index 32909f24b..2566977e9 100644 --- a/runtime/builder/builder.go +++ b/runtime/builder/builder.go @@ -187,7 +187,7 @@ func (q Query) buildOutputs(outputs []Output) (string, error) { return builder.String(), nil } -var ErrOrWithoutAnd = fmt.Errorf("OR can only be used with ANDs") +var ErrDuplicateField = fmt.Errorf("duplicate field") func (q Query) buildFields(list bool, wrapList bool, fields []Field) (string, error) { var builder strings.Builder @@ -226,12 +226,8 @@ func (q Query) buildFields(list bool, wrapList bool, fields []Field) (string, er } for _, f := range final { - if f.Name == "OR" { - for _, field := range f.Fields { - if field.Name != "AND" { - return "", ErrOrWithoutAnd - } - } + if err := checkFields(f, f.Fields); err != nil { + return "", err } if wrapList { @@ -280,6 +276,19 @@ func (q Query) buildFields(list bool, wrapList bool, fields []Field) (string, er return builder.String(), nil } +func checkFields(parent Field, fields []Field) error { + uniqueObjectFields := make(map[string]Field) + for _, f := range fields { + if f.Value != nil && !f.List && !parent.List { + if _, ok := uniqueObjectFields[f.Name]; ok { + return fmt.Errorf("%w: %q", ErrDuplicateField, f.Name) + } + uniqueObjectFields[f.Name] = f + } + } + return nil +} + func (q Query) Exec(ctx context.Context, into interface{}) error { str, err := q.Build() if err != nil { diff --git a/test/features/enums/enums_test.go b/test/features/enums/enums_test.go index 5104cd33a..0a4c93862 100644 --- a/test/features/enums/enums_test.go +++ b/test/features/enums/enums_test.go @@ -2,6 +2,7 @@ package enums import ( "context" + "errors" "testing" "github.com/stretchr/testify/assert" @@ -160,8 +161,25 @@ func TestEnums(t *testing.T) { ).OrderBy( User.ID.Order(SortOrderAsc), ).Exec(ctx) - - assert.Equal(t, builder.ErrOrWithoutAnd, err) + //if err != nil { + // t.Fatalf("fail %s", err) + //} + + assert.Equal(t, builder.ErrDuplicateField, errors.Unwrap(err)) + //massert.Equal(t, []UserModel{ + // { + // InnerUser: InnerUser{ + // ID: "123", + // Role: RoleAdmin, + // }, + // }, + // { + // InnerUser: InnerUser{ + // ID: "789", + // Role: RoleUser, + // }, + // }, + //}, actual) }, }} for _, tt := range tests { diff --git a/test/projects/basic/basic_test.go b/test/projects/basic/basic_test.go index d5b7b456a..fbd8617eb 100644 --- a/test/projects/basic/basic_test.go +++ b/test/projects/basic/basic_test.go @@ -3,6 +3,7 @@ package basic import ( "context" "encoding/json" + "errors" "testing" "github.com/stretchr/testify/assert" @@ -652,7 +653,7 @@ func TestBasic(t *testing.T) { } `}, run: func(t *testing.T, client *PrismaClient, ctx cx) { - _, err := client.User.FindMany( + actual, err := client.User.FindMany( User.Or( User.Email.Equals("email1"), User.ID.Equals("id2"), @@ -660,10 +661,28 @@ func TestBasic(t *testing.T) { ).OrderBy( User.ID.Order(SortOrderAsc), ).Exec(ctx) - assert.Equal(t, builder.ErrOrWithoutAnd, err) + if err != nil { + t.Fatalf("fail %s", err) + } + + expected := []UserModel{{ + InnerUser: InnerUser{ + ID: "id1", + Email: "email1", + Username: "a", + }, + }, { + InnerUser: InnerUser{ + ID: "id2", + Email: "email2", + Username: "b", + }, + }} + + assert.Equal(t, expected, actual) }, }, { - name: "OR operations complex", + name: "OR operations complex with and", // language=GraphQL before: []string{` mutation { @@ -772,7 +791,7 @@ func TestBasic(t *testing.T) { assert.Equal(t, expected, actual) }, }, { - name: "OR operations complex fail", + name: "OR operations complex no wrap", // language=GraphQL before: []string{` mutation { @@ -837,7 +856,8 @@ func TestBasic(t *testing.T) { ).OrderBy( User.ID.Order(SortOrderAsc), ).Exec(ctx) - assert.Equal(t, builder.ErrOrWithoutAnd, err) + + assert.Equal(t, builder.ErrDuplicateField, errors.Unwrap(err)) }, }, { name: "id in",