From a51c50f6a3c8cc10b2ede9c5b1066ffd44c50a6d Mon Sep 17 00:00:00 2001 From: Ariel Mashraki Date: Mon, 19 Apr 2021 13:57:51 +0300 Subject: [PATCH] entc/gen: allow group-by by relations --- entc/gen/internal/bindata.go | 4 +- entc/gen/template/dialect/sql/group.tmpl | 19 +++- .../cascadelete/ent/comment_query.go | 19 +++- .../integration/cascadelete/ent/post_query.go | 19 +++- .../integration/cascadelete/ent/user_query.go | 19 +++- entc/integration/config/ent/user_query.go | 19 +++- entc/integration/customid/ent/blob_query.go | 19 +++- entc/integration/customid/ent/car_query.go | 19 +++- entc/integration/customid/ent/group_query.go | 19 +++- .../integration/customid/ent/mixinid_query.go | 19 +++- entc/integration/customid/ent/note_query.go | 19 +++- entc/integration/customid/ent/pet_query.go | 19 +++- entc/integration/customid/ent/user_query.go | 19 +++- entc/integration/edgefield/ent/card_query.go | 19 +++- entc/integration/edgefield/ent/info_query.go | 19 +++- .../edgefield/ent/metadata_query.go | 19 +++- entc/integration/edgefield/ent/pet_query.go | 19 +++- entc/integration/edgefield/ent/post_query.go | 19 +++- entc/integration/edgefield/ent/user_query.go | 19 +++- entc/integration/ent/card_query.go | 19 +++- entc/integration/ent/comment_query.go | 19 +++- entc/integration/ent/entql.go | 6 ++ entc/integration/ent/fieldtype_query.go | 19 +++- entc/integration/ent/file_query.go | 19 +++- entc/integration/ent/filetype_query.go | 19 +++- entc/integration/ent/goods_query.go | 19 +++- entc/integration/ent/group_query.go | 19 +++- entc/integration/ent/groupinfo_query.go | 19 +++- entc/integration/ent/item_query.go | 19 +++- entc/integration/ent/migrate/schema.go | 7 +- entc/integration/ent/mutation.go | 94 ++++++++++++++++++- entc/integration/ent/node_query.go | 19 +++- entc/integration/ent/pet.go | 12 +++ entc/integration/ent/pet/pet.go | 8 ++ entc/integration/ent/pet/where.go | 83 ++++++++++++++++ entc/integration/ent/pet_create.go | 35 +++++++ entc/integration/ent/pet_query.go | 27 ++++-- entc/integration/ent/pet_update.go | 70 ++++++++++++++ entc/integration/ent/runtime.go | 7 ++ entc/integration/ent/schema/pet.go | 2 + entc/integration/ent/spec_query.go | 19 +++- entc/integration/ent/task_query.go | 19 +++- entc/integration/ent/user_query.go | 19 +++- entc/integration/gremlin/ent/mutation.go | 94 ++++++++++++++++++- entc/integration/gremlin/ent/pet.go | 8 ++ entc/integration/gremlin/ent/pet/pet.go | 7 ++ entc/integration/gremlin/ent/pet/where.go | 71 ++++++++++++++ entc/integration/gremlin/ent/pet_create.go | 29 ++++++ entc/integration/gremlin/ent/pet_query.go | 8 +- entc/integration/gremlin/ent/pet_update.go | 54 +++++++++++ entc/integration/gremlin/ent/runtime.go | 7 ++ entc/integration/hooks/ent/card_query.go | 19 +++- entc/integration/hooks/ent/user_query.go | 19 +++- entc/integration/idtype/ent/user_query.go | 19 +++- entc/integration/integration_test.go | 32 +++++++ entc/integration/json/ent/user_query.go | 19 +++- entc/integration/migrate/entv1/car_query.go | 19 +++- .../migrate/entv1/conversion_query.go | 19 +++- .../migrate/entv1/customtype_query.go | 19 +++- entc/integration/migrate/entv1/user_query.go | 19 +++- entc/integration/migrate/entv2/car_query.go | 19 +++- .../migrate/entv2/conversion_query.go | 19 +++- .../migrate/entv2/customtype_query.go | 19 +++- entc/integration/migrate/entv2/group_query.go | 19 +++- entc/integration/migrate/entv2/media_query.go | 19 +++- entc/integration/migrate/entv2/pet_query.go | 19 +++- entc/integration/migrate/entv2/user_query.go | 19 +++- .../multischema/ent/group_query.go | 19 +++- entc/integration/multischema/ent/pet_query.go | 19 +++- .../integration/multischema/ent/user_query.go | 19 +++- entc/integration/privacy/ent/task_query.go | 19 +++- entc/integration/privacy/ent/team_query.go | 19 +++- entc/integration/privacy/ent/user_query.go | 19 +++- entc/integration/template/ent/group_query.go | 19 +++- entc/integration/template/ent/pet_query.go | 19 +++- entc/integration/template/ent/user_query.go | 19 +++- examples/edgeindex/ent/city_query.go | 19 +++- examples/edgeindex/ent/street_query.go | 19 +++- examples/entcpkg/ent/user_query.go | 19 +++- examples/m2m2types/ent/group_query.go | 19 +++- examples/m2m2types/ent/user_query.go | 19 +++- examples/m2mbidi/ent/user_query.go | 19 +++- examples/m2mrecur/ent/user_query.go | 19 +++- examples/o2m2types/ent/pet_query.go | 19 +++- examples/o2m2types/ent/user_query.go | 19 +++- examples/o2mrecur/ent/node_query.go | 19 +++- examples/o2o2types/ent/card_query.go | 19 +++- examples/o2o2types/ent/user_query.go | 19 +++- examples/o2obidi/ent/user_query.go | 19 +++- examples/o2orecur/ent/node_query.go | 19 +++- examples/privacyadmin/ent/user_query.go | 19 +++- examples/privacytenant/ent/group_query.go | 19 +++- examples/privacytenant/ent/tenant_query.go | 19 +++- examples/privacytenant/ent/user_query.go | 19 +++- examples/start/ent/car_query.go | 19 +++- examples/start/ent/group_query.go | 19 +++- examples/start/ent/user_query.go | 19 +++- examples/traversal/ent/group_query.go | 19 +++- examples/traversal/ent/pet_query.go | 19 +++- examples/traversal/ent/user_query.go | 19 +++- 100 files changed, 1829 insertions(+), 337 deletions(-) diff --git a/entc/gen/internal/bindata.go b/entc/gen/internal/bindata.go index 8fc6ae614..042abd1bb 100644 --- a/entc/gen/internal/bindata.go +++ b/entc/gen/internal/bindata.go @@ -792,7 +792,7 @@ func templateDialectSqlGlobalsTmpl() (*asset, error) { return a, nil } -var _templateDialectSqlGroupTmpl = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x94\x53\xdd\x6a\x1b\x3d\x10\xbd\xf6\x3e\xc5\xc4\xe4\x0b\xbb\xfe\x36\x72\x9a\xbb\xba\xe4\x22\x31\x69\x09\x94\xd0\xd6\xa5\x37\xa5\x14\x45\x3b\x5a\x8b\xc8\xa3\xb5\xa4\x75\x62\xcc\xbe\x7b\x19\x79\x9d\x1f\xb7\x69\x28\xd8\xac\xd0\x39\x33\xe7\xcc\x8f\x36\x9b\xf1\x28\x9b\xba\x66\xed\x4d\x3d\x8f\x70\x7a\xf2\xe6\xed\x71\xe3\x31\x20\x45\x78\x2f\x15\xde\x38\x77\x0b\x57\xa4\x04\x9c\x5b\x0b\x89\x14\x80\x71\xbf\xc2\x4a\x64\x5f\xe7\x26\x40\x70\xad\x57\x08\xca\x55\x08\x26\x80\x35\x0a\x29\x60\x05\x2d\x55\xe8\x21\xce\x11\xce\x1b\xa9\xe6\x08\xa7\xe2\x64\x87\x82\x76\x2d\x55\x99\xa1\x84\x7f\xbc\x9a\x5e\x5e\xcf\x2e\x41\x1b\x8b\xd0\xdf\x79\xe7\x22\x54\xc6\xa3\x8a\xce\xaf\xc1\x69\x88\x4f\xc4\xa2\x47\x14\xd9\x68\xdc\x75\x59\xc6\x35\x40\xed\xe2\xba\xc1\x09\x20\xc5\xda\x09\xe3\xc6\x48\x91\xff\x6a\x5c\x23\x09\xc6\x66\xca\x35\x08\xbb\x10\xa8\x50\x1b\x42\x18\x56\x46\x5a\x54\x71\x1c\x96\x76\x5c\x7b\xd7\x36\x43\xe8\x3a\x26\x1c\xde\xb4\xc6\x72\x05\x93\x33\x68\x64\x50\xd2\xc2\xa1\x48\x49\xc4\x45\x8f\xf4\x44\x8f\x0a\xcd\x6a\xcb\x7c\x38\x3f\x84\xb3\x9e\x6e\x49\x41\xfe\x8c\xdb\x75\x30\x7a\xaa\xd2\x75\x05\x84\xa5\x9d\x29\x49\xb9\x8a\xf7\xa0\x1c\x45\xbc\x8f\x62\xba\xfd\x96\xb0\x02\x43\x11\xbd\x96\x0a\x37\x5d\x01\xe8\xbd\xf3\xb0\xc9\x06\xda\x79\xf8\x59\x82\x4e\xea\x92\x6a\x84\x3d\x1d\xa1\x0d\xda\x2a\x30\x77\x60\x34\x1c\x30\x2c\x3e\x49\x75\x2b\x6b\x64\xf8\x9b\xb4\xa6\x9a\x3a\xdb\x2e\x28\xd7\x45\xa2\x0d\x3c\xc6\xd6\x13\x1c\x25\x4c\x46\xe3\xe8\x92\xf5\x36\xd7\x72\x81\x13\xd0\x25\xcb\x4f\x40\x2f\xa2\x48\xf7\x3a\x1f\x1a\x5a\x31\x17\x92\x18\xfc\xb7\x04\xf6\x95\x1a\x7a\x7c\xb3\x1e\x96\xa0\x8b\x2e\x1b\x0c\xba\x8c\x7f\x01\x6d\x1a\x2b\x5b\xde\x37\x1b\x96\xf6\x73\x8b\x7e\x9d\x17\x19\xbb\x45\x9f\x58\xbb\x08\x96\xcb\x8b\x77\xe9\xfa\xe0\x0c\xc8\xd8\xe4\xb7\xb7\x8b\xde\xa7\xfc\xde\xdd\x05\x8e\x3a\x0a\x4b\x2b\xbe\xb8\xbb\xb0\xe9\xb2\xc1\x92\xb3\x96\x20\x7d\x1d\x9e\x65\xfc\x83\xda\xbe\xa7\xca\xf3\xa9\x67\xaa\x78\x5f\xc2\x93\x64\x25\xb0\xdc\xab\x9e\x2a\xd4\xe8\x13\x55\x4c\xad\x0b\xc8\x8a\x3d\x85\x5d\xf2\xdc\x67\xfc\x38\x72\xa6\x94\xb0\x2a\xb2\x2e\xfb\x97\xc5\xe9\xcb\x80\x51\xca\xb6\x6b\xf0\xe6\xd5\x66\x67\x03\x95\x46\x9f\x9a\xb2\x90\xb7\x98\x7f\xff\x11\xa2\x37\x54\x97\x70\x52\x82\x45\xda\x97\xef\xf7\xa9\x80\xff\x7f\x43\x19\xa4\x50\x14\x8f\x49\xcf\x40\x36\x0d\x52\x95\xf7\x17\xe5\x0b\xdb\x29\x84\x28\x1e\x77\x99\xfe\xb2\xcc\xb4\xdd\xe4\x97\x05\x34\xe5\xbb\x9a\xd9\x49\xf7\xd8\xe7\xdd\xc8\xb7\xed\xd9\x45\xb0\xb4\xf8\xc0\x9b\x7a\xb1\x7e\xa1\xd6\xe4\x2e\x3d\x76\xa4\x8a\x5f\xf4\xaf\x00\x00\x00\xff\xff\xdf\xef\xe7\xf1\x38\x05\x00\x00") +var _templateDialectSqlGroupTmpl = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x94\x54\x61\x6f\xdb\x36\x10\xfd\x2c\xfd\x8a\xab\xd1\x15\x52\xa6\x52\x59\xbf\xcd\x83\x3f\xb4\x46\x36\x04\x18\x8a\x6d\x1e\xf6\x65\x18\x06\x86\x3c\xca\x44\x68\x52\x26\x29\x27\x86\xa1\xff\x3e\x1c\x25\xd9\x4a\x16\x34\x2d\x90\xc0\x02\xef\xf1\xbd\xc7\xe3\x3b\x9e\x4e\xf5\x55\xbe\x76\xed\xd1\xeb\x66\x1b\xe1\xc3\xf5\x0f\x3f\xbe\x6f\x3d\x06\xb4\x11\x7e\xe6\x02\xef\x9c\xbb\x87\x5b\x2b\x18\x7c\x34\x06\x12\x28\x00\xd5\xfd\x01\x25\xcb\xff\xdc\xea\x00\xc1\x75\x5e\x20\x08\x27\x11\x74\x00\xa3\x05\xda\x80\x12\x3a\x2b\xd1\x43\xdc\x22\x7c\x6c\xb9\xd8\x22\x7c\x60\xd7\x53\x15\x94\xeb\xac\xcc\xb5\x4d\xf5\x5f\x6f\xd7\x37\x9f\x37\x37\xa0\xb4\x41\x18\xd7\xbc\x73\x11\xa4\xf6\x28\xa2\xf3\x47\x70\x0a\xe2\x4c\x2c\x7a\x44\x96\x5f\xd5\x7d\x9f\xe7\x74\x06\x68\x5c\x3c\xb6\xb8\x04\xb4\xb1\x71\x4c\xbb\x1a\x6d\xa4\x7f\x51\x37\x68\x19\xd5\x36\xc2\xb5\x08\xd3\x16\x90\xa8\xb4\x45\x58\x48\xcd\x0d\x8a\x58\x87\xbd\xa9\x1b\xef\xba\x76\x01\x7d\x4f\x80\xb7\x77\x9d\x36\x74\x82\xe5\x0a\x5a\x1e\x04\x37\xf0\x96\x25\x12\xf6\x69\xac\x8c\x40\x8f\x02\xf5\x61\x40\x9e\xbf\xcf\xdb\x49\x4f\x75\x56\x40\xf1\x04\xdb\xf7\x70\x35\x57\xe9\xfb\x12\xc2\xde\x6c\x04\xb7\x85\x88\x8f\x20\x9c\x8d\xf8\x18\xd9\x7a\xf8\xad\xe0\x00\xda\x46\xf4\x8a\x0b\x3c\xf5\x25\xa0\xf7\xce\xc3\x29\xcf\x94\xf3\xf0\x6f\x05\x2a\xa9\x73\xdb\x20\x3c\xd3\x61\x4a\xa3\x91\x81\xb0\x99\x56\xf0\x86\xca\xec\x37\x2e\xee\x79\x83\x54\xfe\x8b\x1b\x2d\xd7\xce\x74\x3b\x5b\xa8\x32\xc1\x32\x8f\xb1\xf3\x16\xde\xa5\x1a\x8f\xda\xd9\x1b\xd2\x3b\x7d\xe6\x3b\x5c\x82\xaa\x48\x7e\x09\x6a\x17\x59\x5a\x57\xc5\x42\xdb\x03\x61\x21\x89\xc1\x77\x7b\x20\x5f\xa9\xa1\xef\xef\x8e\x8b\x0a\x54\xd9\xe7\x59\xd6\xe7\xf4\x17\xd0\xa4\x6b\x25\xcb\xcf\xcd\x86\xbd\xf9\xbd\x43\x7f\x2c\xca\x9c\xdc\xa2\x4f\xa8\x69\x07\xc9\x15\xe5\x4f\x69\xf9\xcd\x0a\xac\x36\xc9\xef\x68\x17\xbd\x4f\xfc\xde\x3d\x04\xda\xf5\x2e\xec\x0d\xfb\xc3\x3d\x84\x53\x9f\x67\x7b\x62\xad\x80\xfb\x26\x3c\x61\x7c\x41\xed\xb9\x27\xe9\xe9\x6b\x44\x8a\xf8\x58\xc1\x8c\xac\x02\x92\x7b\xd5\x93\x44\x85\x3e\x41\xd9\xda\xb8\x80\xa4\x38\x42\xc8\x25\xdd\xfb\x86\x86\xa3\x20\x48\x05\x87\x32\xef\xf3\x6f\x09\xce\x78\x0c\xb8\x4a\x6c\x53\x83\x4f\xaf\x36\x3b\xcf\x78\xd3\x78\x6c\xd2\x25\x13\x66\xc7\xef\xb1\xf8\xfb\x9f\x10\xbd\xb6\x4d\x05\xd7\x15\x18\xb4\x4f\x2c\x50\xa4\x6c\x28\xcb\x4b\xf6\xec\x17\xc2\x67\x87\xe4\xcd\x55\x56\xc0\xdb\x16\xad\x2c\x66\x8b\xc4\x52\x4c\x5e\x89\xbb\xcf\xb3\xba\x86\x5b\x05\xd6\x81\x48\xe9\x0c\xf0\x80\x1e\xc7\x9b\x43\x49\x2f\x05\x07\xd1\x85\xe8\x76\x30\xa7\xa7\xa6\x0d\x94\xf4\x90\x48\x54\xbc\x33\x31\xb1\x0d\x5b\x09\xa3\x43\x2a\x8e\xa3\xd1\xd1\x7b\x45\x87\x59\xcc\x12\xcb\xad\x4c\x98\x97\xa8\x03\x4b\x79\xa1\xce\x9c\x83\x34\x8c\x50\x28\xca\x12\x56\x2b\xb8\x4e\xa7\x9e\x8c\x7f\x55\x5f\x2f\xb3\x5a\xc2\xf7\x5f\xe8\xfa\xb7\x8d\xfc\xd9\xc3\xb9\xeb\xe3\x42\x75\x99\x81\x75\xa1\x12\x71\x7f\x21\x17\x17\xf2\x79\x03\x5e\x61\x14\x13\xcb\x99\x7a\x88\xe2\x84\x60\x8c\x0d\x57\x3b\x45\x7f\x82\xfd\x42\x7d\xff\x74\xfc\x7f\x37\x5f\x3e\x19\xf1\x24\xae\xf4\x02\xa3\x95\xf4\xcc\xfe\x17\x00\x00\xff\xff\x86\xd9\x1c\x0d\xcd\x06\x00\x00") func templateDialectSqlGroupTmplBytes() ([]byte, error) { return bindataRead( @@ -807,7 +807,7 @@ func templateDialectSqlGroupTmpl() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "template/dialect/sql/group.tmpl", size: 1336, mode: os.FileMode(420), modTime: time.Unix(1, 0)} + info := bindataFileInfo{name: "template/dialect/sql/group.tmpl", size: 1741, mode: os.FileMode(420), modTime: time.Unix(1, 0)} a := &asset{bytes: bytes, info: info} return a, nil } diff --git a/entc/gen/template/dialect/sql/group.tmpl b/entc/gen/template/dialect/sql/group.tmpl index d984e330f..cbb927cfe 100644 --- a/entc/gen/template/dialect/sql/group.tmpl +++ b/entc/gen/template/dialect/sql/group.tmpl @@ -32,11 +32,22 @@ func ({{ $receiver }} *{{ $builder }}) sqlScan(ctx context.Context, v interface{ func ({{ $receiver }} *{{ $builder }}) sqlQuery() *sql.Selector { selector := {{ $receiver }}.sql - columns := make([]string, 0, len({{ $receiver }}.fields) + len({{ $receiver}}.fns)) - columns = append(columns, {{ $receiver }}.fields...) + aggregation := make([]string, 0, len({{ $receiver}}.fns)) for _, fn := range {{ $receiver }}.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy({{ $receiver }}.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len({{ $receiver }}.fields) + len({{ $receiver}}.fns)) + for _, f := range {{ $receiver }}.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns({{ $receiver }}.fields...)...) } {{ end }} diff --git a/entc/integration/cascadelete/ent/comment_query.go b/entc/integration/cascadelete/ent/comment_query.go index f838b2821..30185e9a2 100644 --- a/entc/integration/cascadelete/ent/comment_query.go +++ b/entc/integration/cascadelete/ent/comment_query.go @@ -742,12 +742,23 @@ func (cgb *CommentGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (cgb *CommentGroupBy) sqlQuery() *sql.Selector { selector := cgb.sql - columns := make([]string, 0, len(cgb.fields)+len(cgb.fns)) - columns = append(columns, cgb.fields...) + aggregation := make([]string, 0, len(cgb.fns)) for _, fn := range cgb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(cgb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(cgb.fields)+len(cgb.fns)) + for _, f := range cgb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(cgb.fields...)...) } // CommentSelect is the builder for selecting fields of Comment entities. diff --git a/entc/integration/cascadelete/ent/post_query.go b/entc/integration/cascadelete/ent/post_query.go index dabc62434..81e4cf994 100644 --- a/entc/integration/cascadelete/ent/post_query.go +++ b/entc/integration/cascadelete/ent/post_query.go @@ -805,12 +805,23 @@ func (pgb *PostGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (pgb *PostGroupBy) sqlQuery() *sql.Selector { selector := pgb.sql - columns := make([]string, 0, len(pgb.fields)+len(pgb.fns)) - columns = append(columns, pgb.fields...) + aggregation := make([]string, 0, len(pgb.fns)) for _, fn := range pgb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(pgb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(pgb.fields)+len(pgb.fns)) + for _, f := range pgb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(pgb.fields...)...) } // PostSelect is the builder for selecting fields of Post entities. diff --git a/entc/integration/cascadelete/ent/user_query.go b/entc/integration/cascadelete/ent/user_query.go index 631539c2b..15787d096 100644 --- a/entc/integration/cascadelete/ent/user_query.go +++ b/entc/integration/cascadelete/ent/user_query.go @@ -742,12 +742,23 @@ func (ugb *UserGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (ugb *UserGroupBy) sqlQuery() *sql.Selector { selector := ugb.sql - columns := make([]string, 0, len(ugb.fields)+len(ugb.fns)) - columns = append(columns, ugb.fields...) + aggregation := make([]string, 0, len(ugb.fns)) for _, fn := range ugb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(ugb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(ugb.fields)+len(ugb.fns)) + for _, f := range ugb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(ugb.fields...)...) } // UserSelect is the builder for selecting fields of User entities. diff --git a/entc/integration/config/ent/user_query.go b/entc/integration/config/ent/user_query.go index 84f79a6e3..9dbc17857 100644 --- a/entc/integration/config/ent/user_query.go +++ b/entc/integration/config/ent/user_query.go @@ -674,12 +674,23 @@ func (ugb *UserGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (ugb *UserGroupBy) sqlQuery() *sql.Selector { selector := ugb.sql - columns := make([]string, 0, len(ugb.fields)+len(ugb.fns)) - columns = append(columns, ugb.fields...) + aggregation := make([]string, 0, len(ugb.fns)) for _, fn := range ugb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(ugb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(ugb.fields)+len(ugb.fns)) + for _, f := range ugb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(ugb.fields...)...) } // UserSelect is the builder for selecting fields of User entities. diff --git a/entc/integration/customid/ent/blob_query.go b/entc/integration/customid/ent/blob_query.go index 1486f2981..e5f8db9be 100644 --- a/entc/integration/customid/ent/blob_query.go +++ b/entc/integration/customid/ent/blob_query.go @@ -855,12 +855,23 @@ func (bgb *BlobGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (bgb *BlobGroupBy) sqlQuery() *sql.Selector { selector := bgb.sql - columns := make([]string, 0, len(bgb.fields)+len(bgb.fns)) - columns = append(columns, bgb.fields...) + aggregation := make([]string, 0, len(bgb.fns)) for _, fn := range bgb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(bgb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(bgb.fields)+len(bgb.fns)) + for _, f := range bgb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(bgb.fields...)...) } // BlobSelect is the builder for selecting fields of Blob entities. diff --git a/entc/integration/customid/ent/car_query.go b/entc/integration/customid/ent/car_query.go index b26ae3125..f00d6c02a 100644 --- a/entc/integration/customid/ent/car_query.go +++ b/entc/integration/customid/ent/car_query.go @@ -753,12 +753,23 @@ func (cgb *CarGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (cgb *CarGroupBy) sqlQuery() *sql.Selector { selector := cgb.sql - columns := make([]string, 0, len(cgb.fields)+len(cgb.fns)) - columns = append(columns, cgb.fields...) + aggregation := make([]string, 0, len(cgb.fns)) for _, fn := range cgb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(cgb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(cgb.fields)+len(cgb.fns)) + for _, f := range cgb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(cgb.fields...)...) } // CarSelect is the builder for selecting fields of Car entities. diff --git a/entc/integration/customid/ent/group_query.go b/entc/integration/customid/ent/group_query.go index 6b54d51af..f4f94d174 100644 --- a/entc/integration/customid/ent/group_query.go +++ b/entc/integration/customid/ent/group_query.go @@ -758,12 +758,23 @@ func (ggb *GroupGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (ggb *GroupGroupBy) sqlQuery() *sql.Selector { selector := ggb.sql - columns := make([]string, 0, len(ggb.fields)+len(ggb.fns)) - columns = append(columns, ggb.fields...) + aggregation := make([]string, 0, len(ggb.fns)) for _, fn := range ggb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(ggb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(ggb.fields)+len(ggb.fns)) + for _, f := range ggb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(ggb.fields...)...) } // GroupSelect is the builder for selecting fields of Group entities. diff --git a/entc/integration/customid/ent/mixinid_query.go b/entc/integration/customid/ent/mixinid_query.go index 254ef72fc..60e422e4c 100644 --- a/entc/integration/customid/ent/mixinid_query.go +++ b/entc/integration/customid/ent/mixinid_query.go @@ -675,12 +675,23 @@ func (migb *MixinIDGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (migb *MixinIDGroupBy) sqlQuery() *sql.Selector { selector := migb.sql - columns := make([]string, 0, len(migb.fields)+len(migb.fns)) - columns = append(columns, migb.fields...) + aggregation := make([]string, 0, len(migb.fns)) for _, fn := range migb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(migb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(migb.fields)+len(migb.fns)) + for _, f := range migb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(migb.fields...)...) } // MixinIDSelect is the builder for selecting fields of MixinID entities. diff --git a/entc/integration/customid/ent/note_query.go b/entc/integration/customid/ent/note_query.go index 53da8fcf8..138713134 100644 --- a/entc/integration/customid/ent/note_query.go +++ b/entc/integration/customid/ent/note_query.go @@ -819,12 +819,23 @@ func (ngb *NoteGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (ngb *NoteGroupBy) sqlQuery() *sql.Selector { selector := ngb.sql - columns := make([]string, 0, len(ngb.fields)+len(ngb.fns)) - columns = append(columns, ngb.fields...) + aggregation := make([]string, 0, len(ngb.fns)) for _, fn := range ngb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(ngb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(ngb.fields)+len(ngb.fns)) + for _, f := range ngb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(ngb.fields...)...) } // NoteSelect is the builder for selecting fields of Note entities. diff --git a/entc/integration/customid/ent/pet_query.go b/entc/integration/customid/ent/pet_query.go index 9dbbb58ff..fb3b1fc47 100644 --- a/entc/integration/customid/ent/pet_query.go +++ b/entc/integration/customid/ent/pet_query.go @@ -962,12 +962,23 @@ func (pgb *PetGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (pgb *PetGroupBy) sqlQuery() *sql.Selector { selector := pgb.sql - columns := make([]string, 0, len(pgb.fields)+len(pgb.fns)) - columns = append(columns, pgb.fields...) + aggregation := make([]string, 0, len(pgb.fns)) for _, fn := range pgb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(pgb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(pgb.fields)+len(pgb.fns)) + for _, f := range pgb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(pgb.fields...)...) } // PetSelect is the builder for selecting fields of Pet entities. diff --git a/entc/integration/customid/ent/user_query.go b/entc/integration/customid/ent/user_query.go index 6246af0a7..ae19cf230 100644 --- a/entc/integration/customid/ent/user_query.go +++ b/entc/integration/customid/ent/user_query.go @@ -962,12 +962,23 @@ func (ugb *UserGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (ugb *UserGroupBy) sqlQuery() *sql.Selector { selector := ugb.sql - columns := make([]string, 0, len(ugb.fields)+len(ugb.fns)) - columns = append(columns, ugb.fields...) + aggregation := make([]string, 0, len(ugb.fns)) for _, fn := range ugb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(ugb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(ugb.fields)+len(ugb.fns)) + for _, f := range ugb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(ugb.fields...)...) } // UserSelect is the builder for selecting fields of User entities. diff --git a/entc/integration/edgefield/ent/card_query.go b/entc/integration/edgefield/ent/card_query.go index a61fddbf9..1102ec317 100644 --- a/entc/integration/edgefield/ent/card_query.go +++ b/entc/integration/edgefield/ent/card_query.go @@ -742,12 +742,23 @@ func (cgb *CardGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (cgb *CardGroupBy) sqlQuery() *sql.Selector { selector := cgb.sql - columns := make([]string, 0, len(cgb.fields)+len(cgb.fns)) - columns = append(columns, cgb.fields...) + aggregation := make([]string, 0, len(cgb.fns)) for _, fn := range cgb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(cgb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(cgb.fields)+len(cgb.fns)) + for _, f := range cgb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(cgb.fields...)...) } // CardSelect is the builder for selecting fields of Card entities. diff --git a/entc/integration/edgefield/ent/info_query.go b/entc/integration/edgefield/ent/info_query.go index bdc9ebc21..5e2e4229e 100644 --- a/entc/integration/edgefield/ent/info_query.go +++ b/entc/integration/edgefield/ent/info_query.go @@ -742,12 +742,23 @@ func (igb *InfoGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (igb *InfoGroupBy) sqlQuery() *sql.Selector { selector := igb.sql - columns := make([]string, 0, len(igb.fields)+len(igb.fns)) - columns = append(columns, igb.fields...) + aggregation := make([]string, 0, len(igb.fns)) for _, fn := range igb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(igb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(igb.fields)+len(igb.fns)) + for _, f := range igb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(igb.fields...)...) } // InfoSelect is the builder for selecting fields of Info entities. diff --git a/entc/integration/edgefield/ent/metadata_query.go b/entc/integration/edgefield/ent/metadata_query.go index ea20cdbbf..22ffbf960 100644 --- a/entc/integration/edgefield/ent/metadata_query.go +++ b/entc/integration/edgefield/ent/metadata_query.go @@ -742,12 +742,23 @@ func (mgb *MetadataGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (mgb *MetadataGroupBy) sqlQuery() *sql.Selector { selector := mgb.sql - columns := make([]string, 0, len(mgb.fields)+len(mgb.fns)) - columns = append(columns, mgb.fields...) + aggregation := make([]string, 0, len(mgb.fns)) for _, fn := range mgb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(mgb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(mgb.fields)+len(mgb.fns)) + for _, f := range mgb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(mgb.fields...)...) } // MetadataSelect is the builder for selecting fields of Metadata entities. diff --git a/entc/integration/edgefield/ent/pet_query.go b/entc/integration/edgefield/ent/pet_query.go index 6d5e7f07c..8605f03fa 100644 --- a/entc/integration/edgefield/ent/pet_query.go +++ b/entc/integration/edgefield/ent/pet_query.go @@ -742,12 +742,23 @@ func (pgb *PetGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (pgb *PetGroupBy) sqlQuery() *sql.Selector { selector := pgb.sql - columns := make([]string, 0, len(pgb.fields)+len(pgb.fns)) - columns = append(columns, pgb.fields...) + aggregation := make([]string, 0, len(pgb.fns)) for _, fn := range pgb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(pgb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(pgb.fields)+len(pgb.fns)) + for _, f := range pgb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(pgb.fields...)...) } // PetSelect is the builder for selecting fields of Pet entities. diff --git a/entc/integration/edgefield/ent/post_query.go b/entc/integration/edgefield/ent/post_query.go index d91e163b2..bcfa7354e 100644 --- a/entc/integration/edgefield/ent/post_query.go +++ b/entc/integration/edgefield/ent/post_query.go @@ -745,12 +745,23 @@ func (pgb *PostGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (pgb *PostGroupBy) sqlQuery() *sql.Selector { selector := pgb.sql - columns := make([]string, 0, len(pgb.fields)+len(pgb.fns)) - columns = append(columns, pgb.fields...) + aggregation := make([]string, 0, len(pgb.fns)) for _, fn := range pgb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(pgb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(pgb.fields)+len(pgb.fns)) + for _, f := range pgb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(pgb.fields...)...) } // PostSelect is the builder for selecting fields of Post entities. diff --git a/entc/integration/edgefield/ent/user_query.go b/entc/integration/edgefield/ent/user_query.go index 479fb4062..71c6f7514 100644 --- a/entc/integration/edgefield/ent/user_query.go +++ b/entc/integration/edgefield/ent/user_query.go @@ -1111,12 +1111,23 @@ func (ugb *UserGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (ugb *UserGroupBy) sqlQuery() *sql.Selector { selector := ugb.sql - columns := make([]string, 0, len(ugb.fields)+len(ugb.fns)) - columns = append(columns, ugb.fields...) + aggregation := make([]string, 0, len(ugb.fns)) for _, fn := range ugb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(ugb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(ugb.fields)+len(ugb.fns)) + for _, f := range ugb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(ugb.fields...)...) } // UserSelect is the builder for selecting fields of User entities. diff --git a/entc/integration/ent/card_query.go b/entc/integration/ent/card_query.go index 6b051034d..50367bbc7 100644 --- a/entc/integration/ent/card_query.go +++ b/entc/integration/ent/card_query.go @@ -856,12 +856,23 @@ func (cgb *CardGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (cgb *CardGroupBy) sqlQuery() *sql.Selector { selector := cgb.sql - columns := make([]string, 0, len(cgb.fields)+len(cgb.fns)) - columns = append(columns, cgb.fields...) + aggregation := make([]string, 0, len(cgb.fns)) for _, fn := range cgb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(cgb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(cgb.fields)+len(cgb.fns)) + for _, f := range cgb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(cgb.fields...)...) } // CardSelect is the builder for selecting fields of Card entities. diff --git a/entc/integration/ent/comment_query.go b/entc/integration/ent/comment_query.go index 7713ef878..7cf11c13f 100644 --- a/entc/integration/ent/comment_query.go +++ b/entc/integration/ent/comment_query.go @@ -674,12 +674,23 @@ func (cgb *CommentGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (cgb *CommentGroupBy) sqlQuery() *sql.Selector { selector := cgb.sql - columns := make([]string, 0, len(cgb.fields)+len(cgb.fns)) - columns = append(columns, cgb.fields...) + aggregation := make([]string, 0, len(cgb.fns)) for _, fn := range cgb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(cgb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(cgb.fields)+len(cgb.fns)) + for _, f := range cgb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(cgb.fields...)...) } // CommentSelect is the builder for selecting fields of Comment entities. diff --git a/entc/integration/ent/entql.go b/entc/integration/ent/entql.go index cba6ed613..c3a5865ac 100644 --- a/entc/integration/ent/entql.go +++ b/entc/integration/ent/entql.go @@ -250,6 +250,7 @@ var schemaGraph = func() *sqlgraph.Schema { }, Type: "Pet", Fields: map[string]*sqlgraph.FieldSpec{ + pet.FieldAge: {Type: field.TypeFloat64, Column: pet.FieldAge}, pet.FieldName: {Type: field.TypeString, Column: pet.FieldName}, pet.FieldUUID: {Type: field.TypeUUID, Column: pet.FieldUUID}, }, @@ -1642,6 +1643,11 @@ func (f *PetFilter) WhereID(p entql.IntP) { f.Where(p.Field(pet.FieldID)) } +// WhereAge applies the entql float64 predicate on the age field. +func (f *PetFilter) WhereAge(p entql.Float64P) { + f.Where(p.Field(pet.FieldAge)) +} + // WhereName applies the entql string predicate on the name field. func (f *PetFilter) WhereName(p entql.StringP) { f.Where(p.Field(pet.FieldName)) diff --git a/entc/integration/ent/fieldtype_query.go b/entc/integration/ent/fieldtype_query.go index 281dc58a1..e8d9c7cc9 100644 --- a/entc/integration/ent/fieldtype_query.go +++ b/entc/integration/ent/fieldtype_query.go @@ -679,12 +679,23 @@ func (ftgb *FieldTypeGroupBy) sqlScan(ctx context.Context, v interface{}) error func (ftgb *FieldTypeGroupBy) sqlQuery() *sql.Selector { selector := ftgb.sql - columns := make([]string, 0, len(ftgb.fields)+len(ftgb.fns)) - columns = append(columns, ftgb.fields...) + aggregation := make([]string, 0, len(ftgb.fns)) for _, fn := range ftgb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(ftgb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(ftgb.fields)+len(ftgb.fns)) + for _, f := range ftgb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(ftgb.fields...)...) } // FieldTypeSelect is the builder for selecting fields of FieldType entities. diff --git a/entc/integration/ent/file_query.go b/entc/integration/ent/file_query.go index da112facf..a64eaa29d 100644 --- a/entc/integration/ent/file_query.go +++ b/entc/integration/ent/file_query.go @@ -886,12 +886,23 @@ func (fgb *FileGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (fgb *FileGroupBy) sqlQuery() *sql.Selector { selector := fgb.sql - columns := make([]string, 0, len(fgb.fields)+len(fgb.fns)) - columns = append(columns, fgb.fields...) + aggregation := make([]string, 0, len(fgb.fns)) for _, fn := range fgb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(fgb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(fgb.fields)+len(fgb.fns)) + for _, f := range fgb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(fgb.fields...)...) } // FileSelect is the builder for selecting fields of File entities. diff --git a/entc/integration/ent/filetype_query.go b/entc/integration/ent/filetype_query.go index cde069e55..6af889e23 100644 --- a/entc/integration/ent/filetype_query.go +++ b/entc/integration/ent/filetype_query.go @@ -746,12 +746,23 @@ func (ftgb *FileTypeGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (ftgb *FileTypeGroupBy) sqlQuery() *sql.Selector { selector := ftgb.sql - columns := make([]string, 0, len(ftgb.fields)+len(ftgb.fns)) - columns = append(columns, ftgb.fields...) + aggregation := make([]string, 0, len(ftgb.fns)) for _, fn := range ftgb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(ftgb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(ftgb.fields)+len(ftgb.fns)) + for _, f := range ftgb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(ftgb.fields...)...) } // FileTypeSelect is the builder for selecting fields of FileType entities. diff --git a/entc/integration/ent/goods_query.go b/entc/integration/ent/goods_query.go index 8319c7736..644588715 100644 --- a/entc/integration/ent/goods_query.go +++ b/entc/integration/ent/goods_query.go @@ -650,12 +650,23 @@ func (ggb *GoodsGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (ggb *GoodsGroupBy) sqlQuery() *sql.Selector { selector := ggb.sql - columns := make([]string, 0, len(ggb.fields)+len(ggb.fns)) - columns = append(columns, ggb.fields...) + aggregation := make([]string, 0, len(ggb.fns)) for _, fn := range ggb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(ggb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(ggb.fields)+len(ggb.fns)) + for _, f := range ggb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(ggb.fields...)...) } // GoodsSelect is the builder for selecting fields of Goods entities. diff --git a/entc/integration/ent/group_query.go b/entc/integration/ent/group_query.go index f2893dfb2..4c9da9c7f 100644 --- a/entc/integration/ent/group_query.go +++ b/entc/integration/ent/group_query.go @@ -987,12 +987,23 @@ func (ggb *GroupGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (ggb *GroupGroupBy) sqlQuery() *sql.Selector { selector := ggb.sql - columns := make([]string, 0, len(ggb.fields)+len(ggb.fns)) - columns = append(columns, ggb.fields...) + aggregation := make([]string, 0, len(ggb.fns)) for _, fn := range ggb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(ggb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(ggb.fields)+len(ggb.fns)) + for _, f := range ggb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(ggb.fields...)...) } // GroupSelect is the builder for selecting fields of Group entities. diff --git a/entc/integration/ent/groupinfo_query.go b/entc/integration/ent/groupinfo_query.go index 555c4f731..1b62edfec 100644 --- a/entc/integration/ent/groupinfo_query.go +++ b/entc/integration/ent/groupinfo_query.go @@ -746,12 +746,23 @@ func (gigb *GroupInfoGroupBy) sqlScan(ctx context.Context, v interface{}) error func (gigb *GroupInfoGroupBy) sqlQuery() *sql.Selector { selector := gigb.sql - columns := make([]string, 0, len(gigb.fields)+len(gigb.fns)) - columns = append(columns, gigb.fields...) + aggregation := make([]string, 0, len(gigb.fns)) for _, fn := range gigb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(gigb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(gigb.fields)+len(gigb.fns)) + for _, f := range gigb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(gigb.fields...)...) } // GroupInfoSelect is the builder for selecting fields of GroupInfo entities. diff --git a/entc/integration/ent/item_query.go b/entc/integration/ent/item_query.go index d6008f322..7541715e9 100644 --- a/entc/integration/ent/item_query.go +++ b/entc/integration/ent/item_query.go @@ -650,12 +650,23 @@ func (igb *ItemGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (igb *ItemGroupBy) sqlQuery() *sql.Selector { selector := igb.sql - columns := make([]string, 0, len(igb.fields)+len(igb.fns)) - columns = append(columns, igb.fields...) + aggregation := make([]string, 0, len(igb.fns)) for _, fn := range igb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(igb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(igb.fields)+len(igb.fns)) + for _, f := range igb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(igb.fields...)...) } // ItemSelect is the builder for selecting fields of Item entities. diff --git a/entc/integration/ent/migrate/schema.go b/entc/integration/ent/migrate/schema.go index a16b63924..44a9fab42 100644 --- a/entc/integration/ent/migrate/schema.go +++ b/entc/integration/ent/migrate/schema.go @@ -303,6 +303,7 @@ var ( // PetColumns holds the columns for the "pet" table. PetColumns = []*schema.Column{ {Name: "id", Type: field.TypeInt, Increment: true}, + {Name: "age", Type: field.TypeFloat64, Default: 0}, {Name: "name", Type: field.TypeString}, {Name: "uuid", Type: field.TypeUUID, Nullable: true}, {Name: "user_pets", Type: field.TypeInt, Nullable: true}, @@ -316,13 +317,13 @@ var ( ForeignKeys: []*schema.ForeignKey{ { Symbol: "pet_users_pets", - Columns: []*schema.Column{PetColumns[3]}, + Columns: []*schema.Column{PetColumns[4]}, RefColumns: []*schema.Column{UsersColumns[0]}, OnDelete: schema.SetNull, }, { Symbol: "pet_users_team", - Columns: []*schema.Column{PetColumns[4]}, + Columns: []*schema.Column{PetColumns[5]}, RefColumns: []*schema.Column{UsersColumns[0]}, OnDelete: schema.SetNull, }, @@ -331,7 +332,7 @@ var ( { Name: "pet_name_user_pets", Unique: false, - Columns: []*schema.Column{PetColumns[1], PetColumns[3]}, + Columns: []*schema.Column{PetColumns[2], PetColumns[4]}, }, }, } diff --git a/entc/integration/ent/mutation.go b/entc/integration/ent/mutation.go index 0d9ecb010..3cb8735b7 100644 --- a/entc/integration/ent/mutation.go +++ b/entc/integration/ent/mutation.go @@ -9962,6 +9962,8 @@ type PetMutation struct { op Op typ string id *int + age *float64 + addage *float64 name *string uuid *uuid.UUID clearedFields map[string]struct{} @@ -10053,6 +10055,62 @@ func (m *PetMutation) ID() (id int, exists bool) { return *m.id, true } +// SetAge sets the "age" field. +func (m *PetMutation) SetAge(f float64) { + m.age = &f + m.addage = nil +} + +// Age returns the value of the "age" field in the mutation. +func (m *PetMutation) Age() (r float64, exists bool) { + v := m.age + if v == nil { + return + } + return *v, true +} + +// OldAge returns the old "age" field's value of the Pet entity. +// If the Pet object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *PetMutation) OldAge(ctx context.Context) (v float64, err error) { + if !m.op.Is(OpUpdateOne) { + return v, fmt.Errorf("OldAge is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, fmt.Errorf("OldAge requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldAge: %w", err) + } + return oldValue.Age, nil +} + +// AddAge adds f to the "age" field. +func (m *PetMutation) AddAge(f float64) { + if m.addage != nil { + *m.addage += f + } else { + m.addage = &f + } +} + +// AddedAge returns the value that was added to the "age" field in this mutation. +func (m *PetMutation) AddedAge() (r float64, exists bool) { + v := m.addage + if v == nil { + return + } + return *v, true +} + +// ResetAge resets all changes to the "age" field. +func (m *PetMutation) ResetAge() { + m.age = nil + m.addage = nil +} + // SetName sets the "name" field. func (m *PetMutation) SetName(s string) { m.name = &s @@ -10230,7 +10288,10 @@ func (m *PetMutation) Type() string { // order to get all numeric fields that were incremented/decremented, call // AddedFields(). func (m *PetMutation) Fields() []string { - fields := make([]string, 0, 2) + fields := make([]string, 0, 3) + if m.age != nil { + fields = append(fields, pet.FieldAge) + } if m.name != nil { fields = append(fields, pet.FieldName) } @@ -10245,6 +10306,8 @@ func (m *PetMutation) Fields() []string { // schema. func (m *PetMutation) Field(name string) (ent.Value, bool) { switch name { + case pet.FieldAge: + return m.Age() case pet.FieldName: return m.Name() case pet.FieldUUID: @@ -10258,6 +10321,8 @@ func (m *PetMutation) Field(name string) (ent.Value, bool) { // database failed. func (m *PetMutation) OldField(ctx context.Context, name string) (ent.Value, error) { switch name { + case pet.FieldAge: + return m.OldAge(ctx) case pet.FieldName: return m.OldName(ctx) case pet.FieldUUID: @@ -10271,6 +10336,13 @@ func (m *PetMutation) OldField(ctx context.Context, name string) (ent.Value, err // type. func (m *PetMutation) SetField(name string, value ent.Value) error { switch name { + case pet.FieldAge: + v, ok := value.(float64) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetAge(v) + return nil case pet.FieldName: v, ok := value.(string) if !ok { @@ -10292,13 +10364,21 @@ func (m *PetMutation) SetField(name string, value ent.Value) error { // AddedFields returns all numeric fields that were incremented/decremented during // this mutation. func (m *PetMutation) AddedFields() []string { - return nil + var fields []string + if m.addage != nil { + fields = append(fields, pet.FieldAge) + } + return fields } // AddedField returns the numeric value that was incremented/decremented on a field // with the given name. The second boolean return value indicates that this field // was not set, or was not defined in the schema. func (m *PetMutation) AddedField(name string) (ent.Value, bool) { + switch name { + case pet.FieldAge: + return m.AddedAge() + } return nil, false } @@ -10307,6 +10387,13 @@ func (m *PetMutation) AddedField(name string) (ent.Value, bool) { // type. func (m *PetMutation) AddField(name string, value ent.Value) error { switch name { + case pet.FieldAge: + v, ok := value.(float64) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.AddAge(v) + return nil } return fmt.Errorf("unknown Pet numeric field %s", name) } @@ -10343,6 +10430,9 @@ func (m *PetMutation) ClearField(name string) error { // It returns an error if the field is not defined in the schema. func (m *PetMutation) ResetField(name string) error { switch name { + case pet.FieldAge: + m.ResetAge() + return nil case pet.FieldName: m.ResetName() return nil diff --git a/entc/integration/ent/node_query.go b/entc/integration/ent/node_query.go index d9c895704..b76d2c830 100644 --- a/entc/integration/ent/node_query.go +++ b/entc/integration/ent/node_query.go @@ -817,12 +817,23 @@ func (ngb *NodeGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (ngb *NodeGroupBy) sqlQuery() *sql.Selector { selector := ngb.sql - columns := make([]string, 0, len(ngb.fields)+len(ngb.fns)) - columns = append(columns, ngb.fields...) + aggregation := make([]string, 0, len(ngb.fns)) for _, fn := range ngb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(ngb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(ngb.fields)+len(ngb.fns)) + for _, f := range ngb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(ngb.fields...)...) } // NodeSelect is the builder for selecting fields of Node entities. diff --git a/entc/integration/ent/pet.go b/entc/integration/ent/pet.go index 0df5a41a1..6972ffed7 100644 --- a/entc/integration/ent/pet.go +++ b/entc/integration/ent/pet.go @@ -21,6 +21,8 @@ type Pet struct { config `json:"-"` // ID of the ent. ID int `json:"id,omitempty"` + // Age holds the value of the "age" field. + Age float64 `json:"age,omitempty"` // Name holds the value of the "name" field. Name string `json:"name,omitempty"` // UUID holds the value of the "uuid" field. @@ -76,6 +78,8 @@ func (*Pet) scanValues(columns []string) ([]interface{}, error) { values := make([]interface{}, len(columns)) for i := range columns { switch columns[i] { + case pet.FieldAge: + values[i] = new(sql.NullFloat64) case pet.FieldID: values[i] = new(sql.NullInt64) case pet.FieldName: @@ -107,6 +111,12 @@ func (pe *Pet) assignValues(columns []string, values []interface{}) error { return fmt.Errorf("unexpected type %T for field id", value) } pe.ID = int(value.Int64) + case pet.FieldAge: + if value, ok := values[i].(*sql.NullFloat64); !ok { + return fmt.Errorf("unexpected type %T for field age", values[i]) + } else if value.Valid { + pe.Age = value.Float64 + } case pet.FieldName: if value, ok := values[i].(*sql.NullString); !ok { return fmt.Errorf("unexpected type %T for field name", values[i]) @@ -171,6 +181,8 @@ func (pe *Pet) String() string { var builder strings.Builder builder.WriteString("Pet(") builder.WriteString(fmt.Sprintf("id=%v", pe.ID)) + builder.WriteString(", age=") + builder.WriteString(fmt.Sprintf("%v", pe.Age)) builder.WriteString(", name=") builder.WriteString(pe.Name) builder.WriteString(", uuid=") diff --git a/entc/integration/ent/pet/pet.go b/entc/integration/ent/pet/pet.go index d12ae1521..4db46a114 100644 --- a/entc/integration/ent/pet/pet.go +++ b/entc/integration/ent/pet/pet.go @@ -11,6 +11,8 @@ const ( Label = "pet" // FieldID holds the string denoting the id field in the database. FieldID = "id" + // FieldAge holds the string denoting the age field in the database. + FieldAge = "age" // FieldName holds the string denoting the name field in the database. FieldName = "name" // FieldUUID holds the string denoting the uuid field in the database. @@ -40,6 +42,7 @@ const ( // Columns holds all SQL columns for pet fields. var Columns = []string{ FieldID, + FieldAge, FieldName, FieldUUID, } @@ -66,4 +69,9 @@ func ValidColumn(column string) bool { return false } +var ( + // DefaultAge holds the default value on creation for the "age" field. + DefaultAge float64 +) + // comment from another template. diff --git a/entc/integration/ent/pet/where.go b/entc/integration/ent/pet/where.go index 65d97c39c..774a0b073 100644 --- a/entc/integration/ent/pet/where.go +++ b/entc/integration/ent/pet/where.go @@ -96,6 +96,13 @@ func IDLTE(id int) predicate.Pet { }) } +// Age applies equality check predicate on the "age" field. It's identical to AgeEQ. +func Age(v float64) predicate.Pet { + return predicate.Pet(func(s *sql.Selector) { + s.Where(sql.EQ(s.C(FieldAge), v)) + }) +} + // Name applies equality check predicate on the "name" field. It's identical to NameEQ. func Name(v string) predicate.Pet { return predicate.Pet(func(s *sql.Selector) { @@ -110,6 +117,82 @@ func UUID(v uuid.UUID) predicate.Pet { }) } +// AgeEQ applies the EQ predicate on the "age" field. +func AgeEQ(v float64) predicate.Pet { + return predicate.Pet(func(s *sql.Selector) { + s.Where(sql.EQ(s.C(FieldAge), v)) + }) +} + +// AgeNEQ applies the NEQ predicate on the "age" field. +func AgeNEQ(v float64) predicate.Pet { + return predicate.Pet(func(s *sql.Selector) { + s.Where(sql.NEQ(s.C(FieldAge), v)) + }) +} + +// AgeIn applies the In predicate on the "age" field. +func AgeIn(vs ...float64) predicate.Pet { + v := make([]interface{}, len(vs)) + for i := range v { + v[i] = vs[i] + } + return predicate.Pet(func(s *sql.Selector) { + // if not arguments were provided, append the FALSE constants, + // since we can't apply "IN ()". This will make this predicate falsy. + if len(v) == 0 { + s.Where(sql.False()) + return + } + s.Where(sql.In(s.C(FieldAge), v...)) + }) +} + +// AgeNotIn applies the NotIn predicate on the "age" field. +func AgeNotIn(vs ...float64) predicate.Pet { + v := make([]interface{}, len(vs)) + for i := range v { + v[i] = vs[i] + } + return predicate.Pet(func(s *sql.Selector) { + // if not arguments were provided, append the FALSE constants, + // since we can't apply "IN ()". This will make this predicate falsy. + if len(v) == 0 { + s.Where(sql.False()) + return + } + s.Where(sql.NotIn(s.C(FieldAge), v...)) + }) +} + +// AgeGT applies the GT predicate on the "age" field. +func AgeGT(v float64) predicate.Pet { + return predicate.Pet(func(s *sql.Selector) { + s.Where(sql.GT(s.C(FieldAge), v)) + }) +} + +// AgeGTE applies the GTE predicate on the "age" field. +func AgeGTE(v float64) predicate.Pet { + return predicate.Pet(func(s *sql.Selector) { + s.Where(sql.GTE(s.C(FieldAge), v)) + }) +} + +// AgeLT applies the LT predicate on the "age" field. +func AgeLT(v float64) predicate.Pet { + return predicate.Pet(func(s *sql.Selector) { + s.Where(sql.LT(s.C(FieldAge), v)) + }) +} + +// AgeLTE applies the LTE predicate on the "age" field. +func AgeLTE(v float64) predicate.Pet { + return predicate.Pet(func(s *sql.Selector) { + s.Where(sql.LTE(s.C(FieldAge), v)) + }) +} + // NameEQ applies the EQ predicate on the "name" field. func NameEQ(v string) predicate.Pet { return predicate.Pet(func(s *sql.Selector) { diff --git a/entc/integration/ent/pet_create.go b/entc/integration/ent/pet_create.go index 82749ae7c..ab1721183 100644 --- a/entc/integration/ent/pet_create.go +++ b/entc/integration/ent/pet_create.go @@ -25,6 +25,20 @@ type PetCreate struct { hooks []Hook } +// SetAge sets the "age" field. +func (pc *PetCreate) SetAge(f float64) *PetCreate { + pc.mutation.SetAge(f) + return pc +} + +// SetNillableAge sets the "age" field if the given value is not nil. +func (pc *PetCreate) SetNillableAge(f *float64) *PetCreate { + if f != nil { + pc.SetAge(*f) + } + return pc +} + // SetName sets the "name" field. func (pc *PetCreate) SetName(s string) *PetCreate { pc.mutation.SetName(s) @@ -86,6 +100,7 @@ func (pc *PetCreate) Save(ctx context.Context) (*Pet, error) { err error node *Pet ) + pc.defaults() if len(pc.hooks) == 0 { if err = pc.check(); err != nil { return nil, err @@ -124,8 +139,19 @@ func (pc *PetCreate) SaveX(ctx context.Context) *Pet { return v } +// defaults sets the default values of the builder before save. +func (pc *PetCreate) defaults() { + if _, ok := pc.mutation.Age(); !ok { + v := pet.DefaultAge + pc.mutation.SetAge(v) + } +} + // check runs all checks and user-defined validators on the builder. func (pc *PetCreate) check() error { + if _, ok := pc.mutation.Age(); !ok { + return &ValidationError{Name: "age", err: errors.New("ent: missing required field \"age\"")} + } if _, ok := pc.mutation.Name(); !ok { return &ValidationError{Name: "name", err: errors.New("ent: missing required field \"name\"")} } @@ -156,6 +182,14 @@ func (pc *PetCreate) createSpec() (*Pet, *sqlgraph.CreateSpec) { }, } ) + if value, ok := pc.mutation.Age(); ok { + _spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{ + Type: field.TypeFloat64, + Value: value, + Column: pet.FieldAge, + }) + _node.Age = value + } if value, ok := pc.mutation.Name(); ok { _spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{ Type: field.TypeString, @@ -229,6 +263,7 @@ func (pcb *PetCreateBulk) Save(ctx context.Context) ([]*Pet, error) { for i := range pcb.builders { func(i int, root context.Context) { builder := pcb.builders[i] + builder.defaults() var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { mutation, ok := m.(*PetMutation) if !ok { diff --git a/entc/integration/ent/pet_query.go b/entc/integration/ent/pet_query.go index 0b0bd5d14..b005394d7 100644 --- a/entc/integration/ent/pet_query.go +++ b/entc/integration/ent/pet_query.go @@ -330,12 +330,12 @@ func (pq *PetQuery) WithOwner(opts ...func(*UserQuery)) *PetQuery { // Example: // // var v []struct { -// Name string `json:"name,omitempty"` +// Age float64 `json:"age,omitempty"` // Count int `json:"count,omitempty"` // } // // client.Pet.Query(). -// GroupBy(pet.FieldName). +// GroupBy(pet.FieldAge). // Aggregate(ent.Count()). // Scan(ctx, &v) // @@ -357,11 +357,11 @@ func (pq *PetQuery) GroupBy(field string, fields ...string) *PetGroupBy { // Example: // // var v []struct { -// Name string `json:"name,omitempty"` +// Age float64 `json:"age,omitempty"` // } // // client.Pet.Query(). -// Select(pet.FieldName). +// Select(pet.FieldAge). // Scan(ctx, &v) // func (pq *PetQuery) Select(field string, fields ...string) *PetSelect { @@ -818,12 +818,23 @@ func (pgb *PetGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (pgb *PetGroupBy) sqlQuery() *sql.Selector { selector := pgb.sql - columns := make([]string, 0, len(pgb.fields)+len(pgb.fns)) - columns = append(columns, pgb.fields...) + aggregation := make([]string, 0, len(pgb.fns)) for _, fn := range pgb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(pgb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(pgb.fields)+len(pgb.fns)) + for _, f := range pgb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(pgb.fields...)...) } // PetSelect is the builder for selecting fields of Pet entities. diff --git a/entc/integration/ent/pet_update.go b/entc/integration/ent/pet_update.go index b8d3addea..9cd4e2599 100644 --- a/entc/integration/ent/pet_update.go +++ b/entc/integration/ent/pet_update.go @@ -32,6 +32,27 @@ func (pu *PetUpdate) Where(ps ...predicate.Pet) *PetUpdate { return pu } +// SetAge sets the "age" field. +func (pu *PetUpdate) SetAge(f float64) *PetUpdate { + pu.mutation.ResetAge() + pu.mutation.SetAge(f) + return pu +} + +// SetNillableAge sets the "age" field if the given value is not nil. +func (pu *PetUpdate) SetNillableAge(f *float64) *PetUpdate { + if f != nil { + pu.SetAge(*f) + } + return pu +} + +// AddAge adds f to the "age" field. +func (pu *PetUpdate) AddAge(f float64) *PetUpdate { + pu.mutation.AddAge(f) + return pu +} + // SetName sets the "name" field. func (pu *PetUpdate) SetName(s string) *PetUpdate { pu.mutation.SetName(s) @@ -174,6 +195,20 @@ func (pu *PetUpdate) sqlSave(ctx context.Context) (n int, err error) { } } } + if value, ok := pu.mutation.Age(); ok { + _spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{ + Type: field.TypeFloat64, + Value: value, + Column: pet.FieldAge, + }) + } + if value, ok := pu.mutation.AddedAge(); ok { + _spec.Fields.Add = append(_spec.Fields.Add, &sqlgraph.FieldSpec{ + Type: field.TypeFloat64, + Value: value, + Column: pet.FieldAge, + }) + } if value, ok := pu.mutation.Name(); ok { _spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{ Type: field.TypeString, @@ -283,6 +318,27 @@ type PetUpdateOne struct { mutation *PetMutation } +// SetAge sets the "age" field. +func (puo *PetUpdateOne) SetAge(f float64) *PetUpdateOne { + puo.mutation.ResetAge() + puo.mutation.SetAge(f) + return puo +} + +// SetNillableAge sets the "age" field if the given value is not nil. +func (puo *PetUpdateOne) SetNillableAge(f *float64) *PetUpdateOne { + if f != nil { + puo.SetAge(*f) + } + return puo +} + +// AddAge adds f to the "age" field. +func (puo *PetUpdateOne) AddAge(f float64) *PetUpdateOne { + puo.mutation.AddAge(f) + return puo +} + // SetName sets the "name" field. func (puo *PetUpdateOne) SetName(s string) *PetUpdateOne { puo.mutation.SetName(s) @@ -449,6 +505,20 @@ func (puo *PetUpdateOne) sqlSave(ctx context.Context) (_node *Pet, err error) { } } } + if value, ok := puo.mutation.Age(); ok { + _spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{ + Type: field.TypeFloat64, + Value: value, + Column: pet.FieldAge, + }) + } + if value, ok := puo.mutation.AddedAge(); ok { + _spec.Fields.Add = append(_spec.Fields.Add, &sqlgraph.FieldSpec{ + Type: field.TypeFloat64, + Value: value, + Column: pet.FieldAge, + }) + } if value, ok := puo.mutation.Name(); ok { _spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{ Type: field.TypeString, diff --git a/entc/integration/ent/runtime.go b/entc/integration/ent/runtime.go index 8b5c452a8..ec2d64d16 100644 --- a/entc/integration/ent/runtime.go +++ b/entc/integration/ent/runtime.go @@ -17,6 +17,7 @@ import ( "entgo.io/ent/entc/integration/ent/file" "entgo.io/ent/entc/integration/ent/group" "entgo.io/ent/entc/integration/ent/groupinfo" + "entgo.io/ent/entc/integration/ent/pet" "entgo.io/ent/entc/integration/ent/schema" "entgo.io/ent/entc/integration/ent/task" "entgo.io/ent/entc/integration/ent/user" @@ -163,6 +164,12 @@ func init() { groupinfoDescMaxUsers := groupinfoFields[1].Descriptor() // groupinfo.DefaultMaxUsers holds the default value on creation for the max_users field. groupinfo.DefaultMaxUsers = groupinfoDescMaxUsers.Default.(int) + petFields := schema.Pet{}.Fields() + _ = petFields + // petDescAge is the schema descriptor for age field. + petDescAge := petFields[0].Descriptor() + // pet.DefaultAge holds the default value on creation for the age field. + pet.DefaultAge = petDescAge.Default.(float64) taskFields := schema.Task{}.Fields() _ = taskFields // taskDescPriority is the schema descriptor for priority field. diff --git a/entc/integration/ent/schema/pet.go b/entc/integration/ent/schema/pet.go index 51fe921f0..bb0e295d6 100644 --- a/entc/integration/ent/schema/pet.go +++ b/entc/integration/ent/schema/pet.go @@ -30,6 +30,8 @@ func (Pet) Annotations() []schema.Annotation { // Fields of the Pet. func (Pet) Fields() []ent.Field { return []ent.Field{ + field.Float("age"). + Default(0), field.String("name"), field.UUID("uuid", uuid.UUID{}). Optional(), diff --git a/entc/integration/ent/spec_query.go b/entc/integration/ent/spec_query.go index 093de60f4..46285c48a 100644 --- a/entc/integration/ent/spec_query.go +++ b/entc/integration/ent/spec_query.go @@ -758,12 +758,23 @@ func (sgb *SpecGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (sgb *SpecGroupBy) sqlQuery() *sql.Selector { selector := sgb.sql - columns := make([]string, 0, len(sgb.fields)+len(sgb.fns)) - columns = append(columns, sgb.fields...) + aggregation := make([]string, 0, len(sgb.fns)) for _, fn := range sgb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(sgb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(sgb.fields)+len(sgb.fns)) + for _, f := range sgb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(sgb.fields...)...) } // SpecSelect is the builder for selecting fields of Spec entities. diff --git a/entc/integration/ent/task_query.go b/entc/integration/ent/task_query.go index cef476d3b..cbcff33a4 100644 --- a/entc/integration/ent/task_query.go +++ b/entc/integration/ent/task_query.go @@ -674,12 +674,23 @@ func (tgb *TaskGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (tgb *TaskGroupBy) sqlQuery() *sql.Selector { selector := tgb.sql - columns := make([]string, 0, len(tgb.fields)+len(tgb.fns)) - columns = append(columns, tgb.fields...) + aggregation := make([]string, 0, len(tgb.fns)) for _, fn := range tgb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(tgb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(tgb.fields)+len(tgb.fns)) + for _, f := range tgb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(tgb.fields...)...) } // TaskSelect is the builder for selecting fields of Task entities. diff --git a/entc/integration/ent/user_query.go b/entc/integration/ent/user_query.go index 39c05fd08..76641abb7 100644 --- a/entc/integration/ent/user_query.go +++ b/entc/integration/ent/user_query.go @@ -1549,12 +1549,23 @@ func (ugb *UserGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (ugb *UserGroupBy) sqlQuery() *sql.Selector { selector := ugb.sql - columns := make([]string, 0, len(ugb.fields)+len(ugb.fns)) - columns = append(columns, ugb.fields...) + aggregation := make([]string, 0, len(ugb.fns)) for _, fn := range ugb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(ugb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(ugb.fields)+len(ugb.fns)) + for _, f := range ugb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(ugb.fields...)...) } // UserSelect is the builder for selecting fields of User entities. diff --git a/entc/integration/gremlin/ent/mutation.go b/entc/integration/gremlin/ent/mutation.go index 5e204b1b8..8d5141297 100644 --- a/entc/integration/gremlin/ent/mutation.go +++ b/entc/integration/gremlin/ent/mutation.go @@ -9962,6 +9962,8 @@ type PetMutation struct { op Op typ string id *string + age *float64 + addage *float64 name *string uuid *uuid.UUID clearedFields map[string]struct{} @@ -10053,6 +10055,62 @@ func (m *PetMutation) ID() (id string, exists bool) { return *m.id, true } +// SetAge sets the "age" field. +func (m *PetMutation) SetAge(f float64) { + m.age = &f + m.addage = nil +} + +// Age returns the value of the "age" field in the mutation. +func (m *PetMutation) Age() (r float64, exists bool) { + v := m.age + if v == nil { + return + } + return *v, true +} + +// OldAge returns the old "age" field's value of the Pet entity. +// If the Pet object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *PetMutation) OldAge(ctx context.Context) (v float64, err error) { + if !m.op.Is(OpUpdateOne) { + return v, fmt.Errorf("OldAge is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, fmt.Errorf("OldAge requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldAge: %w", err) + } + return oldValue.Age, nil +} + +// AddAge adds f to the "age" field. +func (m *PetMutation) AddAge(f float64) { + if m.addage != nil { + *m.addage += f + } else { + m.addage = &f + } +} + +// AddedAge returns the value that was added to the "age" field in this mutation. +func (m *PetMutation) AddedAge() (r float64, exists bool) { + v := m.addage + if v == nil { + return + } + return *v, true +} + +// ResetAge resets all changes to the "age" field. +func (m *PetMutation) ResetAge() { + m.age = nil + m.addage = nil +} + // SetName sets the "name" field. func (m *PetMutation) SetName(s string) { m.name = &s @@ -10230,7 +10288,10 @@ func (m *PetMutation) Type() string { // order to get all numeric fields that were incremented/decremented, call // AddedFields(). func (m *PetMutation) Fields() []string { - fields := make([]string, 0, 2) + fields := make([]string, 0, 3) + if m.age != nil { + fields = append(fields, pet.FieldAge) + } if m.name != nil { fields = append(fields, pet.FieldName) } @@ -10245,6 +10306,8 @@ func (m *PetMutation) Fields() []string { // schema. func (m *PetMutation) Field(name string) (ent.Value, bool) { switch name { + case pet.FieldAge: + return m.Age() case pet.FieldName: return m.Name() case pet.FieldUUID: @@ -10258,6 +10321,8 @@ func (m *PetMutation) Field(name string) (ent.Value, bool) { // database failed. func (m *PetMutation) OldField(ctx context.Context, name string) (ent.Value, error) { switch name { + case pet.FieldAge: + return m.OldAge(ctx) case pet.FieldName: return m.OldName(ctx) case pet.FieldUUID: @@ -10271,6 +10336,13 @@ func (m *PetMutation) OldField(ctx context.Context, name string) (ent.Value, err // type. func (m *PetMutation) SetField(name string, value ent.Value) error { switch name { + case pet.FieldAge: + v, ok := value.(float64) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetAge(v) + return nil case pet.FieldName: v, ok := value.(string) if !ok { @@ -10292,13 +10364,21 @@ func (m *PetMutation) SetField(name string, value ent.Value) error { // AddedFields returns all numeric fields that were incremented/decremented during // this mutation. func (m *PetMutation) AddedFields() []string { - return nil + var fields []string + if m.addage != nil { + fields = append(fields, pet.FieldAge) + } + return fields } // AddedField returns the numeric value that was incremented/decremented on a field // with the given name. The second boolean return value indicates that this field // was not set, or was not defined in the schema. func (m *PetMutation) AddedField(name string) (ent.Value, bool) { + switch name { + case pet.FieldAge: + return m.AddedAge() + } return nil, false } @@ -10307,6 +10387,13 @@ func (m *PetMutation) AddedField(name string) (ent.Value, bool) { // type. func (m *PetMutation) AddField(name string, value ent.Value) error { switch name { + case pet.FieldAge: + v, ok := value.(float64) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.AddAge(v) + return nil } return fmt.Errorf("unknown Pet numeric field %s", name) } @@ -10343,6 +10430,9 @@ func (m *PetMutation) ClearField(name string) error { // It returns an error if the field is not defined in the schema. func (m *PetMutation) ResetField(name string) error { switch name { + case pet.FieldAge: + m.ResetAge() + return nil case pet.FieldName: m.ResetName() return nil diff --git a/entc/integration/gremlin/ent/pet.go b/entc/integration/gremlin/ent/pet.go index f787663d4..e1c3d9636 100644 --- a/entc/integration/gremlin/ent/pet.go +++ b/entc/integration/gremlin/ent/pet.go @@ -20,6 +20,8 @@ type Pet struct { config `json:"-"` // ID of the ent. ID string `json:"id,omitempty"` + // Age holds the value of the "age" field. + Age float64 `json:"age,omitempty"` // Name holds the value of the "name" field. Name string `json:"name,omitempty"` // UUID holds the value of the "uuid" field. @@ -76,6 +78,7 @@ func (pe *Pet) FromResponse(res *gremlin.Response) error { } var scanpe struct { ID string `json:"id,omitempty"` + Age float64 `json:"age,omitempty"` Name string `json:"name,omitempty"` UUID uuid.UUID `json:"uuid,omitempty"` } @@ -83,6 +86,7 @@ func (pe *Pet) FromResponse(res *gremlin.Response) error { return err } pe.ID = scanpe.ID + pe.Age = scanpe.Age pe.Name = scanpe.Name pe.UUID = scanpe.UUID return nil @@ -121,6 +125,8 @@ func (pe *Pet) String() string { var builder strings.Builder builder.WriteString("Pet(") builder.WriteString(fmt.Sprintf("id=%v", pe.ID)) + builder.WriteString(", age=") + builder.WriteString(fmt.Sprintf("%v", pe.Age)) builder.WriteString(", name=") builder.WriteString(pe.Name) builder.WriteString(", uuid=") @@ -140,6 +146,7 @@ func (pe *Pets) FromResponse(res *gremlin.Response) error { } var scanpe []struct { ID string `json:"id,omitempty"` + Age float64 `json:"age,omitempty"` Name string `json:"name,omitempty"` UUID uuid.UUID `json:"uuid,omitempty"` } @@ -149,6 +156,7 @@ func (pe *Pets) FromResponse(res *gremlin.Response) error { for _, v := range scanpe { *pe = append(*pe, &Pet{ ID: v.ID, + Age: v.Age, Name: v.Name, UUID: v.UUID, }) diff --git a/entc/integration/gremlin/ent/pet/pet.go b/entc/integration/gremlin/ent/pet/pet.go index 1618a7b0d..f19717a69 100644 --- a/entc/integration/gremlin/ent/pet/pet.go +++ b/entc/integration/gremlin/ent/pet/pet.go @@ -11,6 +11,8 @@ const ( Label = "pet" // FieldID holds the string denoting the id field in the database. FieldID = "id" + // FieldAge holds the string denoting the age field in the database. + FieldAge = "age" // FieldName holds the string denoting the name field in the database. FieldName = "name" // FieldUUID holds the string denoting the uuid field in the database. @@ -25,4 +27,9 @@ const ( OwnerInverseLabel = "user_pets" ) +var ( + // DefaultAge holds the default value on creation for the "age" field. + DefaultAge float64 +) + // comment from another template. diff --git a/entc/integration/gremlin/ent/pet/where.go b/entc/integration/gremlin/ent/pet/where.go index be79ffe40..9d3ad0b98 100644 --- a/entc/integration/gremlin/ent/pet/where.go +++ b/entc/integration/gremlin/ent/pet/where.go @@ -85,6 +85,13 @@ func IDLTE(id string) predicate.Pet { }) } +// Age applies equality check predicate on the "age" field. It's identical to AgeEQ. +func Age(v float64) predicate.Pet { + return predicate.Pet(func(t *dsl.Traversal) { + t.Has(Label, FieldAge, p.EQ(v)) + }) +} + // Name applies equality check predicate on the "name" field. It's identical to NameEQ. func Name(v string) predicate.Pet { return predicate.Pet(func(t *dsl.Traversal) { @@ -99,6 +106,70 @@ func UUID(v uuid.UUID) predicate.Pet { }) } +// AgeEQ applies the EQ predicate on the "age" field. +func AgeEQ(v float64) predicate.Pet { + return predicate.Pet(func(t *dsl.Traversal) { + t.Has(Label, FieldAge, p.EQ(v)) + }) +} + +// AgeNEQ applies the NEQ predicate on the "age" field. +func AgeNEQ(v float64) predicate.Pet { + return predicate.Pet(func(t *dsl.Traversal) { + t.Has(Label, FieldAge, p.NEQ(v)) + }) +} + +// AgeIn applies the In predicate on the "age" field. +func AgeIn(vs ...float64) predicate.Pet { + v := make([]interface{}, len(vs)) + for i := range v { + v[i] = vs[i] + } + return predicate.Pet(func(t *dsl.Traversal) { + t.Has(Label, FieldAge, p.Within(v...)) + }) +} + +// AgeNotIn applies the NotIn predicate on the "age" field. +func AgeNotIn(vs ...float64) predicate.Pet { + v := make([]interface{}, len(vs)) + for i := range v { + v[i] = vs[i] + } + return predicate.Pet(func(t *dsl.Traversal) { + t.Has(Label, FieldAge, p.Without(v...)) + }) +} + +// AgeGT applies the GT predicate on the "age" field. +func AgeGT(v float64) predicate.Pet { + return predicate.Pet(func(t *dsl.Traversal) { + t.Has(Label, FieldAge, p.GT(v)) + }) +} + +// AgeGTE applies the GTE predicate on the "age" field. +func AgeGTE(v float64) predicate.Pet { + return predicate.Pet(func(t *dsl.Traversal) { + t.Has(Label, FieldAge, p.GTE(v)) + }) +} + +// AgeLT applies the LT predicate on the "age" field. +func AgeLT(v float64) predicate.Pet { + return predicate.Pet(func(t *dsl.Traversal) { + t.Has(Label, FieldAge, p.LT(v)) + }) +} + +// AgeLTE applies the LTE predicate on the "age" field. +func AgeLTE(v float64) predicate.Pet { + return predicate.Pet(func(t *dsl.Traversal) { + t.Has(Label, FieldAge, p.LTE(v)) + }) +} + // NameEQ applies the EQ predicate on the "name" field. func NameEQ(v string) predicate.Pet { return predicate.Pet(func(t *dsl.Traversal) { diff --git a/entc/integration/gremlin/ent/pet_create.go b/entc/integration/gremlin/ent/pet_create.go index 70ae4cf0b..d474235c6 100644 --- a/entc/integration/gremlin/ent/pet_create.go +++ b/entc/integration/gremlin/ent/pet_create.go @@ -28,6 +28,20 @@ type PetCreate struct { hooks []Hook } +// SetAge sets the "age" field. +func (pc *PetCreate) SetAge(f float64) *PetCreate { + pc.mutation.SetAge(f) + return pc +} + +// SetNillableAge sets the "age" field if the given value is not nil. +func (pc *PetCreate) SetNillableAge(f *float64) *PetCreate { + if f != nil { + pc.SetAge(*f) + } + return pc +} + // SetName sets the "name" field. func (pc *PetCreate) SetName(s string) *PetCreate { pc.mutation.SetName(s) @@ -89,6 +103,7 @@ func (pc *PetCreate) Save(ctx context.Context) (*Pet, error) { err error node *Pet ) + pc.defaults() if len(pc.hooks) == 0 { if err = pc.check(); err != nil { return nil, err @@ -127,8 +142,19 @@ func (pc *PetCreate) SaveX(ctx context.Context) *Pet { return v } +// defaults sets the default values of the builder before save. +func (pc *PetCreate) defaults() { + if _, ok := pc.mutation.Age(); !ok { + v := pet.DefaultAge + pc.mutation.SetAge(v) + } +} + // check runs all checks and user-defined validators on the builder. func (pc *PetCreate) check() error { + if _, ok := pc.mutation.Age(); !ok { + return &ValidationError{Name: "age", err: errors.New("ent: missing required field \"age\"")} + } if _, ok := pc.mutation.Name(); !ok { return &ValidationError{Name: "name", err: errors.New("ent: missing required field \"name\"")} } @@ -158,6 +184,9 @@ func (pc *PetCreate) gremlin() *dsl.Traversal { } constraints := make([]*constraint, 0, 1) v := g.AddV(pet.Label) + if value, ok := pc.mutation.Age(); ok { + v.Property(dsl.Single, pet.FieldAge, value) + } if value, ok := pc.mutation.Name(); ok { v.Property(dsl.Single, pet.FieldName, value) } diff --git a/entc/integration/gremlin/ent/pet_query.go b/entc/integration/gremlin/ent/pet_query.go index 2e6dc2693..8f2219174 100644 --- a/entc/integration/gremlin/ent/pet_query.go +++ b/entc/integration/gremlin/ent/pet_query.go @@ -314,12 +314,12 @@ func (pq *PetQuery) WithOwner(opts ...func(*UserQuery)) *PetQuery { // Example: // // var v []struct { -// Name string `json:"name,omitempty"` +// Age float64 `json:"age,omitempty"` // Count int `json:"count,omitempty"` // } // // client.Pet.Query(). -// GroupBy(pet.FieldName). +// GroupBy(pet.FieldAge). // Aggregate(ent.Count()). // Scan(ctx, &v) // @@ -341,11 +341,11 @@ func (pq *PetQuery) GroupBy(field string, fields ...string) *PetGroupBy { // Example: // // var v []struct { -// Name string `json:"name,omitempty"` +// Age float64 `json:"age,omitempty"` // } // // client.Pet.Query(). -// Select(pet.FieldName). +// Select(pet.FieldAge). // Scan(ctx, &v) // func (pq *PetQuery) Select(field string, fields ...string) *PetSelect { diff --git a/entc/integration/gremlin/ent/pet_update.go b/entc/integration/gremlin/ent/pet_update.go index 12812f7d5..dcfce9ed1 100644 --- a/entc/integration/gremlin/ent/pet_update.go +++ b/entc/integration/gremlin/ent/pet_update.go @@ -34,6 +34,27 @@ func (pu *PetUpdate) Where(ps ...predicate.Pet) *PetUpdate { return pu } +// SetAge sets the "age" field. +func (pu *PetUpdate) SetAge(f float64) *PetUpdate { + pu.mutation.ResetAge() + pu.mutation.SetAge(f) + return pu +} + +// SetNillableAge sets the "age" field if the given value is not nil. +func (pu *PetUpdate) SetNillableAge(f *float64) *PetUpdate { + if f != nil { + pu.SetAge(*f) + } + return pu +} + +// AddAge adds f to the "age" field. +func (pu *PetUpdate) AddAge(f float64) *PetUpdate { + pu.mutation.AddAge(f) + return pu +} + // SetName sets the "name" field. func (pu *PetUpdate) SetName(s string) *PetUpdate { pu.mutation.SetName(s) @@ -186,6 +207,12 @@ func (pu *PetUpdate) gremlin() *dsl.Traversal { trs []*dsl.Traversal ) + if value, ok := pu.mutation.Age(); ok { + v.Property(dsl.Single, pet.FieldAge, value) + } + if value, ok := pu.mutation.AddedAge(); ok { + v.Property(dsl.Single, pet.FieldAge, __.Union(__.Values(pet.FieldAge), __.Constant(value)).Sum()) + } if value, ok := pu.mutation.Name(); ok { v.Property(dsl.Single, pet.FieldName, value) } @@ -240,6 +267,27 @@ type PetUpdateOne struct { mutation *PetMutation } +// SetAge sets the "age" field. +func (puo *PetUpdateOne) SetAge(f float64) *PetUpdateOne { + puo.mutation.ResetAge() + puo.mutation.SetAge(f) + return puo +} + +// SetNillableAge sets the "age" field if the given value is not nil. +func (puo *PetUpdateOne) SetNillableAge(f *float64) *PetUpdateOne { + if f != nil { + puo.SetAge(*f) + } + return puo +} + +// AddAge adds f to the "age" field. +func (puo *PetUpdateOne) AddAge(f float64) *PetUpdateOne { + puo.mutation.AddAge(f) + return puo +} + // SetName sets the "name" field. func (puo *PetUpdateOne) SetName(s string) *PetUpdateOne { puo.mutation.SetName(s) @@ -404,6 +452,12 @@ func (puo *PetUpdateOne) gremlin(id string) *dsl.Traversal { trs []*dsl.Traversal ) + if value, ok := puo.mutation.Age(); ok { + v.Property(dsl.Single, pet.FieldAge, value) + } + if value, ok := puo.mutation.AddedAge(); ok { + v.Property(dsl.Single, pet.FieldAge, __.Union(__.Values(pet.FieldAge), __.Constant(value)).Sum()) + } if value, ok := puo.mutation.Name(); ok { v.Property(dsl.Single, pet.FieldName, value) } diff --git a/entc/integration/gremlin/ent/runtime.go b/entc/integration/gremlin/ent/runtime.go index a107c8207..137d9ac44 100644 --- a/entc/integration/gremlin/ent/runtime.go +++ b/entc/integration/gremlin/ent/runtime.go @@ -18,6 +18,7 @@ import ( "entgo.io/ent/entc/integration/gremlin/ent/file" "entgo.io/ent/entc/integration/gremlin/ent/group" "entgo.io/ent/entc/integration/gremlin/ent/groupinfo" + "entgo.io/ent/entc/integration/gremlin/ent/pet" "entgo.io/ent/entc/integration/gremlin/ent/task" "entgo.io/ent/entc/integration/gremlin/ent/user" ) @@ -163,6 +164,12 @@ func init() { groupinfoDescMaxUsers := groupinfoFields[1].Descriptor() // groupinfo.DefaultMaxUsers holds the default value on creation for the max_users field. groupinfo.DefaultMaxUsers = groupinfoDescMaxUsers.Default.(int) + petFields := schema.Pet{}.Fields() + _ = petFields + // petDescAge is the schema descriptor for age field. + petDescAge := petFields[0].Descriptor() + // pet.DefaultAge holds the default value on creation for the age field. + pet.DefaultAge = petDescAge.Default.(float64) taskFields := schema.Task{}.Fields() _ = taskFields // taskDescPriority is the schema descriptor for priority field. diff --git a/entc/integration/hooks/ent/card_query.go b/entc/integration/hooks/ent/card_query.go index 5fc73647e..e667b5eba 100644 --- a/entc/integration/hooks/ent/card_query.go +++ b/entc/integration/hooks/ent/card_query.go @@ -753,12 +753,23 @@ func (cgb *CardGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (cgb *CardGroupBy) sqlQuery() *sql.Selector { selector := cgb.sql - columns := make([]string, 0, len(cgb.fields)+len(cgb.fns)) - columns = append(columns, cgb.fields...) + aggregation := make([]string, 0, len(cgb.fns)) for _, fn := range cgb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(cgb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(cgb.fields)+len(cgb.fns)) + for _, f := range cgb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(cgb.fields...)...) } // CardSelect is the builder for selecting fields of Card entities. diff --git a/entc/integration/hooks/ent/user_query.go b/entc/integration/hooks/ent/user_query.go index 6448010c8..c6e2736d1 100644 --- a/entc/integration/hooks/ent/user_query.go +++ b/entc/integration/hooks/ent/user_query.go @@ -920,12 +920,23 @@ func (ugb *UserGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (ugb *UserGroupBy) sqlQuery() *sql.Selector { selector := ugb.sql - columns := make([]string, 0, len(ugb.fields)+len(ugb.fns)) - columns = append(columns, ugb.fields...) + aggregation := make([]string, 0, len(ugb.fns)) for _, fn := range ugb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(ugb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(ugb.fields)+len(ugb.fns)) + for _, f := range ugb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(ugb.fields...)...) } // UserSelect is the builder for selecting fields of User entities. diff --git a/entc/integration/idtype/ent/user_query.go b/entc/integration/idtype/ent/user_query.go index 9f5a68b0d..49627257f 100644 --- a/entc/integration/idtype/ent/user_query.go +++ b/entc/integration/idtype/ent/user_query.go @@ -955,12 +955,23 @@ func (ugb *UserGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (ugb *UserGroupBy) sqlQuery() *sql.Selector { selector := ugb.sql - columns := make([]string, 0, len(ugb.fields)+len(ugb.fns)) - columns = append(columns, ugb.fields...) + aggregation := make([]string, 0, len(ugb.fns)) for _, fn := range ugb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(ugb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(ugb.fields)+len(ugb.fns)) + for _, f := range ugb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(ugb.fields...)...) } // UserSelect is the builder for selecting fields of User entities. diff --git a/entc/integration/integration_test.go b/entc/integration/integration_test.go index 82741a819..dececa1a2 100644 --- a/entc/integration/integration_test.go +++ b/entc/integration/integration_test.go @@ -784,6 +784,38 @@ func Relation(t *testing.T, client *ent.Client) { for i := range v2 { require.Equal(2, v2[i].Total) } + + t.Log("group by a relation") + foo := client.User.Create().SetName("foo").SetAge(10).AddPets( + client.Pet.Create().SetName("a").SetAge(10).SaveX(ctx), + client.Pet.Create().SetName("b").SetAge(7).SaveX(ctx), + ).SaveX(ctx) + bar := client.User.Create().SetName("bar").SetAge(10).AddPets( + client.Pet.Create().SetName("c").SetAge(14).SaveX(ctx), + client.Pet.Create().SetName("d").SetAge(1).SaveX(ctx), + ).SaveX(ctx) + + var v3 []struct { + ID int + Name string + Average float64 + } + client.User.Query(). + Where(user.IDIn(foo.ID, bar.ID)). + GroupBy(user.FieldID, user.FieldName). + Aggregate(func(s *entsql.Selector) string { + t := entsql.Table(pet.Table) + s.Join(t).On(s.C(user.FieldID), t.C(pet.OwnerColumn)) + return entsql.As(entsql.Avg(t.C(pet.FieldAge)), "average") + }). + ScanX(ctx, &v3) + require.Len(v3, 2) + require.Equal(foo.ID, v3[0].ID) + require.Equal(foo.Name, v3[0].Name) + require.Equal(8.5, v3[0].Average) + require.Equal(bar.ID, v3[1].ID) + require.Equal(bar.Name, v3[1].Name) + require.Equal(7.5, v3[1].Average) } func ClearFields(t *testing.T, client *ent.Client) { diff --git a/entc/integration/json/ent/user_query.go b/entc/integration/json/ent/user_query.go index b64339662..bd37377da 100644 --- a/entc/integration/json/ent/user_query.go +++ b/entc/integration/json/ent/user_query.go @@ -674,12 +674,23 @@ func (ugb *UserGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (ugb *UserGroupBy) sqlQuery() *sql.Selector { selector := ugb.sql - columns := make([]string, 0, len(ugb.fields)+len(ugb.fns)) - columns = append(columns, ugb.fields...) + aggregation := make([]string, 0, len(ugb.fns)) for _, fn := range ugb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(ugb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(ugb.fields)+len(ugb.fns)) + for _, f := range ugb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(ugb.fields...)...) } // UserSelect is the builder for selecting fields of User entities. diff --git a/entc/integration/migrate/entv1/car_query.go b/entc/integration/migrate/entv1/car_query.go index 94e4143ff..5aa939ca0 100644 --- a/entc/integration/migrate/entv1/car_query.go +++ b/entc/integration/migrate/entv1/car_query.go @@ -729,12 +729,23 @@ func (cgb *CarGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (cgb *CarGroupBy) sqlQuery() *sql.Selector { selector := cgb.sql - columns := make([]string, 0, len(cgb.fields)+len(cgb.fns)) - columns = append(columns, cgb.fields...) + aggregation := make([]string, 0, len(cgb.fns)) for _, fn := range cgb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(cgb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(cgb.fields)+len(cgb.fns)) + for _, f := range cgb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(cgb.fields...)...) } // CarSelect is the builder for selecting fields of Car entities. diff --git a/entc/integration/migrate/entv1/conversion_query.go b/entc/integration/migrate/entv1/conversion_query.go index cf5a296d2..192dadf90 100644 --- a/entc/integration/migrate/entv1/conversion_query.go +++ b/entc/integration/migrate/entv1/conversion_query.go @@ -674,12 +674,23 @@ func (cgb *ConversionGroupBy) sqlScan(ctx context.Context, v interface{}) error func (cgb *ConversionGroupBy) sqlQuery() *sql.Selector { selector := cgb.sql - columns := make([]string, 0, len(cgb.fields)+len(cgb.fns)) - columns = append(columns, cgb.fields...) + aggregation := make([]string, 0, len(cgb.fns)) for _, fn := range cgb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(cgb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(cgb.fields)+len(cgb.fns)) + for _, f := range cgb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(cgb.fields...)...) } // ConversionSelect is the builder for selecting fields of Conversion entities. diff --git a/entc/integration/migrate/entv1/customtype_query.go b/entc/integration/migrate/entv1/customtype_query.go index 6928952a6..3ea34c351 100644 --- a/entc/integration/migrate/entv1/customtype_query.go +++ b/entc/integration/migrate/entv1/customtype_query.go @@ -674,12 +674,23 @@ func (ctgb *CustomTypeGroupBy) sqlScan(ctx context.Context, v interface{}) error func (ctgb *CustomTypeGroupBy) sqlQuery() *sql.Selector { selector := ctgb.sql - columns := make([]string, 0, len(ctgb.fields)+len(ctgb.fns)) - columns = append(columns, ctgb.fields...) + aggregation := make([]string, 0, len(ctgb.fns)) for _, fn := range ctgb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(ctgb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(ctgb.fields)+len(ctgb.fns)) + for _, f := range ctgb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(ctgb.fields...)...) } // CustomTypeSelect is the builder for selecting fields of CustomType entities. diff --git a/entc/integration/migrate/entv1/user_query.go b/entc/integration/migrate/entv1/user_query.go index 5120f95a6..aa45ee7c5 100644 --- a/entc/integration/migrate/entv1/user_query.go +++ b/entc/integration/migrate/entv1/user_query.go @@ -948,12 +948,23 @@ func (ugb *UserGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (ugb *UserGroupBy) sqlQuery() *sql.Selector { selector := ugb.sql - columns := make([]string, 0, len(ugb.fields)+len(ugb.fns)) - columns = append(columns, ugb.fields...) + aggregation := make([]string, 0, len(ugb.fns)) for _, fn := range ugb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(ugb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(ugb.fields)+len(ugb.fns)) + for _, f := range ugb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(ugb.fields...)...) } // UserSelect is the builder for selecting fields of User entities. diff --git a/entc/integration/migrate/entv2/car_query.go b/entc/integration/migrate/entv2/car_query.go index 35e559530..817daa60a 100644 --- a/entc/integration/migrate/entv2/car_query.go +++ b/entc/integration/migrate/entv2/car_query.go @@ -729,12 +729,23 @@ func (cgb *CarGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (cgb *CarGroupBy) sqlQuery() *sql.Selector { selector := cgb.sql - columns := make([]string, 0, len(cgb.fields)+len(cgb.fns)) - columns = append(columns, cgb.fields...) + aggregation := make([]string, 0, len(cgb.fns)) for _, fn := range cgb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(cgb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(cgb.fields)+len(cgb.fns)) + for _, f := range cgb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(cgb.fields...)...) } // CarSelect is the builder for selecting fields of Car entities. diff --git a/entc/integration/migrate/entv2/conversion_query.go b/entc/integration/migrate/entv2/conversion_query.go index 2e30471db..bf845579f 100644 --- a/entc/integration/migrate/entv2/conversion_query.go +++ b/entc/integration/migrate/entv2/conversion_query.go @@ -674,12 +674,23 @@ func (cgb *ConversionGroupBy) sqlScan(ctx context.Context, v interface{}) error func (cgb *ConversionGroupBy) sqlQuery() *sql.Selector { selector := cgb.sql - columns := make([]string, 0, len(cgb.fields)+len(cgb.fns)) - columns = append(columns, cgb.fields...) + aggregation := make([]string, 0, len(cgb.fns)) for _, fn := range cgb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(cgb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(cgb.fields)+len(cgb.fns)) + for _, f := range cgb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(cgb.fields...)...) } // ConversionSelect is the builder for selecting fields of Conversion entities. diff --git a/entc/integration/migrate/entv2/customtype_query.go b/entc/integration/migrate/entv2/customtype_query.go index 5dd002aa3..3d7259db3 100644 --- a/entc/integration/migrate/entv2/customtype_query.go +++ b/entc/integration/migrate/entv2/customtype_query.go @@ -674,12 +674,23 @@ func (ctgb *CustomTypeGroupBy) sqlScan(ctx context.Context, v interface{}) error func (ctgb *CustomTypeGroupBy) sqlQuery() *sql.Selector { selector := ctgb.sql - columns := make([]string, 0, len(ctgb.fields)+len(ctgb.fns)) - columns = append(columns, ctgb.fields...) + aggregation := make([]string, 0, len(ctgb.fns)) for _, fn := range ctgb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(ctgb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(ctgb.fields)+len(ctgb.fns)) + for _, f := range ctgb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(ctgb.fields...)...) } // CustomTypeSelect is the builder for selecting fields of CustomType entities. diff --git a/entc/integration/migrate/entv2/group_query.go b/entc/integration/migrate/entv2/group_query.go index cec48523e..29211df7a 100644 --- a/entc/integration/migrate/entv2/group_query.go +++ b/entc/integration/migrate/entv2/group_query.go @@ -650,12 +650,23 @@ func (ggb *GroupGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (ggb *GroupGroupBy) sqlQuery() *sql.Selector { selector := ggb.sql - columns := make([]string, 0, len(ggb.fields)+len(ggb.fns)) - columns = append(columns, ggb.fields...) + aggregation := make([]string, 0, len(ggb.fns)) for _, fn := range ggb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(ggb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(ggb.fields)+len(ggb.fns)) + for _, f := range ggb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(ggb.fields...)...) } // GroupSelect is the builder for selecting fields of Group entities. diff --git a/entc/integration/migrate/entv2/media_query.go b/entc/integration/migrate/entv2/media_query.go index d87586c7e..35a0762b9 100644 --- a/entc/integration/migrate/entv2/media_query.go +++ b/entc/integration/migrate/entv2/media_query.go @@ -674,12 +674,23 @@ func (mgb *MediaGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (mgb *MediaGroupBy) sqlQuery() *sql.Selector { selector := mgb.sql - columns := make([]string, 0, len(mgb.fields)+len(mgb.fns)) - columns = append(columns, mgb.fields...) + aggregation := make([]string, 0, len(mgb.fns)) for _, fn := range mgb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(mgb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(mgb.fields)+len(mgb.fns)) + for _, f := range mgb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(mgb.fields...)...) } // MediaSelect is the builder for selecting fields of Media entities. diff --git a/entc/integration/migrate/entv2/pet_query.go b/entc/integration/migrate/entv2/pet_query.go index ebced8f07..eae9ed421 100644 --- a/entc/integration/migrate/entv2/pet_query.go +++ b/entc/integration/migrate/entv2/pet_query.go @@ -729,12 +729,23 @@ func (pgb *PetGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (pgb *PetGroupBy) sqlQuery() *sql.Selector { selector := pgb.sql - columns := make([]string, 0, len(pgb.fields)+len(pgb.fns)) - columns = append(columns, pgb.fields...) + aggregation := make([]string, 0, len(pgb.fns)) for _, fn := range pgb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(pgb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(pgb.fields)+len(pgb.fns)) + for _, f := range pgb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(pgb.fields...)...) } // PetSelect is the builder for selecting fields of Pet entities. diff --git a/entc/integration/migrate/entv2/user_query.go b/entc/integration/migrate/entv2/user_query.go index aa166baa2..8f72745b8 100644 --- a/entc/integration/migrate/entv2/user_query.go +++ b/entc/integration/migrate/entv2/user_query.go @@ -912,12 +912,23 @@ func (ugb *UserGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (ugb *UserGroupBy) sqlQuery() *sql.Selector { selector := ugb.sql - columns := make([]string, 0, len(ugb.fields)+len(ugb.fns)) - columns = append(columns, ugb.fields...) + aggregation := make([]string, 0, len(ugb.fns)) for _, fn := range ugb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(ugb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(ugb.fields)+len(ugb.fns)) + for _, f := range ugb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(ugb.fields...)...) } // UserSelect is the builder for selecting fields of User entities. diff --git a/entc/integration/multischema/ent/group_query.go b/entc/integration/multischema/ent/group_query.go index c30279556..a6c97bb42 100644 --- a/entc/integration/multischema/ent/group_query.go +++ b/entc/integration/multischema/ent/group_query.go @@ -794,12 +794,23 @@ func (ggb *GroupGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (ggb *GroupGroupBy) sqlQuery() *sql.Selector { selector := ggb.sql - columns := make([]string, 0, len(ggb.fields)+len(ggb.fns)) - columns = append(columns, ggb.fields...) + aggregation := make([]string, 0, len(ggb.fns)) for _, fn := range ggb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(ggb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(ggb.fields)+len(ggb.fns)) + for _, f := range ggb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(ggb.fields...)...) } // GroupSelect is the builder for selecting fields of Group entities. diff --git a/entc/integration/multischema/ent/pet_query.go b/entc/integration/multischema/ent/pet_query.go index c4df20141..d36bac1c2 100644 --- a/entc/integration/multischema/ent/pet_query.go +++ b/entc/integration/multischema/ent/pet_query.go @@ -764,12 +764,23 @@ func (pgb *PetGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (pgb *PetGroupBy) sqlQuery() *sql.Selector { selector := pgb.sql - columns := make([]string, 0, len(pgb.fields)+len(pgb.fns)) - columns = append(columns, pgb.fields...) + aggregation := make([]string, 0, len(pgb.fns)) for _, fn := range pgb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(pgb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(pgb.fields)+len(pgb.fns)) + for _, f := range pgb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(pgb.fields...)...) } // PetSelect is the builder for selecting fields of Pet entities. diff --git a/entc/integration/multischema/ent/user_query.go b/entc/integration/multischema/ent/user_query.go index 189252a93..135248660 100644 --- a/entc/integration/multischema/ent/user_query.go +++ b/entc/integration/multischema/ent/user_query.go @@ -863,12 +863,23 @@ func (ugb *UserGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (ugb *UserGroupBy) sqlQuery() *sql.Selector { selector := ugb.sql - columns := make([]string, 0, len(ugb.fields)+len(ugb.fns)) - columns = append(columns, ugb.fields...) + aggregation := make([]string, 0, len(ugb.fns)) for _, fn := range ugb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(ugb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(ugb.fields)+len(ugb.fns)) + for _, f := range ugb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(ugb.fields...)...) } // UserSelect is the builder for selecting fields of User entities. diff --git a/entc/integration/privacy/ent/task_query.go b/entc/integration/privacy/ent/task_query.go index 58ed83fc6..d73f879bf 100644 --- a/entc/integration/privacy/ent/task_query.go +++ b/entc/integration/privacy/ent/task_query.go @@ -862,12 +862,23 @@ func (tgb *TaskGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (tgb *TaskGroupBy) sqlQuery() *sql.Selector { selector := tgb.sql - columns := make([]string, 0, len(tgb.fields)+len(tgb.fns)) - columns = append(columns, tgb.fields...) + aggregation := make([]string, 0, len(tgb.fns)) for _, fn := range tgb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(tgb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(tgb.fields)+len(tgb.fns)) + for _, f := range tgb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(tgb.fields...)...) } // TaskSelect is the builder for selecting fields of Task entities. diff --git a/entc/integration/privacy/ent/team_query.go b/entc/integration/privacy/ent/team_query.go index 1c78f1a90..3d0e5334d 100644 --- a/entc/integration/privacy/ent/team_query.go +++ b/entc/integration/privacy/ent/team_query.go @@ -890,12 +890,23 @@ func (tgb *TeamGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (tgb *TeamGroupBy) sqlQuery() *sql.Selector { selector := tgb.sql - columns := make([]string, 0, len(tgb.fields)+len(tgb.fns)) - columns = append(columns, tgb.fields...) + aggregation := make([]string, 0, len(tgb.fns)) for _, fn := range tgb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(tgb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(tgb.fields)+len(tgb.fns)) + for _, f := range tgb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(tgb.fields...)...) } // TeamSelect is the builder for selecting fields of Team entities. diff --git a/entc/integration/privacy/ent/user_query.go b/entc/integration/privacy/ent/user_query.go index d18a1c49d..776651a1d 100644 --- a/entc/integration/privacy/ent/user_query.go +++ b/entc/integration/privacy/ent/user_query.go @@ -854,12 +854,23 @@ func (ugb *UserGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (ugb *UserGroupBy) sqlQuery() *sql.Selector { selector := ugb.sql - columns := make([]string, 0, len(ugb.fields)+len(ugb.fns)) - columns = append(columns, ugb.fields...) + aggregation := make([]string, 0, len(ugb.fns)) for _, fn := range ugb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(ugb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(ugb.fields)+len(ugb.fns)) + for _, f := range ugb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(ugb.fields...)...) } // UserSelect is the builder for selecting fields of User entities. diff --git a/entc/integration/template/ent/group_query.go b/entc/integration/template/ent/group_query.go index 8e6f8bcf6..0cd15c65f 100644 --- a/entc/integration/template/ent/group_query.go +++ b/entc/integration/template/ent/group_query.go @@ -678,12 +678,23 @@ func (ggb *GroupGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (ggb *GroupGroupBy) sqlQuery() *sql.Selector { selector := ggb.sql - columns := make([]string, 0, len(ggb.fields)+len(ggb.fns)) - columns = append(columns, ggb.fields...) + aggregation := make([]string, 0, len(ggb.fns)) for _, fn := range ggb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(ggb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(ggb.fields)+len(ggb.fns)) + for _, f := range ggb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(ggb.fields...)...) } // GroupSelect is the builder for selecting fields of Group entities. diff --git a/entc/integration/template/ent/pet_query.go b/entc/integration/template/ent/pet_query.go index 0a3e0bff8..0d5a9f7b4 100644 --- a/entc/integration/template/ent/pet_query.go +++ b/entc/integration/template/ent/pet_query.go @@ -757,12 +757,23 @@ func (pgb *PetGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (pgb *PetGroupBy) sqlQuery() *sql.Selector { selector := pgb.sql - columns := make([]string, 0, len(pgb.fields)+len(pgb.fns)) - columns = append(columns, pgb.fields...) + aggregation := make([]string, 0, len(pgb.fns)) for _, fn := range pgb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(pgb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(pgb.fields)+len(pgb.fns)) + for _, f := range pgb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(pgb.fields...)...) } // PetSelect is the builder for selecting fields of Pet entities. diff --git a/entc/integration/template/ent/user_query.go b/entc/integration/template/ent/user_query.go index 9c1582319..5e7770ec9 100644 --- a/entc/integration/template/ent/user_query.go +++ b/entc/integration/template/ent/user_query.go @@ -851,12 +851,23 @@ func (ugb *UserGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (ugb *UserGroupBy) sqlQuery() *sql.Selector { selector := ugb.sql - columns := make([]string, 0, len(ugb.fields)+len(ugb.fns)) - columns = append(columns, ugb.fields...) + aggregation := make([]string, 0, len(ugb.fns)) for _, fn := range ugb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(ugb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(ugb.fields)+len(ugb.fns)) + for _, f := range ugb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(ugb.fields...)...) } // UserSelect is the builder for selecting fields of User entities. diff --git a/examples/edgeindex/ent/city_query.go b/examples/edgeindex/ent/city_query.go index 3948930f6..ff2b2d022 100644 --- a/examples/edgeindex/ent/city_query.go +++ b/examples/edgeindex/ent/city_query.go @@ -746,12 +746,23 @@ func (cgb *CityGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (cgb *CityGroupBy) sqlQuery() *sql.Selector { selector := cgb.sql - columns := make([]string, 0, len(cgb.fields)+len(cgb.fns)) - columns = append(columns, cgb.fields...) + aggregation := make([]string, 0, len(cgb.fns)) for _, fn := range cgb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(cgb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(cgb.fields)+len(cgb.fns)) + for _, f := range cgb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(cgb.fields...)...) } // CitySelect is the builder for selecting fields of City entities. diff --git a/examples/edgeindex/ent/street_query.go b/examples/edgeindex/ent/street_query.go index 8bc993e76..54ffde13c 100644 --- a/examples/edgeindex/ent/street_query.go +++ b/examples/edgeindex/ent/street_query.go @@ -753,12 +753,23 @@ func (sgb *StreetGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (sgb *StreetGroupBy) sqlQuery() *sql.Selector { selector := sgb.sql - columns := make([]string, 0, len(sgb.fields)+len(sgb.fns)) - columns = append(columns, sgb.fields...) + aggregation := make([]string, 0, len(sgb.fns)) for _, fn := range sgb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(sgb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(sgb.fields)+len(sgb.fns)) + for _, f := range sgb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(sgb.fields...)...) } // StreetSelect is the builder for selecting fields of Street entities. diff --git a/examples/entcpkg/ent/user_query.go b/examples/entcpkg/ent/user_query.go index 9c489d753..b51feefe4 100644 --- a/examples/entcpkg/ent/user_query.go +++ b/examples/entcpkg/ent/user_query.go @@ -674,12 +674,23 @@ func (ugb *UserGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (ugb *UserGroupBy) sqlQuery() *sql.Selector { selector := ugb.sql - columns := make([]string, 0, len(ugb.fields)+len(ugb.fns)) - columns = append(columns, ugb.fields...) + aggregation := make([]string, 0, len(ugb.fns)) for _, fn := range ugb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(ugb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(ugb.fields)+len(ugb.fns)) + for _, f := range ugb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(ugb.fields...)...) } // UserSelect is the builder for selecting fields of User entities. diff --git a/examples/m2m2types/ent/group_query.go b/examples/m2m2types/ent/group_query.go index 171740466..c5b82b8a5 100644 --- a/examples/m2m2types/ent/group_query.go +++ b/examples/m2m2types/ent/group_query.go @@ -782,12 +782,23 @@ func (ggb *GroupGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (ggb *GroupGroupBy) sqlQuery() *sql.Selector { selector := ggb.sql - columns := make([]string, 0, len(ggb.fields)+len(ggb.fns)) - columns = append(columns, ggb.fields...) + aggregation := make([]string, 0, len(ggb.fns)) for _, fn := range ggb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(ggb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(ggb.fields)+len(ggb.fns)) + for _, f := range ggb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(ggb.fields...)...) } // GroupSelect is the builder for selecting fields of Group entities. diff --git a/examples/m2m2types/ent/user_query.go b/examples/m2m2types/ent/user_query.go index be9ea7130..68b3a4917 100644 --- a/examples/m2m2types/ent/user_query.go +++ b/examples/m2m2types/ent/user_query.go @@ -782,12 +782,23 @@ func (ugb *UserGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (ugb *UserGroupBy) sqlQuery() *sql.Selector { selector := ugb.sql - columns := make([]string, 0, len(ugb.fields)+len(ugb.fns)) - columns = append(columns, ugb.fields...) + aggregation := make([]string, 0, len(ugb.fns)) for _, fn := range ugb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(ugb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(ugb.fields)+len(ugb.fns)) + for _, f := range ugb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(ugb.fields...)...) } // UserSelect is the builder for selecting fields of User entities. diff --git a/examples/m2mbidi/ent/user_query.go b/examples/m2mbidi/ent/user_query.go index 32563abe4..55aec3c80 100644 --- a/examples/m2mbidi/ent/user_query.go +++ b/examples/m2mbidi/ent/user_query.go @@ -781,12 +781,23 @@ func (ugb *UserGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (ugb *UserGroupBy) sqlQuery() *sql.Selector { selector := ugb.sql - columns := make([]string, 0, len(ugb.fields)+len(ugb.fns)) - columns = append(columns, ugb.fields...) + aggregation := make([]string, 0, len(ugb.fns)) for _, fn := range ugb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(ugb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(ugb.fields)+len(ugb.fns)) + for _, f := range ugb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(ugb.fields...)...) } // UserSelect is the builder for selecting fields of User entities. diff --git a/examples/m2mrecur/ent/user_query.go b/examples/m2mrecur/ent/user_query.go index 25906ed7a..d01b44060 100644 --- a/examples/m2mrecur/ent/user_query.go +++ b/examples/m2mrecur/ent/user_query.go @@ -882,12 +882,23 @@ func (ugb *UserGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (ugb *UserGroupBy) sqlQuery() *sql.Selector { selector := ugb.sql - columns := make([]string, 0, len(ugb.fields)+len(ugb.fns)) - columns = append(columns, ugb.fields...) + aggregation := make([]string, 0, len(ugb.fns)) for _, fn := range ugb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(ugb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(ugb.fields)+len(ugb.fns)) + for _, f := range ugb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(ugb.fields...)...) } // UserSelect is the builder for selecting fields of User entities. diff --git a/examples/o2m2types/ent/pet_query.go b/examples/o2m2types/ent/pet_query.go index 285de5362..52ff2adc0 100644 --- a/examples/o2m2types/ent/pet_query.go +++ b/examples/o2m2types/ent/pet_query.go @@ -753,12 +753,23 @@ func (pgb *PetGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (pgb *PetGroupBy) sqlQuery() *sql.Selector { selector := pgb.sql - columns := make([]string, 0, len(pgb.fields)+len(pgb.fns)) - columns = append(columns, pgb.fields...) + aggregation := make([]string, 0, len(pgb.fns)) for _, fn := range pgb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(pgb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(pgb.fields)+len(pgb.fns)) + for _, f := range pgb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(pgb.fields...)...) } // PetSelect is the builder for selecting fields of Pet entities. diff --git a/examples/o2m2types/ent/user_query.go b/examples/o2m2types/ent/user_query.go index 016653c20..7963c3260 100644 --- a/examples/o2m2types/ent/user_query.go +++ b/examples/o2m2types/ent/user_query.go @@ -746,12 +746,23 @@ func (ugb *UserGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (ugb *UserGroupBy) sqlQuery() *sql.Selector { selector := ugb.sql - columns := make([]string, 0, len(ugb.fields)+len(ugb.fns)) - columns = append(columns, ugb.fields...) + aggregation := make([]string, 0, len(ugb.fns)) for _, fn := range ugb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(ugb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(ugb.fields)+len(ugb.fns)) + for _, f := range ugb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(ugb.fields...)...) } // UserSelect is the builder for selecting fields of User entities. diff --git a/examples/o2mrecur/ent/node_query.go b/examples/o2mrecur/ent/node_query.go index 1d7050c9c..85f12e097 100644 --- a/examples/o2mrecur/ent/node_query.go +++ b/examples/o2mrecur/ent/node_query.go @@ -818,12 +818,23 @@ func (ngb *NodeGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (ngb *NodeGroupBy) sqlQuery() *sql.Selector { selector := ngb.sql - columns := make([]string, 0, len(ngb.fields)+len(ngb.fns)) - columns = append(columns, ngb.fields...) + aggregation := make([]string, 0, len(ngb.fns)) for _, fn := range ngb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(ngb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(ngb.fields)+len(ngb.fns)) + for _, f := range ngb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(ngb.fields...)...) } // NodeSelect is the builder for selecting fields of Node entities. diff --git a/examples/o2o2types/ent/card_query.go b/examples/o2o2types/ent/card_query.go index 79f105730..2e01417b8 100644 --- a/examples/o2o2types/ent/card_query.go +++ b/examples/o2o2types/ent/card_query.go @@ -753,12 +753,23 @@ func (cgb *CardGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (cgb *CardGroupBy) sqlQuery() *sql.Selector { selector := cgb.sql - columns := make([]string, 0, len(cgb.fields)+len(cgb.fns)) - columns = append(columns, cgb.fields...) + aggregation := make([]string, 0, len(cgb.fns)) for _, fn := range cgb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(cgb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(cgb.fields)+len(cgb.fns)) + for _, f := range cgb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(cgb.fields...)...) } // CardSelect is the builder for selecting fields of Card entities. diff --git a/examples/o2o2types/ent/user_query.go b/examples/o2o2types/ent/user_query.go index a5e412938..01d578281 100644 --- a/examples/o2o2types/ent/user_query.go +++ b/examples/o2o2types/ent/user_query.go @@ -745,12 +745,23 @@ func (ugb *UserGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (ugb *UserGroupBy) sqlQuery() *sql.Selector { selector := ugb.sql - columns := make([]string, 0, len(ugb.fields)+len(ugb.fns)) - columns = append(columns, ugb.fields...) + aggregation := make([]string, 0, len(ugb.fns)) for _, fn := range ugb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(ugb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(ugb.fields)+len(ugb.fns)) + for _, f := range ugb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(ugb.fields...)...) } // UserSelect is the builder for selecting fields of User entities. diff --git a/examples/o2obidi/ent/user_query.go b/examples/o2obidi/ent/user_query.go index 341580779..4bf0b8614 100644 --- a/examples/o2obidi/ent/user_query.go +++ b/examples/o2obidi/ent/user_query.go @@ -752,12 +752,23 @@ func (ugb *UserGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (ugb *UserGroupBy) sqlQuery() *sql.Selector { selector := ugb.sql - columns := make([]string, 0, len(ugb.fields)+len(ugb.fns)) - columns = append(columns, ugb.fields...) + aggregation := make([]string, 0, len(ugb.fns)) for _, fn := range ugb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(ugb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(ugb.fields)+len(ugb.fns)) + for _, f := range ugb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(ugb.fields...)...) } // UserSelect is the builder for selecting fields of User entities. diff --git a/examples/o2orecur/ent/node_query.go b/examples/o2orecur/ent/node_query.go index 1373c99fe..face59493 100644 --- a/examples/o2orecur/ent/node_query.go +++ b/examples/o2orecur/ent/node_query.go @@ -817,12 +817,23 @@ func (ngb *NodeGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (ngb *NodeGroupBy) sqlQuery() *sql.Selector { selector := ngb.sql - columns := make([]string, 0, len(ngb.fields)+len(ngb.fns)) - columns = append(columns, ngb.fields...) + aggregation := make([]string, 0, len(ngb.fns)) for _, fn := range ngb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(ngb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(ngb.fields)+len(ngb.fns)) + for _, f := range ngb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(ngb.fields...)...) } // NodeSelect is the builder for selecting fields of Node entities. diff --git a/examples/privacyadmin/ent/user_query.go b/examples/privacyadmin/ent/user_query.go index 63f6f130d..27f0d1af3 100644 --- a/examples/privacyadmin/ent/user_query.go +++ b/examples/privacyadmin/ent/user_query.go @@ -680,12 +680,23 @@ func (ugb *UserGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (ugb *UserGroupBy) sqlQuery() *sql.Selector { selector := ugb.sql - columns := make([]string, 0, len(ugb.fields)+len(ugb.fns)) - columns = append(columns, ugb.fields...) + aggregation := make([]string, 0, len(ugb.fns)) for _, fn := range ugb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(ugb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(ugb.fields)+len(ugb.fns)) + for _, f := range ugb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(ugb.fields...)...) } // UserSelect is the builder for selecting fields of User entities. diff --git a/examples/privacytenant/ent/group_query.go b/examples/privacytenant/ent/group_query.go index cb420424a..da7318746 100644 --- a/examples/privacytenant/ent/group_query.go +++ b/examples/privacytenant/ent/group_query.go @@ -862,12 +862,23 @@ func (ggb *GroupGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (ggb *GroupGroupBy) sqlQuery() *sql.Selector { selector := ggb.sql - columns := make([]string, 0, len(ggb.fields)+len(ggb.fns)) - columns = append(columns, ggb.fields...) + aggregation := make([]string, 0, len(ggb.fns)) for _, fn := range ggb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(ggb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(ggb.fields)+len(ggb.fns)) + for _, f := range ggb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(ggb.fields...)...) } // GroupSelect is the builder for selecting fields of Group entities. diff --git a/examples/privacytenant/ent/tenant_query.go b/examples/privacytenant/ent/tenant_query.go index 5a555173c..56930586c 100644 --- a/examples/privacytenant/ent/tenant_query.go +++ b/examples/privacytenant/ent/tenant_query.go @@ -680,12 +680,23 @@ func (tgb *TenantGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (tgb *TenantGroupBy) sqlQuery() *sql.Selector { selector := tgb.sql - columns := make([]string, 0, len(tgb.fields)+len(tgb.fns)) - columns = append(columns, tgb.fields...) + aggregation := make([]string, 0, len(tgb.fns)) for _, fn := range tgb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(tgb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(tgb.fields)+len(tgb.fns)) + for _, f := range tgb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(tgb.fields...)...) } // TenantSelect is the builder for selecting fields of Tenant entities. diff --git a/examples/privacytenant/ent/user_query.go b/examples/privacytenant/ent/user_query.go index 21bccd245..4e3a760d4 100644 --- a/examples/privacytenant/ent/user_query.go +++ b/examples/privacytenant/ent/user_query.go @@ -862,12 +862,23 @@ func (ugb *UserGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (ugb *UserGroupBy) sqlQuery() *sql.Selector { selector := ugb.sql - columns := make([]string, 0, len(ugb.fields)+len(ugb.fns)) - columns = append(columns, ugb.fields...) + aggregation := make([]string, 0, len(ugb.fns)) for _, fn := range ugb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(ugb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(ugb.fields)+len(ugb.fns)) + for _, f := range ugb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(ugb.fields...)...) } // UserSelect is the builder for selecting fields of User entities. diff --git a/examples/start/ent/car_query.go b/examples/start/ent/car_query.go index f49fe29c6..653aa1800 100644 --- a/examples/start/ent/car_query.go +++ b/examples/start/ent/car_query.go @@ -753,12 +753,23 @@ func (cgb *CarGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (cgb *CarGroupBy) sqlQuery() *sql.Selector { selector := cgb.sql - columns := make([]string, 0, len(cgb.fields)+len(cgb.fns)) - columns = append(columns, cgb.fields...) + aggregation := make([]string, 0, len(cgb.fns)) for _, fn := range cgb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(cgb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(cgb.fields)+len(cgb.fns)) + for _, f := range cgb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(cgb.fields...)...) } // CarSelect is the builder for selecting fields of Car entities. diff --git a/examples/start/ent/group_query.go b/examples/start/ent/group_query.go index 001bd0c6b..00488ef29 100644 --- a/examples/start/ent/group_query.go +++ b/examples/start/ent/group_query.go @@ -782,12 +782,23 @@ func (ggb *GroupGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (ggb *GroupGroupBy) sqlQuery() *sql.Selector { selector := ggb.sql - columns := make([]string, 0, len(ggb.fields)+len(ggb.fns)) - columns = append(columns, ggb.fields...) + aggregation := make([]string, 0, len(ggb.fns)) for _, fn := range ggb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(ggb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(ggb.fields)+len(ggb.fns)) + for _, f := range ggb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(ggb.fields...)...) } // GroupSelect is the builder for selecting fields of Group entities. diff --git a/examples/start/ent/user_query.go b/examples/start/ent/user_query.go index 3c61c0c01..51523dcee 100644 --- a/examples/start/ent/user_query.go +++ b/examples/start/ent/user_query.go @@ -848,12 +848,23 @@ func (ugb *UserGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (ugb *UserGroupBy) sqlQuery() *sql.Selector { selector := ugb.sql - columns := make([]string, 0, len(ugb.fields)+len(ugb.fns)) - columns = append(columns, ugb.fields...) + aggregation := make([]string, 0, len(ugb.fns)) for _, fn := range ugb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(ugb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(ugb.fields)+len(ugb.fns)) + for _, f := range ugb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(ugb.fields...)...) } // UserSelect is the builder for selecting fields of User entities. diff --git a/examples/traversal/ent/group_query.go b/examples/traversal/ent/group_query.go index ad65d9cdb..2c220f013 100644 --- a/examples/traversal/ent/group_query.go +++ b/examples/traversal/ent/group_query.go @@ -855,12 +855,23 @@ func (ggb *GroupGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (ggb *GroupGroupBy) sqlQuery() *sql.Selector { selector := ggb.sql - columns := make([]string, 0, len(ggb.fields)+len(ggb.fns)) - columns = append(columns, ggb.fields...) + aggregation := make([]string, 0, len(ggb.fns)) for _, fn := range ggb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(ggb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(ggb.fields)+len(ggb.fns)) + for _, f := range ggb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(ggb.fields...)...) } // GroupSelect is the builder for selecting fields of Group entities. diff --git a/examples/traversal/ent/pet_query.go b/examples/traversal/ent/pet_query.go index 0ef74325a..a9693db7d 100644 --- a/examples/traversal/ent/pet_query.go +++ b/examples/traversal/ent/pet_query.go @@ -855,12 +855,23 @@ func (pgb *PetGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (pgb *PetGroupBy) sqlQuery() *sql.Selector { selector := pgb.sql - columns := make([]string, 0, len(pgb.fields)+len(pgb.fns)) - columns = append(columns, pgb.fields...) + aggregation := make([]string, 0, len(pgb.fns)) for _, fn := range pgb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(pgb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(pgb.fields)+len(pgb.fns)) + for _, f := range pgb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(pgb.fields...)...) } // PetSelect is the builder for selecting fields of Pet entities. diff --git a/examples/traversal/ent/user_query.go b/examples/traversal/ent/user_query.go index cd7500885..72acbd5fb 100644 --- a/examples/traversal/ent/user_query.go +++ b/examples/traversal/ent/user_query.go @@ -1014,12 +1014,23 @@ func (ugb *UserGroupBy) sqlScan(ctx context.Context, v interface{}) error { func (ugb *UserGroupBy) sqlQuery() *sql.Selector { selector := ugb.sql - columns := make([]string, 0, len(ugb.fields)+len(ugb.fns)) - columns = append(columns, ugb.fields...) + aggregation := make([]string, 0, len(ugb.fns)) for _, fn := range ugb.fns { - columns = append(columns, fn(selector)) + aggregation = append(aggregation, fn(selector)) } - return selector.Select(columns...).GroupBy(ugb.fields...) + // If no columns were selected in a custom aggregation function, the default + // selection is the fields used for "group-by", and the aggregation functions. + if len(selector.Columns()) == 0 { + columns := make([]string, 0, len(ugb.fields)+len(ugb.fns)) + for _, f := range ugb.fields { + columns = append(columns, selector.C(f)) + } + for _, c := range aggregation { + columns = append(columns, c) + } + selector.Select(columns...) + } + return selector.GroupBy(selector.Columns(ugb.fields...)...) } // UserSelect is the builder for selecting fields of User entities.