From caf721df4769c0babfa8e79b6fee720cce8f9410 Mon Sep 17 00:00:00 2001 From: Ariel Mashraki <7413593+a8m@users.noreply.github.com> Date: Mon, 13 Jan 2020 17:21:26 +0200 Subject: [PATCH] entc/gen: add eager-loading support (#263) * entc/gen: add OwnFK indicator for type edges * entc/gen: add Edges field for generated types * entc/gen: add With method to query-builder template * entc/gen: scan and assign foreign-keys on eager-loading * entc/gen: load fk-relations (wip) * entc/integration: add o2m/m2o tests for eager-loading * entc/gen: add m2m support for eager-loading * entc/gen: add integration tests for m2m and subgraphs * entc/gen/integration: add tests for o2o eager-loading * all: generate all assets --- entc/gen/graph.go | 7 +- entc/gen/internal/bindata.go | 24 +- entc/gen/template/builder/query.tmpl | 33 +- entc/gen/template/client.tmpl | 4 +- entc/gen/template/dialect/sql/decode.tmpl | 57 +- entc/gen/template/dialect/sql/meta.tmpl | 15 +- entc/gen/template/dialect/sql/query.tmpl | 175 ++++- entc/gen/template/ent.tmpl | 29 +- entc/gen/type.go | 97 ++- entc/integration/config/ent/user.go | 4 +- entc/integration/config/ent/user/user.go | 2 +- entc/integration/config/ent/user_query.go | 3 +- entc/integration/customid/ent/blob.go | 6 +- entc/integration/customid/ent/blob/blob.go | 2 +- entc/integration/customid/ent/blob_query.go | 3 +- entc/integration/customid/ent/client.go | 28 + entc/integration/customid/ent/example_test.go | 11 + entc/integration/customid/ent/group.go | 10 +- entc/integration/customid/ent/group/group.go | 2 +- entc/integration/customid/ent/group_query.go | 81 ++- .../customid/ent/migrate/schema.go | 18 +- entc/integration/customid/ent/schema/user.go | 3 + entc/integration/customid/ent/user.go | 41 +- entc/integration/customid/ent/user/user.go | 15 +- entc/integration/customid/ent/user/where.go | 60 ++ entc/integration/customid/ent/user_create.go | 90 ++- entc/integration/customid/ent/user_query.go | 197 +++++- entc/integration/customid/ent/user_update.go | 309 ++++++++- entc/integration/ent/card.go | 35 +- entc/integration/ent/card/card.go | 7 +- entc/integration/ent/card_query.go | 57 +- entc/integration/ent/comment.go | 10 +- entc/integration/ent/comment/comment.go | 2 +- entc/integration/ent/comment_query.go | 3 +- entc/integration/ent/fieldtype.go | 38 +- entc/integration/ent/fieldtype/fieldtype.go | 2 +- entc/integration/ent/fieldtype_query.go | 3 +- entc/integration/ent/file.go | 53 +- entc/integration/ent/file/file.go | 9 +- entc/integration/ent/file_query.go | 94 ++- entc/integration/ent/filetype.go | 12 +- entc/integration/ent/filetype/filetype.go | 2 +- entc/integration/ent/filetype_query.go | 51 +- entc/integration/ent/group.go | 43 +- entc/integration/ent/group/group.go | 7 +- entc/integration/ent/group_query.go | 222 ++++++- entc/integration/ent/groupinfo.go | 14 +- entc/integration/ent/groupinfo/groupinfo.go | 2 +- entc/integration/ent/groupinfo_query.go | 51 +- entc/integration/ent/item.go | 4 +- entc/integration/ent/item/item.go | 2 +- entc/integration/ent/item_query.go | 3 +- entc/integration/ent/node.go | 31 +- entc/integration/ent/node/node.go | 7 +- entc/integration/ent/node_query.go | 103 ++- entc/integration/ent/pet.go | 39 +- entc/integration/ent/pet/pet.go | 8 +- entc/integration/ent/pet_query.go | 94 ++- entc/integration/ent/user.go | 79 ++- entc/integration/ent/user/user.go | 9 +- entc/integration/ent/user_query.go | 616 +++++++++++++++++- entc/integration/gremlin/ent/card.go | 6 + entc/integration/gremlin/ent/card_query.go | 13 + entc/integration/gremlin/ent/file.go | 8 + entc/integration/gremlin/ent/file_query.go | 25 + entc/integration/gremlin/ent/filetype.go | 6 + .../integration/gremlin/ent/filetype_query.go | 13 + entc/integration/gremlin/ent/group.go | 12 + entc/integration/gremlin/ent/group_query.go | 49 ++ entc/integration/gremlin/ent/groupinfo.go | 6 + .../gremlin/ent/groupinfo_query.go | 13 + entc/integration/gremlin/ent/node.go | 8 + entc/integration/gremlin/ent/node_query.go | 25 + entc/integration/gremlin/ent/pet.go | 8 + entc/integration/gremlin/ent/pet_query.go | 25 + entc/integration/gremlin/ent/user.go | 26 + entc/integration/gremlin/ent/user_query.go | 133 ++++ entc/integration/idtype/ent/user.go | 33 +- entc/integration/idtype/ent/user/user.go | 7 +- entc/integration/idtype/ent/user_query.go | 208 +++++- entc/integration/integration_test.go | 125 ++++ entc/integration/json/ent/user.go | 16 +- entc/integration/json/ent/user/user.go | 2 +- entc/integration/json/ent/user_query.go | 3 +- entc/integration/migrate/entv1/car.go | 27 +- entc/integration/migrate/entv1/car/car.go | 7 +- entc/integration/migrate/entv1/car_query.go | 57 +- entc/integration/migrate/entv1/user.go | 55 +- entc/integration/migrate/entv1/user/user.go | 8 +- entc/integration/migrate/entv1/user_query.go | 175 ++++- entc/integration/migrate/entv2/car.go | 27 +- entc/integration/migrate/entv2/car/car.go | 7 +- entc/integration/migrate/entv2/car_query.go | 57 +- entc/integration/migrate/entv2/group.go | 4 +- entc/integration/migrate/entv2/group/group.go | 2 +- entc/integration/migrate/entv2/group_query.go | 3 +- entc/integration/migrate/entv2/pet.go | 4 +- entc/integration/migrate/entv2/pet/pet.go | 2 +- entc/integration/migrate/entv2/pet_query.go | 3 +- entc/integration/migrate/entv2/user.go | 28 +- entc/integration/migrate/entv2/user/user.go | 2 +- entc/integration/migrate/entv2/user_query.go | 46 +- entc/integration/template/ent/group.go | 6 +- entc/integration/template/ent/group/group.go | 2 +- entc/integration/template/ent/group_query.go | 3 +- entc/integration/template/ent/pet.go | 31 +- entc/integration/template/ent/pet/pet.go | 7 +- entc/integration/template/ent/pet_query.go | 57 +- entc/integration/template/ent/user.go | 14 +- entc/integration/template/ent/user/user.go | 2 +- entc/integration/template/ent/user_query.go | 121 +++- examples/edgeindex/ent/city.go | 12 +- examples/edgeindex/ent/city/city.go | 2 +- examples/edgeindex/ent/city_query.go | 46 +- examples/edgeindex/ent/street.go | 29 +- examples/edgeindex/ent/street/street.go | 7 +- examples/edgeindex/ent/street_query.go | 57 +- examples/entcpkg/ent/user.go | 4 +- examples/entcpkg/ent/user/user.go | 2 +- examples/entcpkg/ent/user_query.go | 3 +- examples/m2m2types/ent/group.go | 12 +- examples/m2m2types/ent/group/group.go | 2 +- examples/m2m2types/ent/group_query.go | 81 ++- examples/m2m2types/ent/user.go | 14 +- examples/m2m2types/ent/user/user.go | 2 +- examples/m2m2types/ent/user_query.go | 81 ++- examples/m2mbidi/ent/user.go | 14 +- examples/m2mbidi/ent/user/user.go | 2 +- examples/m2mbidi/ent/user_query.go | 81 ++- examples/m2mrecur/ent/user.go | 16 +- examples/m2mrecur/ent/user/user.go | 2 +- examples/m2mrecur/ent/user_query.go | 156 ++++- examples/o2m2types/ent/pet.go | 29 +- examples/o2m2types/ent/pet/pet.go | 7 +- examples/o2m2types/ent/pet_query.go | 57 +- examples/o2m2types/ent/user.go | 14 +- examples/o2m2types/ent/user/user.go | 2 +- examples/o2m2types/ent/user_query.go | 46 +- examples/o2mrecur/ent/node.go | 31 +- examples/o2mrecur/ent/node/node.go | 7 +- examples/o2mrecur/ent/node_query.go | 98 ++- examples/o2o2types/ent/card.go | 31 +- examples/o2o2types/ent/card/card.go | 7 +- examples/o2o2types/ent/card_query.go | 57 +- examples/o2o2types/ent/user.go | 14 +- examples/o2o2types/ent/user/user.go | 2 +- examples/o2o2types/ent/user_query.go | 46 +- examples/o2obidi/ent/user.go | 31 +- examples/o2obidi/ent/user/user.go | 7 +- examples/o2obidi/ent/user_query.go | 57 +- examples/o2orecur/ent/node.go | 31 +- examples/o2orecur/ent/node/node.go | 7 +- examples/o2orecur/ent/node_query.go | 98 ++- examples/start/ent/car.go | 31 +- examples/start/ent/car/car.go | 7 +- examples/start/ent/car_query.go | 57 +- examples/start/ent/group.go | 12 +- examples/start/ent/group/group.go | 2 +- examples/start/ent/group_query.go | 81 ++- examples/start/ent/user.go | 16 +- examples/start/ent/user/user.go | 2 +- examples/start/ent/user_query.go | 121 +++- examples/traversal/ent/group.go | 31 +- examples/traversal/ent/group/group.go | 7 +- examples/traversal/ent/group_query.go | 133 +++- examples/traversal/ent/pet.go | 31 +- examples/traversal/ent/pet/pet.go | 7 +- examples/traversal/ent/pet_query.go | 133 +++- examples/traversal/ent/user.go | 20 +- examples/traversal/ent/user/user.go | 2 +- examples/traversal/ent/user_query.go | 236 ++++++- 171 files changed, 6400 insertions(+), 398 deletions(-) diff --git a/entc/gen/graph.go b/entc/gen/graph.go index 3ec74e823..05ef34459 100644 --- a/entc/gen/graph.go +++ b/entc/gen/graph.go @@ -67,7 +67,10 @@ func NewGraph(c *Config, schemas ...*load.Schema) (g *Graph, err error) { g.addEdges(schema) } for _, t := range g.Nodes { - check(g.resolve(t), "resolve %q relations", t.Name) + check(resolve(t), "resolve %q relations", t.Name) + } + for _, t := range g.Nodes { + t.resolveFKs() } for _, schema := range schemas { g.addIndexes(schema) @@ -206,7 +209,7 @@ func (g *Graph) addEdges(schema *load.Schema) { // - A have an edge (E) to B (not unique), and B have a back-reference non-unique edge (E') for E. // - A have an edge (E) to A (not unique). // -func (g *Graph) resolve(t *Type) error { +func resolve(t *Type) error { for _, e := range t.Edges { switch { case e.IsInverse(): diff --git a/entc/gen/internal/bindata.go b/entc/gen/internal/bindata.go index 71d14fd34..bffa9f9a7 100644 --- a/entc/gen/internal/bindata.go +++ b/entc/gen/internal/bindata.go @@ -181,7 +181,7 @@ func templateBuilderDeleteTmpl() (*asset, error) { return a, nil } -var _templateBuilderQueryTmpl = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xcc\x5a\x6d\x6f\xe3\x36\xf2\x7f\x2d\x7d\x8a\xa9\x91\x06\xf6\xc2\x4b\xef\xee\xbb\x7f\xfe\xc8\x01\xe9\x66\xf7\x60\xa0\xd8\xde\xed\x16\xb8\x02\x8b\x45\xab\x48\x23\x9b\x8d\x4c\xaa\x24\xe5\x24\xf0\xe9\xbb\x1f\x38\xa4\x1e\x2d\xc7\x72\x92\xeb\xf5\x95\x2d\x69\x38\x9c\x87\x1f\x7f\xc3\xa7\xdd\x6e\xf1\x2a\x7c\x2f\xf3\x07\xc5\x57\x6b\x03\xef\xde\xbc\xfd\xbf\xd7\xb9\x42\x8d\xc2\xc0\xc7\x28\xc6\x1b\x29\x6f\x61\x29\x62\x06\x57\x59\x06\x24\xa4\xc1\x7e\x57\x5b\x4c\x58\xf8\xf3\x9a\x6b\xd0\xb2\x50\x31\x42\x2c\x13\x04\xae\x21\xe3\x31\x0a\x8d\x09\x14\x22\x41\x05\x66\x8d\x70\x95\x47\xf1\x1a\xe1\x1d\x7b\x53\x7d\x85\x54\x16\x22\x09\xb9\xa0\xef\x3f\x2e\xdf\x7f\xf8\xf4\xe5\x03\xa4\x3c\x43\xf0\xef\x94\x94\x06\x12\xae\x30\x36\x52\x3d\x80\x4c\xc1\xb4\x3a\x33\x0a\x91\x85\xaf\x16\x65\x19\x86\xbb\x1d\x24\x98\x72\x81\x30\xf9\xa3\x40\xf5\x30\x81\xb2\xb4\x2f\xcf\xf2\xdb\x15\x5c\x5c\xc2\x4d\xa4\x11\xce\xd8\x7b\x29\x52\xbe\x62\xff\x88\xe2\xdb\x68\x85\xe0\x5b\x1a\xdc\xe4\x59\x64\x10\x26\x6b\x8c\x12\x54\x13\x38\xdb\xff\xc4\x37\xb9\x54\xa6\xf5\xe9\xec\xa6\xe0\x99\xf5\xee\xe2\x12\x72\xc5\x85\x81\x69\x1e\xe9\x38\xca\xe0\x8c\x7d\x8a\x36\x38\x83\xc9\x3f\x3b\xa6\x28\x8c\x91\x6f\x5d\x83\xfa\x7f\xad\xc5\x6a\x5d\x2c\xa0\xad\xb8\x2c\x6d\x2c\x6d\x20\xaa\x37\xa9\x54\x40\xfe\x71\xb1\xb2\xa2\x9d\x0e\xad\x3c\x0a\xc3\x0d\x47\xcd\x42\xf3\x90\x63\x5f\x9b\x36\xaa\x88\x0d\xec\xc2\x20\xa6\x40\x84\x41\xc6\x37\xdc\x04\xc1\x2b\x2e\x4c\x18\xc8\x34\xd5\xd8\x3c\xa9\x04\x55\x10\x7c\xfd\xf6\x93\xfd\x13\x06\x85\xe0\x7f\x14\x68\x5f\x68\xa3\xb8\x58\x85\x41\xae\x30\xe1\x71\x64\x50\x43\xf0\xf5\x5b\xfd\xc4\x6c\xaf\x95\x45\x61\xb0\x58\x00\x17\x06\xd5\x06\x13\x6e\x23\x49\xf6\xb3\x30\x20\xa9\x2f\x46\x2a\x97\x09\x68\x3f\xb3\x1f\x9c\xd1\x65\x19\xba\xb0\xfc\x6b\x8d\x0a\x21\x4a\x12\x0d\x11\x08\xbc\x83\xba\x37\x8a\x49\x2b\x46\x2c\x4c\x0b\x11\xc3\xb4\x13\xf1\xb2\x84\x57\xdd\x58\xcc\x9c\xca\x69\xae\x81\x31\x36\x6c\xfb\xac\xdf\xc8\x46\xae\xad\xb7\x2c\x59\x2b\x06\x97\x10\xe5\x39\x8a\xa4\xdf\x75\x4b\x66\x0e\xb9\x66\x8c\xcd\xc2\x40\xa1\x29\x94\x80\x9e\xa8\xf7\xf6\x47\x9b\x95\xca\x5b\x4a\x11\x68\x83\x39\x18\x49\x9e\xfa\x08\x8e\xf4\x93\x94\x4d\x9d\x16\x2e\xcc\x51\xa7\xac\xc5\x4e\xfa\x12\xce\xe9\xcf\x11\x6b\x7f\x22\xd8\x78\x73\x05\x38\x14\x3d\xc3\x60\xa7\x6f\xea\xf5\x8c\x35\xd9\x8b\x5f\xc2\xb9\xfb\x77\xcc\x68\x0b\xea\xc6\x66\x7a\x7a\x86\xc9\xb6\xfd\x54\x5a\x28\xd1\xdf\x71\x16\x53\xa7\x07\x51\x43\x9f\xe7\x20\x8f\xe1\xc5\xd2\xb7\xe3\x45\x62\xdf\x75\xa4\x41\xf3\x0d\xcf\x22\xc5\xcd\x03\xdc\x71\xb3\x06\x4c\x56\xce\x23\x8e\xda\x72\x6b\x9c\x71\x14\x86\x99\x4d\x9e\x01\xf1\xe7\x6e\x07\x2a\x12\x2b\x84\xb3\x5f\xe7\x70\x86\x96\xa1\xce\xd8\x87\x64\x85\x9a\x86\xb0\xed\xd5\xea\xf8\xf5\x30\xe5\x21\xfb\xf9\x21\xc7\x7d\xe2\xb3\xc3\x9f\x9e\x5a\x6c\x85\x35\x5d\xc5\xeb\x88\x0b\x47\x71\x71\xa1\x94\xad\x38\x14\x78\x90\xae\x00\x50\xc7\x0d\xb9\x25\x2b\x64\x61\x30\x32\x27\x07\x7b\x9d\xfa\xec\x74\x3c\x72\x29\x0a\x5c\xef\x17\x97\x70\x3e\x20\xb1\x73\xac\x79\xd1\xcf\x02\x73\xef\xcb\x30\x08\x76\xbb\xd7\x3e\xe4\xf7\x06\x45\x02\x67\x30\xf9\xec\x05\x27\xad\x36\x13\x1b\xdb\x89\x8d\xf4\x6b\x1b\xa3\x80\x22\x4c\xe9\xa8\x22\x9b\xc2\x24\xe1\x51\x86\xb1\x59\x7c\xaf\x17\x64\xd6\x22\x8f\xcc\x7a\xd2\x22\x4d\x70\x6d\xa9\xd3\xfb\xba\x52\x39\x3d\xcc\x7d\xb4\x9f\xac\x1d\xae\x1b\x0f\x22\xd2\x16\x06\x94\x76\xfb\xd1\x97\x9e\x8f\x5c\x69\x03\x4e\xc6\xe5\x24\xa5\x37\x6d\x4a\x74\x05\xe6\xa1\x2a\xd0\x6e\x94\xc0\x67\xdf\xe6\xd5\x07\xa5\x3e\x49\xf3\xd1\xd6\x75\xb8\x5b\xa3\x00\x21\x6d\xf3\x4c\xde\xd9\x52\x57\x2b\xb9\x8b\xb4\x2b\xfe\xa3\x07\x18\xd9\x36\x8d\xcd\x3d\xc4\x52\x18\xbc\x37\xb6\x94\xdb\xdf\x19\x4c\x5f\xb5\x0d\x9c\x03\x2a\x25\xd5\xcc\x0f\xb8\x3c\x2b\x14\x15\xc8\xcf\x8d\x76\x12\xb1\x81\xee\xa7\xd1\x31\xe5\xdb\x19\xbb\xca\x32\xdb\xd7\x2c\x0c\x78\x4a\xc2\xdf\x5d\x82\xe0\x19\x21\xc4\xc7\x50\xf0\x8c\xf4\xd8\x30\x5a\xa9\x0c\xc5\xf4\x40\x7f\x33\xb8\xbc\x84\x37\x7b\x8d\xcf\x5b\xc1\xda\xb9\x20\x37\xf3\x12\xf6\x63\x74\x83\x59\x49\xda\x9b\xa1\x3f\xa4\xfd\xeb\x9b\x6f\x73\xab\x30\x6c\x25\xf1\x17\x37\x07\xbb\x45\xf7\x38\x87\x9b\xc2\x40\x1e\x09\x1e\x6b\xe0\xa9\x25\x3c\x0a\x12\xc8\x38\x2e\x94\x3e\x2d\x09\xbf\x0c\x67\xa1\x93\x84\x8a\xed\x46\x45\xbd\x4e\xed\x5e\xb8\xcf\xcf\xe1\xbb\xa5\xae\x62\x34\x45\xe5\xd2\x1a\x90\x27\xf4\xd8\x8b\x4f\xa7\xc3\x76\x40\x96\xd7\xc7\x70\xcd\x93\x53\x30\xcd\x93\xa7\x62\x78\x79\x7d\x00\xc5\x3c\x71\x06\x2d\xaf\x89\x49\xeb\x88\x35\x70\xde\x46\x0a\x78\xa2\xe1\xeb\xb7\x9e\x20\xc5\x8d\x27\xda\x35\x78\x04\xd7\xcb\x6b\x4d\x81\xfe\xff\x61\x50\xb7\xb1\xcc\x13\xdd\xc2\xad\xd3\x3b\x0e\xb1\x6d\x65\x3e\x35\x3c\xd1\x83\x30\x5d\x5e\x77\x81\xba\xbc\x7e\x59\xa8\x1e\x0a\x76\x2f\x7e\xd6\x45\x9e\x3c\x0e\x50\xa7\xea\x99\x10\xe5\x49\x35\xfb\x10\xd9\x43\x07\x91\xd2\xbe\x38\x46\xb4\xf3\xba\x49\x1d\x16\x9e\x82\x90\x06\xf0\x3e\x8a\x4d\x66\xcb\x26\x56\x0d\x2d\x3e\x9d\x38\x8e\x87\xa8\xb5\xeb\xcf\x61\xd9\x77\xa7\xb3\xac\xbe\xe3\x26\x5e\x3f\xce\xb4\x76\x69\x63\x97\x7a\x6f\x2f\x1a\x25\xc7\x88\xd3\xb5\x78\x73\xf1\x24\x7e\x4e\x30\x8d\x8a\xcc\x1c\x68\xfc\x85\x8b\x55\x91\x45\xea\x31\x7e\x6f\x10\xd1\xd0\xb6\x7d\x7a\xa9\xa1\x40\x9a\x5f\x9a\xb4\x2b\xa0\x0c\x26\xef\x24\x7e\xb6\x9a\x7a\xf4\xbc\x3f\x18\x7a\xec\x3c\x6e\x20\x78\x92\x7e\xd2\x20\xf8\xdf\xd1\xf4\xbb\x71\x34\xdd\x1a\x0c\x44\xd5\x1d\xe0\xf3\x04\x2e\x3d\xe9\xb6\xd1\x7d\x0a\x8b\xb7\x70\xdd\x69\x36\x06\xd1\x95\x9d\x2d\x64\xb7\x98\xde\x85\xf7\x45\xd1\xfd\x32\x3c\xdf\xe4\xfd\x04\x54\xd7\x94\x7e\x95\x65\x80\xf7\x18\x17\x06\x75\x83\x54\x88\x44\xd2\x80\x15\x32\xae\x0d\xc8\xb4\x43\x49\x1e\xe3\xa3\x3d\xf6\xb4\x39\x80\xcd\xaf\xdf\x0e\x92\xf4\xf0\xf2\x91\xf5\xf6\x60\x6a\x46\xae\x5d\x6a\x28\xe9\x2a\xcb\x5e\x2a\x67\x56\xef\xb0\x0b\x3d\x0f\x9e\x52\x5e\x1e\xab\x2a\x07\x89\x69\xa8\x07\x1f\x84\xe5\xb5\x3e\x29\xaf\x6d\xd2\x1a\x1f\x12\x3f\xe4\x07\x93\x3a\xc4\x37\xa3\xb8\xe6\x40\x84\xbe\xa0\x5d\x56\x4e\xfb\xe3\xf7\x23\xc7\x2c\x59\x5e\xcf\xd8\x97\x38\x12\xd6\x98\x39\x9c\x5b\x6a\x39\xc0\x42\xdd\xda\xdc\x4c\xf4\xda\xb3\xbc\xe5\xb5\x6e\x00\xb4\xbc\xd6\x2f\x05\x20\xab\xf7\x10\x80\x06\xc7\xbc\x3e\x08\x97\x8a\x6b\x4f\x19\xf1\xda\xbb\xf7\x5e\x16\xa2\xbb\x60\x8e\xe9\x0d\xed\x53\x23\xac\xf8\x16\xc5\x89\x1b\x49\xa4\xf2\x50\xe9\x11\xe6\xf4\x21\x5d\x2b\x9c\xb5\x8d\x6e\xb2\x42\x8f\x2f\x95\x17\xa7\x7b\xd8\x7c\x2e\xfc\xc6\x73\xe1\xdd\x18\xca\x46\xcb\xda\xd1\xf9\x20\x8d\xde\xb9\x0f\xf7\xbc\xbd\x85\xa1\x0a\xb4\xee\x34\xa3\x76\x1d\x69\xc0\x0c\x37\x28\x8c\xae\x66\x14\x2b\x15\xe5\xeb\xd1\x2e\x52\x0f\x07\x12\x74\x23\x65\x76\x7a\x86\x6a\x8d\xb3\xb6\x13\x4d\x86\xe8\xf1\xa5\x32\xe4\x74\x0f\xdb\x6f\xcd\xb7\x66\xa3\xeb\xf0\x40\x8a\x5a\xe6\x8e\x4e\x11\x69\xac\xf0\x97\xd9\xb9\x59\x43\x9d\x49\x91\x67\x6e\x23\x5f\xb6\x33\xe5\x8d\x9e\x03\x17\x71\x56\x24\x5c\xac\x20\xca\x32\x88\xb4\x96\x31\x8f\x0c\x26\xb4\x5b\xab\x19\x2c\x0d\xc4\x91\x80\x1b\xb4\xca\x0b\x8d\x09\x18\x09\xb9\xc2\x3c\x52\x76\x3c\x6e\x36\x52\x74\x55\x6a\xe2\xee\x42\xa3\xed\x6d\x03\x09\x4f\x53\x54\x28\xec\xac\x31\x4a\x8d\x3f\xad\x8a\xc9\x4a\xae\x61\x13\x25\x38\x1e\xff\xb6\xd5\x74\x70\xf3\xd7\x47\xe2\xbc\xfb\xc5\x86\xac\xda\x54\xdc\xdb\x1f\x76\x1f\xe6\x61\xe0\x4e\x69\x2e\x20\x18\xde\xa7\xb7\x12\x6e\xcf\x7b\x40\x89\xfb\x40\x22\x2a\x41\x65\x95\xf8\xfd\x66\x7f\xb0\xb3\x2b\xe7\x7b\x39\x26\x51\xc6\xd8\xcc\xb6\x73\xe7\x3e\x17\xd0\xb4\x73\xe7\x3f\x43\x0d\x9d\x6c\xd5\xb2\x39\xf7\xb8\x80\xba\xf1\xf0\x51\xcb\x90\xb2\xa6\x79\xa5\x70\xb1\xa8\x12\x33\x74\xa0\xd4\x3f\x51\xda\xdf\xa7\xed\x09\x30\x9f\xaf\xb9\x5f\x86\xd9\xcf\x2b\x25\x8b\xfc\x87\xd6\x36\x77\xe7\x84\xed\xdf\xf5\xe6\xec\xf7\xfa\xef\x24\xe9\x76\xb9\x2d\xf6\xfc\x73\x8d\x41\xd2\x04\x5b\x54\x86\xc7\xa8\xe1\xc6\x2d\x4a\xa4\x82\x8d\x54\x08\xa9\xad\xb6\x7a\x11\xcb\xac\xd8\x08\xcd\xa8\x5a\x1a\x0b\x38\x99\x1a\x14\x4e\x09\x6d\x24\x47\xab\x95\xc2\x15\x1d\x74\x15\x22\x36\x5c\x0a\x3d\x27\x62\x20\xf7\x7e\x97\x5c\xc0\xf4\x16\x1f\x74\x23\x38\x83\xc9\x1c\x26\x34\xa5\xac\xb7\xa3\x33\x14\x70\xe6\x4a\xbc\x76\xe7\x91\xaf\xe1\x2c\xb5\x0e\x72\x91\xe0\x7d\xf3\xed\x8d\xfd\xba\x58\x38\x1e\x8a\x36\x79\x86\x17\xee\x91\xe6\x1a\x5b\xa0\xec\xbb\x43\xc4\xc5\xc2\x85\x3c\x65\x5f\xe8\x15\x69\xa8\x4e\xf2\xd2\xba\x00\xff\xd6\x96\xf9\x39\x5a\x41\x59\xfe\x46\x6d\x5d\xf9\xb4\x75\xe1\xb7\xdf\xb5\x14\x17\x13\x57\x1b\xe4\x86\x1b\xdc\xe4\xe6\x61\x42\x62\xde\x9a\xc0\x9f\x59\x0c\x1c\x7a\x32\xda\xe9\x9f\xce\x18\x69\xf5\x69\xd8\x9b\xde\x38\x2b\xde\x4b\xa1\x4d\x24\x8c\x1d\xae\x4e\xfe\xaa\x0a\x1b\xb5\xc8\x6f\x57\x4d\x1d\x9a\x79\x91\xd6\x84\x68\x3b\xb3\xe6\x54\xbb\xea\x65\x39\x96\x19\x2a\xab\x28\xed\xe0\x06\xd0\xdc\x83\x00\x18\x63\xee\x8d\x67\x8e\x0e\x06\x1d\x7d\x38\x30\x55\xe7\x12\x3d\x81\xe3\xe7\x12\xd4\x80\xf9\xee\x2e\xa1\x3f\x92\xe9\x43\x59\xd9\xe3\x0e\x9c\x5c\x93\xde\x88\x19\x58\xb5\xf6\x24\x7c\x2e\x6a\xc2\x23\x35\xd5\xe0\xd2\x34\xf3\x1c\x35\xba\xdc\x24\xb5\x1e\x5c\xee\x71\x60\x04\x41\xaa\xe4\x66\x7f\xba\xf5\x57\x06\xfe\xa9\x88\x3e\x30\x5f\x3f\x04\xe8\x17\x40\xab\xef\x71\x14\x58\xbb\x39\x75\x68\x75\xef\xa4\xaa\x01\xdb\x17\x3a\x8e\xd8\x4a\xc5\x69\xa0\xad\x5b\x3d\x1b\xb7\x95\x26\x0f\xdd\xde\x99\x9e\xf7\x64\xd2\x84\x6d\xe2\x33\x33\xa9\x38\x24\x1c\x75\xa6\x37\xe9\x18\x41\x6d\x06\x4f\xf2\x5a\x27\x75\x74\xde\x4b\xc3\xea\xa6\x9e\xd3\x40\x7d\x0b\xc6\x71\xc3\xe7\xc1\xab\x26\x3d\xda\xa8\xef\x9b\xf4\xf9\x66\xe0\xd2\x09\x89\xbc\xbe\x79\x18\x7b\xe9\xa4\xaf\x72\xff\xe6\x89\xcf\x6b\x73\x9b\x24\x15\x1a\x00\xe0\xeb\xb7\x9a\x91\x5f\xe8\xee\x48\xad\xcf\x1d\xf7\x37\x5c\x51\x55\x4c\x2e\x45\x53\x5c\xab\x0b\x00\xb5\xc7\x7b\x0b\xb8\x6e\x84\xab\xf1\xd3\xf3\x78\xd6\x74\x3b\xb5\x9e\x31\xc6\xae\x9a\x02\x7d\x88\xe6\x87\xd4\x33\xdb\xbc\x73\x4b\x60\x48\x62\x0e\xa9\xd8\xbf\x5a\xd2\x97\xf4\x11\xb1\x14\x61\x15\x66\xdc\xef\x69\x74\x9d\xa5\x09\xb2\xb6\x32\x74\x09\x0b\x75\x91\x51\x9d\x96\xad\xd8\x6d\xa3\xac\xc0\x27\x44\xa5\x62\xa7\xfe\xf2\x63\x0e\x5b\x97\xea\x34\x8a\x71\x57\xce\xfc\xf2\x66\xf7\xa8\x3b\xfd\x41\xdc\x50\xdf\x76\xd6\x72\xb5\x59\x4b\xd9\xa7\x13\x96\x52\x27\xf8\x34\xb8\xa6\xda\x73\x6a\xd7\xdf\x97\xd9\xf3\xa8\xed\xc2\xde\xce\x4b\x77\x75\xe5\x06\x7c\xeb\xde\x86\xf1\x4c\xb3\xe1\x86\x6f\x5b\x57\x37\xd2\x76\xa9\x35\xb6\xcc\xba\xed\x2e\x7f\x3d\xc3\x89\x94\x65\xbd\x28\x1b\xd8\xa5\xb4\xf5\xc5\x95\xda\x0a\x2a\xac\x9a\xaf\x0a\xbb\x70\xca\x32\x79\x87\xfe\x7c\xb4\xbe\xa0\x56\xa3\x8a\xd8\xd3\xd6\x6e\x1a\xf4\x9d\x2b\x1c\x23\x43\x5c\xd9\xf8\xe8\x26\x99\xe9\xed\x8e\xb5\xce\xe5\x07\xc6\x0c\xf1\xcf\x0c\xfe\x06\x6f\x49\xb6\xbf\xad\x25\x95\x66\x9f\xf0\x6e\x3a\x69\xa6\x85\x17\x43\xd4\xc6\xea\xf0\x71\x4d\x47\x10\x51\xbc\xe6\xb8\x8d\x6e\x32\x74\xe1\x20\x79\x1b\x0e\x9a\xb5\x98\x75\x24\xe0\xad\x0b\xc4\x64\x16\x06\x76\x95\x5c\xcf\x30\x2a\x27\x9c\xe9\xe3\x60\x72\x3e\x80\x93\xfd\x2d\xba\xa0\x75\xfd\x63\xeb\x8f\xbd\xca\xb0\x93\xfe\x66\x94\x54\x6f\x8e\x8e\x94\xa7\xe7\xf1\xd1\xed\x3b\x53\xdd\xc6\xd9\xce\x1f\x0d\x42\x1b\x14\xb3\x26\x66\xed\x40\xb4\x47\x4c\x27\x06\xbd\xeb\x2f\x8f\x95\xf8\x7e\xd9\x3c\x56\xd8\x49\xfe\xa9\x85\xdd\xcd\x3b\x06\xea\xba\xfb\x30\x5c\xd8\xfb\xd3\xab\xba\xb2\xef\x4d\xce\x06\x4a\xbb\xef\xd1\xd7\x63\x3f\xec\x47\x94\xf8\x3d\xdd\x23\x6a\xfc\x50\x3d\x27\xad\x27\x54\xf4\xc1\x02\x56\xcf\x38\x9f\x5e\xc0\x7a\x01\xae\x20\xdc\x77\xf3\x99\x25\x6c\xaf\x97\xff\x76\x0d\x3b\xc9\xad\x27\x56\xb1\x7d\xa7\xfe\xf2\x65\xac\x5e\x29\x1c\x2c\x63\x4e\xc2\x12\xf7\x70\xe5\x1a\x1d\xd8\x67\xd7\xae\xfd\xf0\x3e\xb9\x78\xf5\xad\x3b\x5a\xbd\x9a\x28\x3c\xa3\x7c\x3d\x86\x8f\xbf\x48\xfd\x3a\x39\x9b\x4f\xa9\x60\xc3\x83\xff\x4f\x28\x61\x7b\x05\xe2\x58\x0d\xd3\x7e\xfb\xe5\x09\x45\xac\xfa\xfb\x9f\x00\x00\x00\xff\xff\xcb\x06\x9f\x91\x57\x32\x00\x00") +var _templateBuilderQueryTmpl = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xcc\x5a\x6f\x8f\xdb\x36\xd2\x7f\x6d\x7f\x8a\xa9\xb1\x5d\xd8\x81\x23\x27\x79\xf7\xec\x83\x3d\x60\x9b\x4d\x0e\x06\x8a\xf4\x9a\x04\x68\x81\x20\x68\xb9\xd2\xc8\x66\x23\x93\x2a\x49\x79\x77\xe1\xf3\x77\x3f\x70\x48\x49\xd4\x1f\xaf\xe5\xdd\xbd\x5e\x5f\xd9\x92\xc8\x21\x67\xe6\x37\xbf\x21\x39\xdc\xed\x16\x2f\xc6\x6f\x65\x7e\xaf\xf8\x6a\x6d\xe0\xcd\xab\xd7\xff\xf7\x32\x57\xa8\x51\x18\x78\xcf\x62\xbc\x91\xf2\x1b\x2c\x45\x1c\xc1\x55\x96\x01\x35\xd2\x60\xbf\xab\x2d\x26\xd1\xf8\xf3\x9a\x6b\xd0\xb2\x50\x31\x42\x2c\x13\x04\xae\x21\xe3\x31\x0a\x8d\x09\x14\x22\x41\x05\x66\x8d\x70\x95\xb3\x78\x8d\xf0\x26\x7a\x55\x7e\x85\x54\x16\x22\x19\x73\x41\xdf\x7f\x5c\xbe\x7d\xf7\xe1\xd3\x3b\x48\x79\x86\xe0\xdf\x29\x29\x0d\x24\x5c\x61\x6c\xa4\xba\x07\x99\x82\x09\x06\x33\x0a\x31\x1a\xbf\x58\xec\xf7\xe3\xf1\x6e\x07\x09\xa6\x5c\x20\x4c\xfe\x2c\x50\xdd\x4f\x60\xbf\xb7\x2f\xcf\xf2\x6f\x2b\xb8\xb8\x84\x1b\xa6\x11\xce\xa2\xb7\x52\xa4\x7c\x15\xfd\x8b\xc5\xdf\xd8\x0a\xc1\xf7\x34\xb8\xc9\x33\x66\x10\x26\x6b\x64\x09\xaa\x09\x9c\x75\x3f\xf1\x4d\x2e\x95\x09\x3e\x9d\xdd\x14\x3c\xb3\xda\x5d\x5c\xc2\x59\xf4\xb3\x1d\xf5\x03\xdb\x60\x39\xb0\xc2\x18\xf9\xd6\x7d\xae\xfe\x57\x7d\xac\x8c\xc5\x02\x42\x31\xfb\xbd\xb5\x9c\x55\xbb\x7c\x93\x4a\x05\xa4\x0d\x17\x2b\xdb\x34\x67\x3a\x66\x19\x9c\x45\x7e\x1c\x40\x61\xb8\xe1\xa8\xa3\xb1\xb9\xcf\xb1\x2d\x4d\x1b\x55\xc4\x06\x76\xe3\x51\x4c\x6a\x8f\x47\x19\xdf\x70\x33\x1a\xbd\xe0\xc2\x8c\x47\x32\x4d\x35\xd6\x4f\x2a\x41\x35\x1a\x7d\xf9\xfa\x93\xfd\x33\x1e\x15\x82\xff\x59\xa0\x7d\xa1\x8d\xe2\x62\x35\x1e\xe5\x0a\x13\x1e\x33\x83\x1a\x46\x5f\xbe\x56\x4f\x91\x1d\xb5\x9c\xd1\x78\xb4\xdb\xbd\x84\x5b\x6e\xd6\x70\x16\xbd\x4b\x56\xa8\xe9\xe5\x68\xb1\x00\x64\x2b\x54\x2f\x33\xc9\x12\xab\x0d\xda\x6f\xd1\x78\x44\xed\x15\x13\x2b\x84\x33\xb4\xa6\x8a\x5c\x87\x91\x95\x11\xa8\x8c\x95\xce\x2f\xec\x78\x18\x7d\xbe\xcf\xb1\x69\x74\x27\x0b\x45\x52\xcd\x23\xfc\xbf\x78\x01\x57\x49\xc2\x0d\x97\x82\x65\x90\x72\xcc\x12\x0d\x46\x02\x4b\x12\xfb\x13\x98\x3d\x02\x82\x14\xf5\x3a\x33\x9b\x3c\xb3\xd3\xca\x15\x17\x26\x85\x49\xc2\x59\x86\xb1\x59\x7c\xaf\x17\xe4\x99\x85\x93\x34\x81\xb3\xe8\x93\x91\xca\x83\x8a\xfa\xf2\x14\xd6\x4c\x7f\x2e\x01\xe4\x44\x55\xf3\xbc\x33\xcd\x0f\x51\x67\xd6\x8b\x05\x70\x61\x50\x6d\x30\xe1\xb6\x1d\x8d\x17\xd9\x36\x8d\xc1\x20\x7c\x8e\x7e\xa8\xf1\xe5\x10\xf6\xcb\x1a\x15\x5a\x2d\x35\x30\x10\x78\x0b\x95\xe3\x08\x5e\xa1\xde\xe3\xb4\x10\x31\x4c\x1b\xe0\x2d\x0d\x5e\xc3\x6a\xe6\x44\x4e\x73\x0d\x51\x14\xf5\xc3\x60\xd6\xee\x64\x41\x18\xca\xdd\xef\xa3\x00\x4e\x97\xc0\xf2\x1c\x45\xd2\x1e\x3a\x68\x33\x87\x5c\x47\x51\x34\x1b\x8f\x14\x9a\x42\x09\x68\x35\xf5\xda\xfe\x68\x01\x5e\x6a\x4b\x68\x07\x6d\x30\x2f\x3d\xec\x4d\x38\x50\x4f\x12\x36\x75\x52\xb8\x30\x47\x95\xb2\x33\x76\xad\x2f\xe1\x9c\xfe\x1c\x99\xed\x4f\x14\x81\x7e\xba\x02\x5c\x40\x3e\x61\xc2\x4e\xde\xd4\xcb\x19\x3a\x65\xdf\xfc\x12\xce\xdd\xbf\x63\x93\xb6\xfc\x50\xcf\x99\x9e\x9e\x30\x65\xdb\x7f\x2a\x2d\x94\xe8\xef\xb0\x19\xd3\xa0\x07\x51\x43\x9f\xe7\x20\x8f\xe1\xc5\xe6\x3d\x97\x50\x28\x6d\xad\x99\x06\xcd\x37\x3c\x63\x8a\x9b\x7b\x47\x62\x96\xa6\x48\x23\x8e\xda\x26\xa5\x38\xe3\x28\x4c\x44\x11\x4b\x2c\xb1\xdb\x95\xec\xf5\xdb\xdc\x33\x58\x48\x7c\xc4\x55\xc9\x0a\x7f\x0b\x72\x05\x51\x09\x4c\x6b\x66\x23\x2a\xb3\x91\x33\x83\xc9\xcf\x55\xf2\xb2\xf1\x4f\x4f\xbd\x2c\x18\xaf\x19\x17\x2e\x5b\xc4\x85\x52\x36\x55\x93\xe1\x41\xba\xcc\xe9\x48\xb2\xca\x13\xc9\x0a\xa3\xf1\x68\xa0\x4f\x0e\x8e\x3a\xf5\xde\x69\x68\xe4\x5c\x34\x72\xa3\x5f\x5c\xc2\x79\x4f\x8b\x9d\x4b\x40\x17\x6d\x2f\x44\xee\x7d\xc9\x89\xce\xe4\x77\xc6\x32\xe0\x19\x4c\x3e\xfa\x86\x93\xa0\xcf\xc4\xda\x76\x62\x2d\xfd\xd2\xe5\x09\x2b\xf1\x28\x49\xe7\xcc\xac\xdb\x14\xfd\x10\x0d\x57\x3c\xec\x06\xf1\x10\x22\x59\xe3\x11\x39\xdd\x93\xb4\x0d\x88\xf7\x5c\x69\x03\xae\x8d\xf3\x48\x4a\x6f\x42\x42\x74\x99\xfa\xbe\x5c\xd7\xb8\x18\x81\x8f\xbe\xcf\x8b\x77\x4a\x7d\x90\xe6\xbd\x5d\x0e\xc1\xed\x1a\x05\x08\x69\xbb\x67\xf2\xd6\xae\x19\x2a\x21\xb7\x4c\xbb\x35\xd3\xe0\xf0\xa2\xb9\x4d\x63\x73\x07\xb1\x14\x06\xef\x8c\x5d\x01\xd9\xdf\x19\x4c\x5f\x84\x13\x9c\x03\x2a\x25\xd5\xcc\x87\x5b\x9e\x15\x8a\x56\x1a\x1f\x6b\xe9\xd4\xc4\x9a\xb9\xed\x44\xc7\x93\xaf\x67\xd1\x55\x96\xd9\xb1\x66\xe3\x11\x4f\xa9\xf1\x77\x97\x20\x78\x46\xf8\xf0\x36\x14\x3c\x23\x39\xd6\x8c\xb6\x55\x86\x62\x7a\x60\xbc\x19\x5c\x5e\xc2\xab\x4e\xe7\xf3\xc0\x58\x3b\x67\xe4\x7a\x39\x17\xfd\xc8\x6e\x30\xdb\x93\xf4\x3a\xf0\xfb\xa4\x7f\x79\xf5\x75\x6e\x05\x8e\x03\x27\xfe\xea\x96\xae\xdf\xd0\x3d\xce\xe1\xa6\x30\x90\x33\xc1\x63\x6d\xb3\x39\x13\xce\x48\x20\xe3\xb8\x50\xfa\x34\x27\xfc\xda\xef\x85\x86\x13\x4a\xae\x1b\x64\xf5\xca\xb5\x1d\x73\x9f\x9f\xc3\x77\x4b\x5d\xda\x68\x8a\xca\xb9\x75\x44\x9a\xd0\x63\xcb\x3e\x8d\x01\x43\x83\x2c\xaf\x8f\xe1\x9a\x27\xa7\x60\x9a\x27\x8f\xc5\xf0\xf2\xfa\x00\x8a\x79\xe2\x26\xb4\xbc\x26\x1e\xad\x2c\x56\xc3\x79\xcb\x14\xf0\x44\xc3\x97\xaf\xad\x86\x64\x37\x9e\x68\xd7\xe1\x01\x5c\x2f\xaf\x35\x19\xfa\xff\xfb\x41\x1d\x62\x99\x27\x3a\xc0\xad\x93\x3b\x0c\xb1\xa1\x30\xef\x1a\x9e\xe8\x5e\x98\x2e\xaf\x9b\x40\x5d\x5e\x3f\x2f\x54\x0f\x19\xbb\x65\x3f\xab\x22\x4f\x1e\x06\xa8\x13\xf5\x44\x88\xf2\xa4\x5c\x7b\x88\xec\xbe\x81\x48\x69\x5f\x1c\x23\xda\x79\xd5\xa5\x32\x0b\x4f\x41\x48\x03\x78\xc7\x62\x93\xd9\xa4\x89\x65\x47\x8b\x4f\xd7\x1c\x87\x43\xd4\xce\xeb\xaf\x61\xd9\x37\xa7\xb3\xac\xbe\xe5\x26\x5e\x3f\xcc\xb4\x76\x8f\x68\x77\xc8\xaf\x2f\x6a\x21\xc7\x88\xd3\xf5\x78\x75\xf1\x28\x7e\x4e\x30\x65\x45\x66\x0e\x74\xfe\xc4\xc5\xaa\xc8\x98\x7a\x88\xdf\x6b\x44\xd4\xb4\x6d\x9f\x9e\x2b\x14\x48\xf2\x73\x93\x76\x09\x94\x5e\xe7\x9d\xc4\xcf\x56\x52\x8b\x9e\xbb\xc1\xd0\x62\xe7\x61\x81\xe0\x49\xfa\x51\x41\xf0\xbf\xa3\xe9\x37\xc3\x68\x3a\x08\x06\xa2\xea\x06\xf0\x79\x02\x97\x9e\x74\x43\x74\x9f\xc2\xe2\x01\xae\x1b\xdd\x86\x20\xba\x9c\x67\x80\xec\x80\xe9\x9d\x79\x9f\x15\xdd\xcf\xc3\xf3\xb5\xdf\x4f\x40\x75\x45\xe9\x57\x59\x06\x78\x87\x71\x61\x50\xd7\x48\x05\x26\x92\x1a\xac\x90\x71\x6d\x40\xa6\x0d\x4a\xf2\x18\x1f\xac\xb1\xa7\xcd\x1e\x6c\x7e\xf9\x7a\x90\xa4\xfb\x37\x8f\x51\xeb\x08\xa6\x62\xe4\x4a\xa5\x9a\x92\xae\xb2\xec\xb9\x7c\x66\xe5\xf6\xab\xd0\xd2\xe0\x31\xe9\xe5\xa1\xac\x72\x90\x98\xfa\x46\xf0\x46\x58\x5e\xeb\x93\xfc\x1a\x92\xd6\x70\x93\xf8\x90\xef\x75\x6a\x1f\xdf\x0c\xe2\x9a\x03\x16\xfa\x84\x76\x53\x39\x6d\xc7\xef\x7b\x8e\x59\xb2\xbc\x9e\x45\x9f\x62\x26\xec\x64\xe6\x70\x6e\xa9\xe5\x00\x0b\x35\x73\x73\xbd\xd0\x0b\x57\x79\xcb\x6b\x5d\x03\x68\x79\xad\x9f\x0b\x40\x56\xee\x21\x00\xf5\xc6\xbc\x3e\x08\x97\x92\x6b\x4f\x89\x78\xed\xd5\x7b\x2b\x0b\xd1\xdc\x30\xc7\xf4\x86\x8e\xf7\x11\x56\x7c\x8b\xe2\xc4\x63\x24\x12\x79\x28\xf5\x08\x73\x7a\x48\x57\x02\x67\xe1\xa4\x6b\xaf\xd0\xe3\x73\xf9\xc5\xc9\xee\x9f\x3e\x17\xfe\x04\xbf\xf0\x6a\xf4\x79\x23\x98\xed\x60\x7f\x90\x44\xaf\xdc\xbb\x3b\x1e\x1e\x61\xa8\x02\xad\x3a\x75\xd4\xae\x99\x06\xcc\x70\x83\xc2\xe8\x72\x45\xb1\x52\x2c\x5f\x0f\x56\x91\x46\x38\xe0\xa0\x1b\x29\xb3\xd3\x3d\x54\x49\x9c\x85\x4a\xd4\x1e\xa2\xc7\xe7\xf2\x90\x93\xdd\x3f\x7f\x3b\x7d\x3b\x6d\x74\x03\x1e\x70\x51\x30\xdd\xc1\x2e\x22\x89\x25\xfe\x32\xbb\x36\xab\xa9\x33\x29\xf2\xcc\x1d\xe3\xcb\xd0\x53\x7e\xd2\x73\xe0\x22\xce\x0a\x2a\xb5\xb0\x2c\x03\xa6\xb5\x8c\x39\x33\x98\xd0\x59\xad\x8e\x60\x69\x20\x66\x02\x6e\xd0\x0a\x2f\x34\x52\x15\x24\x57\x98\x33\x65\xe3\x71\xb3\x91\xa2\x29\x52\x13\x77\x17\x1a\xed\x68\x1b\x48\x78\x9a\xa2\x42\x61\x57\x8d\x2c\x35\xbe\xc8\x17\xd3\x2c\xb9\x86\x0d\x4b\x70\x38\xfe\x6d\xaf\x69\xef\xd1\xaf\xb7\xc4\x79\xf3\x8b\x35\x59\x79\xa4\xd8\x39\x1d\x76\x1f\xe6\xe3\x91\x2b\x77\x5d\xc0\xa8\xff\x94\xde\xb6\x70\x27\xde\x3d\x42\xdc\x07\x6a\xa2\x12\x54\x56\x88\x3f\x6d\xf6\x15\xb2\xdd\x7e\xde\xf1\x31\x35\x8d\xa2\x68\x66\xfb\xb9\x02\xda\x05\xd4\xfd\x5c\x21\xad\xaf\xa3\x6b\x5b\xf6\xac\xab\x1e\x17\x50\x75\xee\x2f\xb4\xf4\x09\xab\xbb\x97\x02\x17\x8b\xd2\x31\x7d\xf5\xa4\x76\x41\xa9\x7b\x4a\xdb\x6a\x10\x79\x7f\xcd\xfd\x36\xac\x5d\xc2\xeb\x1c\x80\x87\x85\xd2\xde\xca\xdd\x62\x01\xf0\xcb\xa1\x82\x9f\xc1\x2c\x0b\x96\x10\x2f\x4b\x69\x46\x06\x35\x45\xd7\x40\xc8\x84\x56\x1b\xcc\x80\x83\xb1\x10\x18\x1b\xc2\x36\x0d\x62\xdb\x4c\x1a\x47\xe3\x13\x77\x36\x0e\x9f\xed\xfe\x29\xf7\xd5\x41\xa6\x56\x85\xe3\xba\x32\x30\x1c\xa6\x0a\x85\xdd\x50\x2b\xe3\xef\xb4\x33\xf6\x43\xda\x4e\x65\x6e\xa8\xb0\x66\xe5\xb8\xc3\x03\x0c\xfa\xf5\xc6\x48\xfb\xec\xfd\xa4\x73\xf7\x54\x2a\xf8\x6d\x6e\x75\xa7\x42\x35\xb9\x91\xe6\x60\x05\x8f\x64\x6e\xa6\x24\x7d\x36\x1e\x8d\xf6\xe3\x6e\x24\x1d\x2c\xd3\x5e\x96\xc7\xe5\x87\x0a\x30\x74\x8e\x5e\x55\x3b\xa9\x64\xbe\x52\xb2\xc8\x7f\x08\x2a\x25\x8d\x7a\xf7\xbf\xab\xf3\xfd\xef\xf5\x3f\xa9\xa5\x2b\x94\x58\x02\xf3\xcf\x95\xbf\x48\x12\x6c\x51\x19\x1e\xa3\x86\x1b\xb7\xb3\x95\x0a\x36\x52\xa1\xaf\xfe\x2e\x62\x99\x15\x1b\xa1\x23\x5a\x72\x19\xcb\x5a\x32\x35\x28\x9c\x10\xaa\x45\xb0\xd5\x4a\xe1\x8a\x6a\xa5\x85\x88\x2d\x3a\xf4\x9c\xb2\x0b\x59\xf4\x0f\xc9\x05\x4c\xbf\xe1\xbd\xae\x1b\xce\x60\x32\x87\x09\xed\x4b\xaa\x8a\x46\x86\x02\xce\xdc\x3a\x51\xbb\xdb\x01\x2f\xe1\x2c\xb5\x0a\x72\x91\xe0\x5d\xfd\xed\x95\xfd\xba\x58\xb8\x64\xc6\x36\x79\x86\x17\xee\x91\x16\xac\x5b\x20\x0a\x71\x25\xfd\xc5\xc2\xf9\x22\x8d\x3e\xd1\x2b\x92\x50\x56\x83\xd3\x6a\x15\xf7\x7b\xd8\xe6\x33\x5b\xc1\x7e\xff\x3b\xf5\x75\x6b\x30\xbb\xb8\xf8\xfd\x0f\x2d\xc5\xc5\xc4\x2d\x30\xe4\x86\x1b\xdc\xe4\xe6\x7e\x42\xcd\xfc\x6c\x46\xbe\xec\xd5\x73\x05\xc1\x05\xf2\x74\x16\x91\x54\xef\x86\xce\x1a\xd9\xcd\xe2\xad\x14\xda\x30\x61\x2c\x90\x5d\xfb\xab\xd2\x6c\xd4\x23\xff\xb6\xaa\x17\x33\x33\xdf\x24\x58\x55\x6f\x67\x76\x3a\x01\x68\x06\xc6\x5a\x39\x2b\x72\x3b\x38\x16\x9e\x97\x57\x00\xa2\x28\x72\x6f\x7c\x68\x35\x30\xe8\xe2\xcb\x81\xa9\x0c\xaf\x56\x83\xe3\x21\x46\x1d\x22\x3f\xdc\x25\xb4\xd3\x01\x7d\xd8\x97\xf3\x71\x35\x4b\xd7\xa5\x45\xbb\x3d\x47\x1f\xad\x16\xde\x17\x55\xd6\x24\x31\x63\x1f\x5c\x9a\xb6\x2f\x83\xa2\xcb\xed\x74\xaa\xe0\x72\x8f\x3d\x11\x04\xa9\x92\x9b\xee\x9a\xfd\xef\x0c\xfc\x53\x11\x7d\x60\xd3\x77\x08\xd0\xcf\x80\x56\x3f\xe2\x20\xb0\x36\x7d\xea\xd0\xea\xde\x49\x55\x01\xb6\xdd\xe8\x38\x62\x4b\x11\xa7\x81\xb6\xea\xf5\x64\xdc\x96\x92\x3c\x74\x5b\x65\x61\xaf\xc9\xa4\x36\xdb\xc4\x7b\x66\x52\x72\xc8\x78\x50\x59\xb8\x7b\x69\xa7\xbf\x1c\x1c\x94\x7b\xe9\xca\x00\x85\xd5\x4d\xbd\x00\xa8\x6e\xa0\x39\x6e\xf8\xd8\x7b\xf1\xab\x45\x1b\xd5\xed\xaf\x36\xdf\xf4\x5c\x01\xa3\x26\x2f\x6f\xee\x87\x5e\x01\x6b\x8b\xec\xde\x03\xf3\x7e\xad\xef\x76\xa5\x42\x03\x00\x7c\xf9\x5a\x31\xf2\xd3\xee\x1f\x55\xc7\x40\x95\x3c\x77\x63\xa4\xe6\x8a\x32\x63\x72\x29\xea\xe4\x5a\xde\x21\xa9\x34\xee\x9c\x02\x34\x2d\x5c\xc6\x4f\x4b\xe3\x59\x3d\xec\xd4\x6a\x16\x45\xd1\x55\x9d\xa0\x0f\xd1\x7c\x9f\xf8\xc8\x76\x6f\x5c\x34\xe9\x6b\x31\x87\x54\x74\x6f\x27\xb5\x5b\x7a\x8b\x58\x8a\xb0\x02\x33\xee\x0f\xc6\x9a\xca\xd2\x2e\x4b\xdb\x36\x74\x01\x12\x75\x91\x51\x9e\x96\x81\xed\xb6\x2c\x2b\xf0\x11\x56\x29\xd9\xa9\xbd\x87\x9d\xc3\xd6\xb9\x3a\x65\x31\xee\xf6\x33\xbf\x47\xde\x3d\xa8\x4e\x3b\x88\x6b\xea\xdb\xce\x02\x55\xeb\x0d\xb9\x7d\x3a\x61\x3f\x7e\x82\x4e\xbd\x1b\xf3\x8e\x52\xbb\xf6\xe1\x5e\x47\xa3\x50\x85\xce\xf1\x5d\x73\x8b\xee\x02\x3e\xb8\xfa\x63\x3c\xd3\x6c\xb8\xe1\xdb\x60\xf3\x93\x86\xa9\xd6\xd8\x34\xeb\xce\x4c\xfd\xb6\xc7\x35\xd9\xef\xab\x9d\x7d\xcf\x51\xb7\xcd\x2f\x2e\xd5\x96\x50\x89\xca\xf5\xaa\xb0\xbb\xef\x2c\x93\xb7\xe8\x8b\xec\xd5\x75\xd1\x0a\x55\xc4\x9e\x36\x77\x53\xd0\x37\x76\x28\x03\x4d\x5c\xce\xf1\xc1\x93\x56\xd3\x3a\x62\x0d\x2e\x77\xf4\xc4\x0c\xf1\xcf\x0c\xfe\x01\xaf\xdd\x3e\xa3\x75\x36\x2a\x95\x8e\x3e\xe0\xed\x74\x52\x2f\x0b\x2f\xfa\xa8\x2d\xaa\xcc\xc7\x35\xd5\xb1\x58\xbc\xe6\xb8\x65\x37\x19\x3a\x73\x50\x7b\x6b\x0e\x5a\xb5\x98\x35\x13\xf0\xda\x19\x62\x52\xee\x68\xca\x15\x46\xa9\x84\x9b\xfa\x30\x98\x9c\xf7\xe0\xa4\x7b\xce\x3b\x0a\xee\x10\x6d\x7d\xed\x74\x3f\x6e\xb8\xbf\x8e\x92\xf2\xcd\xd1\x48\x79\xbc\x1f\x1f\x3c\x03\x36\xe5\xa6\x72\x3b\x7f\xd0\x08\x21\x28\x66\xb5\xcd\x42\x43\x84\x11\xd3\xb0\x41\xeb\x0e\xd5\x43\x29\xbe\x9d\x36\x8f\x25\x76\x6a\xff\xd8\xc4\xee\xd6\x1d\x3d\x79\xdd\x7d\xe8\x4f\xec\xed\xe5\x55\x95\xd9\x3b\x8b\xb3\x9e\xd4\xee\x47\xf4\xf9\xd8\x87\xfd\x80\x14\xdf\x91\x3d\x20\xc7\xf7\xe5\x73\x92\x7a\x42\x46\xef\x4d\x60\xd5\x8a\xf3\xf1\x09\xac\x65\xe0\x12\xc2\x6d\x35\x9f\x98\xc2\x3a\xa3\xfc\xb7\x73\xd8\x49\x6a\x3d\x32\x8b\x75\x95\xfa\xdb\xa7\xb1\x6a\xa7\x70\x30\x8d\xb9\x16\x96\xb8\xfb\x33\xd7\x60\xc3\x3e\x39\x77\x75\xcd\xfb\xe8\xe4\xd5\x9e\xdd\xd1\xec\x55\x5b\xe1\x09\xe9\xeb\x21\x7c\xfc\x4d\xf2\xd7\xc9\xde\x7c\x4c\x06\xeb\x0f\xfe\xbf\x20\x85\x75\x12\xc4\xb1\x1c\xa6\xfd\xf1\xcb\x23\x92\x58\xf9\xf7\x3f\x01\x00\x00\xff\xff\x4b\xc7\x04\x70\xd3\x35\x00\x00") func templateBuilderQueryTmplBytes() ([]byte, error) { return bindataRead( @@ -196,7 +196,7 @@ func templateBuilderQueryTmpl() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "template/builder/query.tmpl", size: 12887, mode: os.FileMode(420), modTime: time.Unix(1, 0)} + info := bindataFileInfo{name: "template/builder/query.tmpl", size: 13779, mode: os.FileMode(420), modTime: time.Unix(1, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -241,7 +241,7 @@ func templateBuilderUpdateTmpl() (*asset, error) { return a, nil } -var _templateClientTmpl = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xb4\x58\x5d\x6f\xe3\xba\x11\x7d\xb6\x7e\xc5\x54\xf0\xde\x4a\x81\x23\x6d\xf7\xad\x2e\xf6\x61\x91\x6c\x2f\x02\xb4\xc9\x6d\x37\x45\xef\xdb\x05\x4d\x8e\x64\x36\x32\xa9\xa5\xa8\xc4\x81\x9a\xff\x5e\x0c\x49\x7d\xd9\x8e\x93\x2e\xda\x97\xc4\x22\xa9\x99\x33\x33\x87\xc3\x23\x76\x5d\x7e\x11\x5d\xe9\xfa\xd9\xc8\x72\x6b\xe1\xd3\xc7\x3f\xfc\xf1\xb2\x36\xd8\xa0\xb2\xf0\x67\xc6\x71\xa3\xf5\x03\xdc\x28\x9e\xc1\x97\xaa\x02\xb7\xa8\x01\x9a\x37\x8f\x28\xb2\xe8\x7e\x2b\x1b\x68\x74\x6b\x38\x02\xd7\x02\x41\x36\x50\x49\x8e\xaa\x41\x01\xad\x12\x68\xc0\x6e\x11\xbe\xd4\x8c\x6f\x11\x3e\x65\x1f\xfb\x59\x28\x74\xab\x44\x24\x95\x9b\xff\xcb\xcd\xd5\xd7\xdb\x6f\x5f\xa1\x90\x15\x42\x18\x33\x5a\x5b\x10\xd2\x20\xb7\xda\x3c\x83\x2e\xc0\x4e\x9c\x59\x83\x98\x45\x17\xf9\xcb\x4b\x14\x75\x1d\x08\x2c\xa4\x42\x88\x79\x25\x51\xd9\x18\xc2\xf0\xb2\x7e\x28\x61\xfd\x19\x36\xac\x41\x58\x66\x57\x5a\x15\xb2\xcc\x7e\x61\xfc\x81\x95\x48\x8b\xba\x0e\x2c\xee\xea\x8a\x59\x84\x78\x8b\x4c\xa0\x89\x61\xe9\x5e\x97\xbb\x5a\x1b\x0b\x49\xb4\x88\x2b\x5d\xc6\x51\xb4\x88\xc9\xe2\xb1\x91\x7c\x27\x4b\xc3\x2c\xc6\xd1\xa2\xeb\xc0\x30\x55\x22\x2c\x7f\x5b\xc1\x52\x91\xeb\x65\x76\xab\x05\x36\x64\x72\xe1\x2d\xa8\x13\x26\xfc\xf8\x38\xe0\x6c\x5d\x02\x2a\xe1\xb0\x2c\xe2\x52\xda\x6d\xbb\xc9\xb8\xde\xe5\x45\x28\x8b\x54\xbc\xdd\x30\xab\x4d\x8e\xca\xe6\x42\xb2\x0a\xb9\x3d\x02\x11\xc2\x70\x48\xbe\x59\x6d\x58\x89\xd9\x8d\x1b\x6b\xe0\x72\x04\x15\x96\x05\xcf\xce\x31\xcd\xa6\x51\x94\xe7\x70\xe5\xb2\x4a\xb5\xa5\xc2\xf8\x1c\x83\xdd\x32\x0b\x5b\x5d\x89\x06\x58\x55\x01\x0d\x6d\x5a\x59\x09\x34\x4d\x16\xd9\xe7\x1a\xfb\xd7\x1a\x6b\x5a\x6e\xa1\x8b\x16\xdc\xc5\xed\x43\x93\x05\x01\x6a\x6b\x72\xfb\x57\x9f\x40\x9f\xa3\x3c\x87\x6f\x7c\x8b\x3b\x76\xe0\xaf\xd0\x06\xb8\x41\x66\xa5\x2a\x57\xe0\x73\x2e\x55\x09\x4c\x09\x10\x46\xd7\x35\x3d\x34\xee\xcd\x2c\x5a\x2c\x82\x8d\x8b\x50\x9c\xcc\x3f\xcf\xd2\x7a\xae\x5c\x97\x3d\x16\x5f\x99\x5b\xb6\x23\x78\x27\x20\x49\x65\xd1\x30\xee\xa0\x3c\x49\xbb\x75\xf3\xf3\x97\xc6\xb4\x2c\x16\xf3\x99\x8b\xd9\xa3\xcf\xd7\x90\xff\x80\x70\xe4\xa7\xf7\x9a\x17\x12\x2b\xd1\xe4\x4c\x08\x69\xa5\x56\xac\x0a\x8c\x7d\x71\xb5\xba\xc5\xa7\x90\x77\x97\x2c\x6c\x80\x81\xc2\xa7\x1e\xb2\x2f\x41\x6b\x50\x8c\x68\x4b\xf9\x88\x0a\x74\x4d\xd6\x9a\x2c\x2a\x5a\xc5\x47\x33\x89\xae\x6d\x03\x59\x96\xdd\xb9\xf9\x14\x2e\x82\x79\xaa\x27\x65\xcc\x5b\xec\x2a\x5d\xae\xa1\xd2\x65\xf6\x8b\x91\xca\x56\xea\x25\x5a\xf0\x2c\xd8\x74\x36\xb2\x2c\x4b\xa3\x85\x41\xdb\x1a\x05\x3f\x79\x23\x5d\xb4\x08\xa4\x58\x03\x5f\xb9\xf4\x9c\x61\x46\xa8\xe9\x1a\xfa\x9a\xde\xe2\x93\x1f\x4a\x78\x26\x8c\x7c\x44\x93\xf6\x46\xfa\x04\xbe\x5d\xe3\x79\x49\xd6\x14\xf9\x89\xaa\x24\x3c\x98\x1e\xb6\xc6\xa2\xcf\xf8\x5d\xed\xb2\x87\x8a\x52\xcd\xb5\x52\xc8\x29\x6a\xb0\xda\x65\x57\x30\xcb\x5c\xff\x69\x6a\xe4\xb2\x90\x28\x60\xf3\xec\x67\x1c\x64\x50\xe4\x87\x58\xcc\xc8\x9a\x1f\xbc\x0c\x8b\xb9\x7b\xbd\x6f\x7a\xb4\x72\xe5\x96\xfa\x34\x1e\xd4\x96\x59\x4b\x6d\x56\x90\x67\x69\x33\x8f\xcd\x53\x04\x6a\x66\xd8\x0e\x2d\x9a\x06\x38\x53\xb0\x41\x60\x42\xa0\xf0\xbb\x2a\x70\x82\x28\x3c\xb2\x3b\x10\x81\xa2\x4b\x3c\xa8\x5b\xe7\x9e\x00\x7d\x73\x78\x5c\x82\x1a\x6b\xdc\x86\x0c\xa5\x9e\x32\x25\x09\x54\x59\x01\x1a\xa3\x4d\x4a\x94\x69\x9e\xa4\xe5\x5b\x18\x0d\x3a\x1e\x51\x7a\xba\x0e\xfe\xa5\xa5\x9a\xb4\xa9\x6b\xdf\xd2\x1a\x88\x57\x40\x2d\x7d\x1d\x6a\xbb\xb4\xbb\xba\xa2\x42\xd6\x44\xb5\x02\xe2\xd0\xfb\xf2\x0f\x4d\x1e\xf6\x08\x95\x23\x1e\x4d\x85\x52\xd3\xcb\xfb\x61\x3f\x79\x33\x99\x9f\x13\x58\xb0\xb6\xb2\xe4\x22\x50\x54\xc9\x6a\x05\xc5\xce\x66\x5f\x09\x7c\x91\xc4\xad\x6a\x3c\x29\x51\x04\xfc\x6b\xf8\xf0\x3d\x5e\x4d\x82\x49\x47\x56\xdc\xef\x0f\x8a\x64\x0d\x53\x0d\xe3\xa1\x1e\xb3\x1c\x27\xbc\xdf\x55\x29\xdc\xef\x13\x6e\xf7\x54\x13\x8b\x7b\x4b\x47\x05\xfd\xa7\x64\xde\xef\xa7\x89\x94\x05\xfc\xb6\x02\xfd\xe0\xb6\x60\x60\x7f\x96\x5c\xd8\xfd\xb5\xdf\x08\x7f\xa2\xb9\xee\x4c\x38\xfd\xf1\x48\x9c\xe7\x4c\x29\x4d\x9d\x9a\x19\x0b\x6c\x0a\xd5\x75\x09\xa9\xe6\x83\xb1\x8b\x73\x61\x3d\x20\x42\xa0\xf0\xc9\x03\x5f\x0d\x60\x52\x87\x91\xe6\x7f\xf7\x99\xbc\xbf\x1b\x8c\x43\xe1\x3a\xfb\xd4\xe7\x1a\x3e\x3c\xc6\xce\x9f\x77\xce\x8b\x72\xd2\x7d\xfa\x7a\x10\x00\xd7\x89\x78\x56\xe9\x72\x05\x02\x37\xad\x7b\x72\x3f\x5e\xc6\xfe\x73\xbf\x9f\xf5\x9e\xa2\x5c\xfd\x2f\x7b\x45\x51\x1e\x77\x8b\x15\x45\x1d\xc8\x71\x4d\x68\x0e\xf8\xe1\x10\x5e\x06\x5e\xc0\x8d\xfd\x7d\x03\x6d\xe3\x37\x73\x89\x16\x1e\xd1\x6c\x74\x83\x14\x5d\x49\xc9\xd1\x0a\x86\x1e\xa1\x6b\xa4\xc3\xd0\x75\xef\x3c\x8f\xf2\x7c\x11\xcc\x38\x3f\x49\x4a\xa3\x0e\x4d\x22\x95\xc0\xfd\x10\xd4\xc7\xb4\x07\xee\x57\xfc\xad\x45\xf3\xdc\x2f\xbf\xd2\x2d\x85\x62\xf7\x29\xd9\x3c\xe2\x69\x30\x3d\x3d\x0e\x64\xd1\x27\x7a\x5a\x6b\x7e\xa6\x5c\x61\xe3\x06\x9c\x3d\x73\x56\xbe\x7a\xe9\xc9\x52\x5a\xd3\xe2\xcb\xd9\x73\xa4\xaf\xe5\x0f\x9e\x24\x45\xf9\xff\x3f\x4b\x4e\xf1\x23\x50\xe3\xaa\xa2\x2a\x73\xfa\xdb\xcc\xcf\x8f\xc9\xd1\x42\x47\x40\x6d\xf0\x11\x95\x6d\x1c\x79\xbe\xb7\x68\x24\x36\x50\x18\xbd\x1b\x36\xd0\x89\xee\xe2\xac\x27\xa9\xef\x23\x54\xa5\xbe\x48\x7d\x07\x09\x0b\x22\xaf\x9f\xcf\x45\x4b\x81\x85\x83\xa7\x6f\xc5\x43\xa4\xf1\xd5\xa8\xc3\x83\x86\x0a\x4b\xbd\x86\x62\x53\x05\x75\x2c\x98\x7a\x15\xe7\x54\xe4\xfc\xe5\x23\x31\x19\x84\xbe\x41\x27\x46\x96\x2a\xfb\x3b\x72\x74\xe7\xea\xcb\x4b\xd7\x11\x05\xf0\xbb\x9f\x8e\x79\xec\xc7\xdc\xd3\x78\x7a\x7c\xc8\x3e\x35\xf1\xe0\xfe\xdf\x50\xe9\xa7\xfe\xed\x5e\x7f\x7b\x5d\x35\x47\x32\xee\xde\xb3\xb1\xb8\x8a\x8c\x2a\xcb\xa3\x1e\x45\xd6\xcc\x66\xc2\xc3\x7c\xea\x95\xe1\xe8\x6c\xac\xd4\x4f\xb3\x89\x6e\x60\xfd\xc0\x1f\xa7\xfa\xa6\xe8\xfc\x40\x10\xa1\x0e\xe5\x0c\xe1\x84\x25\x33\xd3\x69\x30\x95\xa4\x87\x32\xd5\x1b\x3c\x80\x74\x30\x3d\x02\xcb\xfc\xaf\x1e\xdf\x3f\x6a\x31\xc3\xa7\xa0\xf5\x23\x3f\x00\xd0\xdb\x3a\x02\x18\x5c\xbc\x06\xd0\x4f\xbf\x01\xf0\x4e\xbd\x85\x71\xac\x29\x2a\x2b\xed\xf3\x5b\x30\xef\x14\x26\x3d\xf9\x8e\xa4\xff\xe9\x10\x08\xc4\x74\x8f\x0e\xa3\x37\xd7\x13\x53\xd9\xcd\x75\x7a\x88\xfd\xe6\xfa\xdd\xe8\xa5\x78\x07\xf2\x9b\xeb\x44\x8a\x50\x96\x9b\xeb\xec\x9e\x36\xe6\xfb\x50\x9f\xca\xfd\x9d\x3a\x4e\xff\x0a\xa4\x58\x83\x14\x2f\xc3\x11\x59\xe1\x8c\xc7\xc2\x0f\xfc\x00\x4d\xbc\xa9\x23\x9a\x04\x0f\xaf\x41\xf5\xd3\xaf\xd2\xc4\x4f\xcf\x68\x72\x0a\xe2\xfb\x59\x32\x18\x7c\x3f\x4b\x46\x0c\x53\x96\x0c\xa3\xaf\xb1\x64\xb2\xe0\xbd\xe0\xcf\x91\x64\xea\xef\x1d\x24\x39\x05\xfa\x54\xe6\x1d\x49\xb2\xbe\x76\xd9\x3f\xb7\x68\x7c\x6a\xa6\xf7\x24\x99\xf3\x99\xa6\xaf\x76\x3f\x3a\x18\x9f\x7f\x84\x34\x41\x0b\x1d\x80\x77\xa3\xaf\x02\x77\xb3\xaf\x32\xe6\x67\xb4\x13\x60\xf3\x53\xc2\x93\x83\x3e\x0c\xa5\x6d\xce\x66\xfb\x67\xb4\xa7\x3e\x10\x68\xfb\x9c\x48\x7d\x32\x87\x3f\xfd\x80\x18\xf8\xd2\xab\xbe\xf3\x19\xce\xee\x54\xf5\xec\xe5\xe0\x10\xce\xaf\xfe\xba\xef\x01\xe9\x61\x05\x9b\xd6\x42\xcd\x94\xe4\x0d\x1d\xbb\x4c\x05\x95\xa1\x39\x6f\x4d\x73\x36\xa2\x5f\xff\x8b\x90\xe6\x11\x51\x24\x23\xc9\x87\xef\x11\x9e\x85\x3c\x91\x91\x93\x5f\x22\x0e\x68\x32\x7c\x4e\x84\x6c\x8c\xa6\x8e\x15\x10\x06\x81\xf1\x55\x94\xfe\x3e\x8f\x16\xf7\xcc\x1a\x24\x50\x52\xb3\x86\xb3\x0a\x96\xe8\x20\x3b\x9c\x29\xc4\x2e\xc9\xbd\x1e\xf2\x3c\xe9\x60\x5c\xda\x47\xd3\xeb\xb8\x5e\x47\x8c\x33\x28\x4a\x04\x5d\x1c\x32\xe7\x0d\x02\x9f\x72\xf2\x66\x7f\xe9\x63\xf2\xd9\xf5\x3b\x68\xfd\xd9\x73\x7d\x12\xd5\x19\xc2\x3b\xd9\xec\xee\x97\x70\x6f\x49\x44\x2d\x15\xc4\xbd\x30\x8b\x83\x1c\xa3\x3c\xc6\x94\xd6\x5e\x5b\x9f\xfb\xae\x77\x28\x72\x12\x53\xd3\xcf\xfa\xb7\xbe\xea\xa7\xea\x3d\x54\xd8\x19\x8a\x5c\xf1\x7a\x75\x37\xfe\x9c\x0d\xe6\x17\xf0\x65\xbc\x7d\x73\xd7\x9d\xe1\xea\x44\x3f\xa2\x31\x52\xa0\x00\xa9\x40\x1b\x77\xcb\xad\x81\x09\x01\xe3\xa5\x1c\xf8\x6b\xba\xfe\x16\x28\x7c\x90\xb9\x0b\xeb\xa3\xfb\xea\x53\x57\x7a\xa4\x3f\x51\x09\x82\xf2\x9f\x00\x00\x00\xff\xff\x63\x11\xb3\xb5\xa4\x17\x00\x00") +var _templateClientTmpl = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xb4\x58\x5d\x6f\xe3\xba\x11\x7d\xb6\x7e\xc5\x54\xc8\xde\x4a\x81\x43\x6d\xf7\xad\x29\xf6\x61\x91\x6c\x2f\x02\xb4\xc9\x6d\x37\x45\xef\xdb\x05\x4d\x8e\x64\x36\x32\xa9\xa5\xa8\xc4\x81\x9b\xff\x5e\x0c\x49\x7d\xd9\x8e\x93\x2e\xda\x97\xc4\x22\xa9\x99\x33\x67\x0e\x87\x23\xee\x76\xc5\x79\x72\x65\x9a\x67\xab\xaa\xb5\x83\x4f\x1f\xff\xf0\xc7\x8b\xc6\x62\x8b\xda\xc1\x9f\xb9\xc0\x95\x31\x0f\x70\xa3\x05\x83\x2f\x75\x0d\x7e\x51\x0b\x34\x6f\x1f\x51\xb2\xe4\x7e\xad\x5a\x68\x4d\x67\x05\x82\x30\x12\x41\xb5\x50\x2b\x81\xba\x45\x09\x9d\x96\x68\xc1\xad\x11\xbe\x34\x5c\xac\x11\x3e\xb1\x8f\xfd\x2c\x94\xa6\xd3\x32\x51\xda\xcf\xff\xe5\xe6\xea\xeb\xed\xb7\xaf\x50\xaa\x1a\x21\x8e\x59\x63\x1c\x48\x65\x51\x38\x63\x9f\xc1\x94\xe0\x26\xce\x9c\x45\x64\xc9\x79\xf1\xf2\x92\x24\xbb\x1d\x48\x2c\x95\x46\x48\x45\xad\x50\xbb\x14\xe2\xf0\x59\xf3\x50\xc1\xe5\x67\x58\xf1\x16\xe1\x8c\x5d\x19\x5d\xaa\x8a\xfd\xc2\xc5\x03\xaf\x90\x16\xed\x76\xe0\x70\xd3\xd4\xdc\x21\xa4\x6b\xe4\x12\x6d\x0a\x67\xfe\x75\xb5\x69\x8c\x75\x90\x25\x8b\xb4\x36\x55\x9a\x24\x8b\x94\x2c\x1e\x1a\x29\x36\xaa\xb2\xdc\x61\x9a\x2c\x76\x3b\xb0\x5c\x57\x08\x67\xbf\x2d\xe1\x4c\x93\xeb\x33\x76\x6b\x24\xb6\x64\x72\x11\x2c\xe8\x23\x26\xc2\xf8\x38\xe0\x6d\x5d\x00\x6a\xe9\xb1\x2c\xd2\x4a\xb9\x75\xb7\x62\xc2\x6c\x8a\x32\xa6\x45\x69\xd1\xad\xb8\x33\xb6\x40\xed\x0a\xa9\x78\x8d\xc2\x1d\x80\x88\x61\x78\x24\xdf\x9c\xb1\xbc\x42\x76\xe3\xc7\x5a\xb8\x18\x41\xc5\x65\xd1\xb3\x77\x4c\xb3\x79\x92\x14\x05\x5c\x79\x56\x29\xb7\x94\x98\xc0\x31\xb8\x35\x77\xb0\x36\xb5\x6c\x81\xd7\x35\xd0\xd0\xaa\x53\xb5\x44\xdb\xb2\xc4\x3d\x37\xd8\xbf\xd6\x3a\xdb\x09\x07\xbb\x64\x21\x7c\xdc\x21\x34\x55\x12\xa0\xae\x21\xb7\x7f\x0d\x04\x06\x8e\x8a\x02\xbe\x89\x35\x6e\xf8\x9e\xbf\xd2\x58\x10\x16\xb9\x53\xba\x5a\x42\xe0\x5c\xe9\x0a\xb8\x96\x20\xad\x69\x1a\x7a\x68\xfd\x9b\x2c\x59\x2c\xa2\x8d\xf3\x98\x1c\x16\x9e\x67\xb4\x9e\x4a\xd7\x45\x8f\x25\x64\xe6\x96\x6f\x08\xde\x11\x48\x4a\x3b\xb4\x5c\x78\x28\x4f\xca\xad\xfd\xfc\xfc\xa5\x91\x96\xc5\x62\x3e\x73\x3e\x7b\x0c\x7c\x0d\xfc\x47\x84\xa3\x3e\x83\xd7\xa2\x54\x58\xcb\xb6\xe0\x52\x2a\xa7\x8c\xe6\x75\x54\xec\x8b\xcf\xd5\x2d\x3e\x45\xde\x3d\x59\xd8\x02\x07\x8d\x4f\x3d\xe4\x90\x82\xce\xa2\x1c\xd1\x56\xea\x11\x35\x98\x86\xac\xb5\x2c\x29\x3b\x2d\x46\x33\x99\x69\x5c\x0b\x8c\xb1\x3b\x3f\x9f\xc3\x79\x34\x4f\xf9\x24\xc6\x82\xc5\x5d\x6d\xaa\x4b\xa8\x4d\xc5\x7e\xb1\x4a\xbb\x5a\xbf\x24\x0b\xc1\xa2\x4d\x6f\x83\x31\x96\x27\x0b\x8b\xae\xb3\x1a\x7e\x0a\x46\x76\xc9\x22\x8a\xe2\x12\xc4\xd2\xd3\x73\x42\x19\x31\xa7\x97\xd0\xe7\xf4\x16\x9f\xc2\x50\x26\x98\xb4\xea\x11\x6d\xde\x1b\xe9\x09\x7c\x3b\xc7\xf3\x94\x5c\x52\xe4\x47\xb2\x92\x89\x68\x7a\xd8\x1a\x8b\x9e\xf1\xbb\xc6\xb3\x87\x9a\xa8\x16\x46\x6b\x14\x14\x35\x38\xe3\xd9\x95\xdc\x71\x5f\x7f\xda\x06\x85\x2a\x15\x4a\x58\x3d\x87\x19\x0f\x19\x34\xf9\x21\x15\x73\xb2\x16\x06\x2f\xe2\x62\xe1\x5f\xef\x8b\x1e\xad\x5c\xfa\xa5\x81\xc6\xbd\xdc\x72\xe7\xa8\xcc\x4a\xf2\xac\x1c\x0b\xd8\x82\x44\xa0\xe1\x96\x6f\xd0\xa1\x6d\x41\x70\x0d\x2b\x04\x2e\x25\xca\xb0\xab\xa2\x26\x48\xc2\xa3\xba\xa3\x10\x28\xba\x2c\x80\xba\xf5\xee\x09\xd0\x37\x8f\xc7\x13\xd4\x3a\xeb\x37\x64\x4c\xf5\x54\x29\x59\x94\xca\x12\xd0\x5a\x63\x73\x92\x4c\xfb\xa4\x9c\x58\xc3\x68\xd0\xeb\x88\xe8\xd9\xed\xe0\x5f\x46\xe9\x49\x99\xba\x0e\x25\xad\x85\x74\x09\x54\xd2\x2f\x63\x6e\xcf\xdc\xa6\xa9\x29\x91\x0d\x49\xad\x84\x34\xd6\xbe\xe2\x43\x5b\xc4\x3d\x42\xe9\x48\x47\x53\x31\xd5\xf4\xf2\x76\xd8\x4f\xc1\x0c\x0b\x73\x12\x4b\xde\xd5\x8e\x5c\x44\x89\x6a\x55\x2f\xa1\xdc\x38\xf6\x95\xc0\x97\x59\xda\xe9\x36\x88\x12\x65\xc4\x7f\x09\x1f\xbe\xa7\xcb\x49\x30\xf9\xa8\x8a\xfb\xed\x5e\x92\x9c\xe5\xba\xe5\x22\xe6\x63\xc6\x71\x26\xfa\x5d\x95\xc3\xfd\x36\x13\x6e\x4b\x39\x71\xb8\x75\x74\x54\xd0\x7f\x22\xf3\x7e\x3b\x25\x52\x95\xf0\xdb\x12\xcc\x83\xdf\x82\x51\xfd\x2c\x3b\x77\xdb\xeb\xb0\x11\xfe\x44\x73\xbb\x13\xe1\xf4\xc7\x23\x69\x5e\x70\xad\x0d\x55\x6a\x6e\x1d\xf0\x29\x54\x5f\x25\x94\x9e\x0f\xa6\x3e\xce\x85\x0b\x80\x08\x81\xc6\xa7\x00\x7c\x39\x80\xc9\x3d\x46\x9a\xff\xdd\x67\xf2\xfe\x6e\x30\x1e\x85\xaf\xec\x53\x9f\x97\xf0\xe1\x31\xf5\xfe\x82\x73\x51\x56\x93\xea\xd3\xe7\x83\x00\xf8\x4a\x24\x58\x6d\xaa\x25\x48\x5c\x75\xfe\xc9\xff\x78\x19\xeb\xcf\xfd\x76\x56\x7b\xca\x6a\xf9\xbf\xac\x15\x65\x75\x58\x2d\x96\x14\x75\x14\xc7\x35\xa1\xd9\xd3\x87\x47\x78\x11\x75\x01\x37\xee\xf7\x2d\x74\x6d\xd8\xcc\x15\x3a\x78\x44\xbb\x32\x2d\x52\x74\x15\x91\x63\x34\x0c\x35\xc2\x34\x48\x87\xa1\xaf\xde\x45\x91\x14\xc5\x22\x9a\xf1\x7e\xb2\x9c\x46\x3d\x9a\x4c\x69\x89\xdb\x21\xa8\x8f\x79\x0f\x3c\xac\xf8\x5b\x87\xf6\xb9\x5f\x7e\x65\x3a\x0a\xc5\x6d\x73\xb2\x79\xa0\xd3\x68\x7a\x7a\x1c\xa8\xb2\x27\x7a\x9a\x6b\x71\x22\x5d\x71\xe3\x46\x9c\xbd\x72\x96\x21\x7b\xf9\xd1\x54\x3a\xdb\xe1\xcb\xc9\x73\xa4\xcf\xe5\x0f\x9e\x24\x65\xf5\xff\x3f\x4b\x8e\xe9\x23\x4a\xe3\xaa\xa6\x2c\x0b\xfa\xdb\xce\xcf\x8f\xc9\xd1\x42\x47\x40\x63\xf1\x11\xb5\x6b\xbd\x78\xbe\x77\x68\x15\xb6\x50\x5a\xb3\x19\x36\xd0\x91\xea\xe2\xad\x67\x79\xa8\x23\x94\xa5\x3e\x49\x7d\x05\x89\x0b\x92\xd0\x3f\x9f\x8a\x96\x02\x8b\x07\x4f\x5f\x8a\x87\x48\xd3\xab\xb1\x0f\x8f\x3d\x54\x5c\x1a\x7a\x28\x3e\xed\xa0\x0e\x1b\xa6\xbe\x8b\xf3\x5d\xe4\xfc\xe5\x83\x66\x32\x36\xfa\x16\x7d\x33\x72\xa6\xd9\xdf\x51\xa0\x3f\x57\x5f\x5e\x76\x3b\x92\x00\x7e\x0f\xd3\xa9\x48\xc3\x98\x7f\x1a\x4f\x8f\x0f\xec\x53\x9b\x0e\xee\xff\x0d\xb5\x79\xea\xdf\xee\xfb\xef\xd0\x57\xcd\x91\x8c\xbb\xf7\x64\x2c\x3e\x23\x63\x97\x15\x50\x8f\x4d\xd6\xcc\x66\x26\xe2\x7c\x1e\x3a\xc3\xd1\xd9\x98\xa9\x9f\x66\x13\xbb\x41\xf5\x83\x7e\x7c\xd7\x37\x45\x17\x06\x62\x13\xea\x51\xce\x10\x4e\x54\x32\x33\x9d\x47\x53\x59\xbe\xdf\xa6\x06\x83\x7b\x90\xf6\xa6\x47\x60\x2c\xfc\xea\xf1\xfd\xa3\x91\x33\x7c\x1a\xba\x30\xf2\x03\x00\x83\xad\x03\x80\xd1\xc5\x6b\x00\xc3\xf4\x1b\x00\xef\xf4\x5b\x18\xc7\x9c\xa2\x76\xca\x3d\xbf\x05\xf3\x4e\x63\xd6\x8b\xef\xa0\xf5\x3f\x1e\x02\x81\x98\xee\xd1\x61\xf4\xe6\x7a\x62\x8a\xdd\x5c\xe7\xfb\xd8\x6f\xae\xdf\x8d\x5e\xc9\x77\x20\xbf\xb9\xce\x94\x8c\x69\xb9\xb9\x66\xf7\xb4\x31\xdf\x87\xfa\x18\xf7\x77\xfa\x90\xfe\x25\x28\x79\x09\x4a\xbe\x0c\x47\x64\x8d\x33\x1d\xcb\x30\xf0\x03\x32\x09\xa6\x0e\x64\x12\x3d\xbc\x06\x35\x4c\xbf\x2a\x93\x30\x3d\x93\xc9\x31\x88\xef\x57\xc9\x60\xf0\xfd\x2a\x19\x31\x4c\x55\x32\x8c\xbe\xa6\x92\xc9\x82\xf7\x82\x3f\x25\x92\xa9\xbf\x77\x88\xe4\x18\xe8\x63\xcc\x7b\x91\xb0\x3e\x77\xec\x9f\x6b\xb4\x81\x9a\xe9\x3d\x09\xf3\x3e\xf3\xfc\xd5\xea\x47\x07\xe3\xf3\x8f\x88\x26\xf6\x42\x7b\xe0\xfd\xe8\xab\xc0\xfd\xec\xab\x8a\xf9\x19\xdd\x04\xd8\xfc\x94\x08\xe2\xa0\x0f\x43\xe5\xda\x93\x6c\xff\x8c\xee\xd8\x07\x02\x6d\x9f\x23\xd4\x67\x73\xf8\xd3\x0f\x88\x41\x2f\x7d\xd7\x77\x9a\x61\x76\xa7\xeb\xe7\xd0\x0e\x0e\xe1\xfc\x1a\xae\xfb\x1e\x90\x1e\x96\xb0\xea\x1c\x34\x5c\x2b\xd1\xd2\xb1\xcb\x75\xec\x32\x8c\x10\x9d\x6d\x4f\x46\xf4\xeb\x7f\x11\xd2\x3c\x22\x8a\x64\x14\xf9\xf0\x3d\x22\x58\xe4\x89\x8c\x1c\xfd\x12\xf1\x40\xb3\xe1\x73\x22\xb2\x31\x9a\x3a\xec\x80\x30\x36\x18\x5f\x65\x15\xee\xf3\x68\x71\xaf\x2c\x9a\x42\x0f\x32\xf0\x19\xe1\x11\x51\x41\x15\x3b\x68\x78\x2b\x78\x4d\xcb\x7a\xec\x7d\xd7\xd6\x77\x0d\xe3\x0c\xca\x0a\xc1\x94\xfb\x3a\x79\x43\xae\xc7\x9c\xbc\x59\x4d\xfa\x08\x02\x97\x61\xbf\x5c\x7e\x0e\xca\x1e\xe7\x8e\xa8\xda\xf7\xc6\xfe\x12\x09\xb7\x8e\x3a\xa5\x33\x0d\x69\xdf\x7d\xa5\xb1\xe7\x22\xb2\x52\xe2\xae\x6f\xa0\x4f\x7d\xbc\x7b\xe7\x05\x75\x4c\xd3\x6f\xf7\xb7\x3e\xdd\xa7\x2d\x7a\x4c\xa3\x37\x94\xf8\x0c\xf5\x2d\xdc\xf8\x73\x36\x58\x9c\xc3\x97\xf1\x8a\xcd\xdf\x69\xc6\xfb\x11\xf3\x88\xd6\x2a\x89\x12\x94\x06\x63\xfd\x55\xb6\x01\x2e\x25\x8c\x37\x6f\x10\xee\xe2\xfa\xab\x9e\xf8\xd5\xe5\x6f\xa5\x0f\x2e\xa5\x8f\xdd\xdb\x51\x93\x89\x5a\x12\x94\xff\x04\x00\x00\xff\xff\x24\x0b\x75\x10\x89\x17\x00\x00") func templateClientTmplBytes() ([]byte, error) { return bindataRead( @@ -256,7 +256,7 @@ func templateClientTmpl() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "template/client.tmpl", size: 6052, mode: os.FileMode(420), modTime: time.Unix(1, 0)} + info := bindataFileInfo{name: "template/client.tmpl", size: 6025, mode: os.FileMode(420), modTime: time.Unix(1, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -601,7 +601,7 @@ func templateDialectSqlCreateTmpl() (*asset, error) { return a, nil } -var _templateDialectSqlDecodeTmpl = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xb4\x56\xdb\x6e\xdb\x38\x10\x7d\xb6\xbe\x62\x2a\x38\x81\x65\x38\xb4\x53\x2c\x16\xd8\x14\x59\xa0\xc8\x05\xd0\x62\x91\x2d\x72\xe9\x4b\x11\x2c\x18\x69\x18\x73\x43\x91\x0e\x49\xe5\x02\x41\xff\xbe\xe0\x45\xb6\x94\xc4\x4d\xfb\xd0\x37\x53\x43\x9e\x99\x39\xe7\xcc\xc0\x4d\x33\x9f\x26\x47\x6a\xf5\xac\xf9\xed\xd2\xc2\xc7\xc5\xfe\x1f\x7b\x2b\x8d\x06\xa5\x85\x53\x5a\xe0\x8d\x52\x77\x90\xcb\x82\xc0\x67\x21\xc0\x5f\x32\xe0\xe2\xfa\x01\x4b\x92\x5c\x2e\xb9\x01\xa3\x6a\x5d\x20\x14\xaa\x44\xe0\x06\x04\x2f\x50\x1a\x2c\xa1\x96\x25\x6a\xb0\x4b\x84\xcf\x2b\x5a\x2c\x11\x3e\x92\x45\x17\x05\xa6\x6a\x59\x26\x5c\xfa\xf8\xdf\xf9\xd1\xc9\xd9\xc5\x09\x30\x2e\x10\xe2\x37\xad\x94\x85\x92\x6b\x2c\xac\xd2\xcf\xa0\x18\xd8\x5e\x32\xab\x11\x49\x32\x9d\xb7\x6d\x92\x34\x0d\x94\xc8\xb8\x44\x48\x4b\x4e\x05\x16\x76\x6e\xee\xc5\xbc\x44\x57\xd1\x5c\x49\x4c\xa1\x6d\xdd\xad\xb1\xc6\x02\xf9\x03\x6a\x38\x38\x84\x31\x39\xef\x4e\x0e\x64\x3e\x07\x53\x50\xf9\x95\x8a\x1a\x5d\x87\xb6\xd6\xd2\xf8\x42\xec\xf3\x0a\x0d\x30\xa5\xfd\x05\xc9\xe5\x2d\x3c\x84\x5b\x4c\xab\x0a\xcc\xbd\x20\xe7\xea\xd1\x90\x84\xd5\xb2\x80\xc9\xd4\x25\x22\x67\xb4\x42\x68\xdb\xac\x07\x3a\xc9\xe0\xdb\x35\x97\x16\x35\xa3\x05\x36\x2d\x34\xc9\x28\xe4\x79\xfd\x7d\xb4\xdb\x34\xc0\x19\x48\x65\x61\x4c\xf2\x63\x72\x65\x50\x1f\xfb\x26\x4b\x68\x5b\x97\xf3\xac\x16\x22\x97\xf6\xf7\xdf\x9a\x06\x50\x18\x97\xcd\x67\xce\x8f\x7d\xe8\xf2\x79\x15\x3f\xa1\x74\x4f\x9a\x76\x96\x8c\x46\x4d\xb3\x07\x9a\xca\x5b\x84\xf1\xbf\x33\x18\xb3\x40\xc4\x29\x47\x51\x1a\x47\xc3\x28\x64\x1e\xb3\x01\xc6\xfa\x69\x80\x4a\x46\x6d\x12\x18\xa3\xc6\xf0\xdb\x8e\xb3\x70\x08\x9c\x45\x82\xec\x92\x5a\x78\x44\x8d\x91\x50\x2c\x87\x9c\xc1\x84\x32\x8b\x1b\x62\x33\x07\x6a\x95\x87\xe8\xd3\x08\xcc\x57\xd8\x51\x3c\x90\xb2\x6d\xe1\x05\xe5\xfd\xaa\x26\xb1\x12\x42\x48\x8f\xe2\x0c\x50\x6b\xa5\x3d\xd3\x9c\x41\x35\x03\xe9\x98\x10\x28\xe3\xfd\x6c\xe6\x0f\x1e\xf7\x0b\x2d\xee\xe8\xad\x83\x26\x47\x4a\xd4\x95\x34\xd9\x27\xa8\xe0\xc3\x21\x48\x0f\xd0\x89\xc8\x2a\x4b\x4e\x1c\x2c\x9b\xa4\x15\x37\x15\xb5\xc5\x12\x64\x5d\xdd\xa0\x76\xee\x75\x3d\x46\x5e\x0e\x60\xa7\x74\xef\x77\xca\x74\xe6\x93\x67\xc9\x68\xd4\x46\x8e\x39\x03\x2a\xcb\xd7\xaa\x4f\x94\x0e\x1f\x73\x73\x61\xb5\x73\x61\x3c\x5d\x5d\xe5\xc7\x59\x14\xcf\x01\x3c\x72\xbb\x04\x7c\xb2\x4e\xac\x31\xa4\x79\xf9\x94\xc2\x02\x52\x2f\x72\xea\x1f\x41\x7a\x8e\x45\x3a\xe0\xd0\x3d\x1e\x35\x0d\x58\xac\x56\x82\xda\xb7\x47\x89\x05\x08\xd2\x4b\xd6\x39\x22\x1c\x82\x0d\x5d\xcc\x37\x3a\x03\x75\xe7\x78\x0d\x5d\x7f\x5b\x5c\x93\xc9\x74\x60\x5d\xd7\xb7\x13\xe0\x83\xba\x0b\x54\xbe\xc5\x65\x2d\xf1\x69\x85\x85\xc5\xd2\x8f\x22\xec\x5c\xfa\x61\xf4\xc5\x00\x77\x14\x7a\x7c\x8f\x15\xeb\x1a\xb4\xe6\x1a\x3e\x84\x30\x4f\x43\x02\xdb\xd6\x58\x5d\x28\xf9\x40\x4e\x95\xae\xa8\xcd\xa5\x0d\xfa\x13\x5f\xdd\x0c\xf6\x17\xd9\xeb\xe9\x8a\x53\xd1\xbf\x99\xad\xc7\xec\xc5\xa0\x8c\xa2\xfd\xd6\x1c\xec\x1f\x5c\x0f\xc7\x90\x6f\x19\xc3\x6d\x4a\x8e\xf9\x46\x4a\xf6\xcb\x84\xec\x0e\x51\x0e\xc9\x45\xe2\x37\x68\x0c\xbc\xb3\x72\x23\xbc\x43\x70\x68\x63\x1e\xfa\xbb\x28\xd4\x0a\x49\x5e\x3e\xc1\xde\x3a\xc4\xfa\x21\xdf\x57\x2f\xa8\xd1\xf6\xc3\xe7\x58\x6c\x82\x4e\x4c\x46\x72\xf3\xd7\xc5\x3f\x67\xa1\x56\xce\xe0\x2d\xd7\x39\xd9\x38\xb4\xad\x33\xdf\xab\xd5\x96\x7d\xda\x78\xef\x67\xad\x17\xc1\xc2\xd6\xe9\x5c\xd8\xcb\xe7\x87\x3a\x98\xa7\x2b\xcd\x2f\x0d\x2e\x60\x77\xd7\xaf\x97\x69\x30\x2e\xfc\x09\x8b\x50\x02\x67\x6e\x31\xb9\xe2\xff\x33\x4a\x92\x2b\x59\x51\x6d\x96\x54\xc4\x9b\x33\xd8\x0d\xde\xb6\xce\xd6\x21\xff\x85\xd5\x75\x61\x03\x73\xbe\x1f\x07\x10\xd3\x7c\x67\xa4\x22\xf0\x5b\xad\x1c\xc0\xce\x43\x3a\x73\x38\xeb\x91\x8a\x9c\x6f\xe6\xdb\xcb\x23\x6b\x21\x3c\x2d\x4e\xa3\x1e\xad\x7b\x3f\x23\xc7\x1a\xe4\xd7\x8b\x11\x6d\xb3\xa4\xe6\x8b\x46\xc6\x9f\x7a\xc9\x53\x73\x2f\xd2\x38\x0d\x2f\x34\x23\x5f\xa9\xe0\x65\xe4\x72\xe3\xbc\x33\x2e\x04\xbd\x11\xdd\xbe\xeb\xb6\xce\x56\x65\xe0\x10\x24\x3e\x4e\x42\xa8\x73\x5f\x78\x39\x7d\xff\xe9\xd0\xb8\x21\x90\xfa\xf2\xd2\xcd\xcc\x0f\x16\xf0\x8f\x14\xf4\xa3\xa8\x5b\x76\xfc\x16\x73\x77\x4c\xbd\x97\x3d\x78\xfa\xe5\xc6\xe9\xac\x16\xce\xbd\x9f\xef\xec\x9c\x8a\xca\xe7\xee\x7f\x5e\x7c\xf1\x7f\x00\x00\x00\xff\xff\xb2\xfe\xca\x7e\xdc\x0a\x00\x00") +var _templateDialectSqlDecodeTmpl = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xb4\x57\x5b\x6f\x1b\x37\x13\x7d\x96\x7e\xc5\x64\x21\x1b\x92\x20\xad\x9c\xe0\xc3\x07\xd4\xa9\x0b\x04\x71\x02\xa8\x2d\xdc\xc0\x8e\xf3\x12\x04\x05\xb3\x1c\x4a\xac\xb8\xa4\x42\x52\xbe\x60\xb1\xff\xbd\xe0\x65\x25\xae\x6e\xb1\x1b\xe4\x6d\xc9\x21\x67\x86\x67\xce\x19\x72\xab\x6a\x32\xec\xbe\x55\xcb\x47\xcd\x67\x73\x0b\xaf\xce\x5e\xfe\x32\x5e\x6a\x34\x28\x2d\xbc\x27\x05\x7e\x55\x6a\x01\x53\x59\xe4\xf0\x46\x08\xf0\x8b\x0c\x38\xbb\xbe\x43\x9a\x77\x3f\xce\xb9\x01\xa3\x56\xba\x40\x28\x14\x45\xe0\x06\x04\x2f\x50\x1a\xa4\xb0\x92\x14\x35\xd8\x39\xc2\x9b\x25\x29\xe6\x08\xaf\xf2\xb3\xc6\x0a\x4c\xad\x24\xed\x72\xe9\xed\x7f\x4e\xdf\xbe\xbb\xba\x79\x07\x8c\x0b\x84\x38\xa7\x95\xb2\x40\xb9\xc6\xc2\x2a\xfd\x08\x8a\x81\x4d\x82\x59\x8d\x98\x77\x87\x93\xba\xee\x76\xab\x0a\x28\x32\x2e\x11\x32\xca\x89\xc0\xc2\x4e\xcc\x37\x31\xa1\xe8\x32\x9a\x28\x89\x19\xd4\xb5\x5b\xd5\xd3\x58\x20\xbf\x43\x0d\xe7\x17\xd0\xcb\xaf\x9b\x91\x73\x32\x99\x80\x29\x88\xfc\x44\xc4\x0a\xdd\x09\xed\x4a\x4b\xe3\x13\xb1\x8f\x4b\x34\xc0\x94\xf6\x0b\x24\x97\x33\xb8\x0b\xab\x98\x56\x25\x98\x6f\x22\xbf\x56\xf7\x26\xef\xb2\x95\x2c\xa0\x3f\x74\x81\xf2\x2b\x52\x22\xd4\xf5\x20\x71\xda\x1f\xc0\xe7\x2f\x5c\x5a\xd4\x8c\x14\x58\xd5\x50\x75\x3b\x21\xce\xee\x7c\xe7\xb4\xaa\x80\x33\x90\xca\x42\x2f\x9f\x5e\xe6\xb7\x06\xf5\xa5\x3f\x24\x85\xba\x76\x31\xaf\x56\x42\x4c\xa5\xfd\xff\xff\xaa\x0a\x50\x18\x17\xcd\x47\x9e\x5e\x7a\xd3\xc7\xc7\x65\x9c\x42\xe9\xb6\x54\xf5\x08\x26\x13\x58\x2f\x09\xf9\x75\x3b\x9d\xaa\x1a\x83\x26\x72\x86\xd0\xfb\x7b\x04\x3d\x16\xb0\x79\xcf\x51\x50\x13\x56\xf8\x64\x7a\xac\xe5\x76\xe3\x8d\x6d\xf9\x0a\xe1\xba\x9d\xba\xeb\x4b\x33\x86\x7b\x6e\xe7\xce\xa3\xd2\xc8\x67\xf2\x0f\x7c\x0c\x6e\x27\x13\x60\x8b\xa7\xc1\xcd\xc2\xd6\xf1\xc2\xed\xdd\x8f\x7d\x67\x2f\xf8\x4d\x80\x7d\xd0\x1f\xc6\x3e\x85\x84\x2d\x1c\x1e\x79\x04\xc2\x5b\x22\x44\x6c\x11\x40\x6a\x4c\x69\xc5\xd8\xd3\xeb\xc5\xbe\x57\xad\x14\xdf\x16\xc0\x1d\x0f\x72\x32\xe3\x38\x4c\x8c\xe1\xb3\x86\xc5\x61\x10\x60\x8d\xb0\xd9\x39\xb1\x70\x8f\x1a\x23\xe6\x48\xdb\x48\x42\x9f\x30\x8b\x1b\xec\x07\xce\xa9\x55\xde\x45\x8a\x2d\x30\x4f\x90\x86\xf4\x2d\x71\xd5\x35\x6c\xd5\x21\xcd\xaa\x1f\x33\xc9\xf3\x3c\x01\x7e\x00\xa8\xb5\xd2\x1e\x7f\xce\xa0\x1c\x81\x74\x28\x0b\x94\x71\xfd\x60\xe4\x07\xde\xef\x07\x52\x2c\xc8\xcc\xb9\xce\xdf\x2a\xb1\x2a\xa5\x19\xbc\x86\x12\x7e\x05\x19\xea\x17\x2b\xcb\x4a\x9b\xbf\x73\x5e\x59\x3f\x2b\xb9\x29\x89\x2d\xe6\x20\x57\xe5\x57\xd4\xae\x9d\xb8\x23\x46\x58\xce\xe1\x84\xc2\x8b\x0b\x38\xa1\xd9\xc8\xc7\x1e\x04\x78\x3d\xde\x9c\x01\x91\x74\x57\x86\x7d\xa5\xc3\xe4\xd4\xdc\x58\xed\x78\x1a\x47\xb7\xb7\xd3\xcb\x41\x52\x30\x2f\x00\x7c\xb0\xae\x4c\x3d\xc8\xa6\xf4\x21\x83\x33\xc8\x3c\x7b\x32\xbf\x09\xb2\x6b\x2c\xb2\x16\x84\x91\x6e\x60\xb1\x5c\x0a\x62\xf7\xf7\x36\x16\x5c\xe4\xfb\xd8\xe1\x07\x81\x67\xce\xe6\x0f\x3a\x02\xe5\xf9\x1c\x4e\xfd\xf9\xec\x4b\xde\x1f\xb6\xb8\xe9\xce\xed\xf0\x7f\xa1\x16\x01\xca\x7d\x58\xae\x24\x3e\x2c\xb1\xb0\x48\xbd\x58\xe1\xe4\xa3\x97\xab\x4f\x06\xb8\x83\xd0\xfb\xf7\xbe\x62\x5e\xad\xa3\xb9\x03\x5f\x40\x90\x4b\x1b\xc0\xba\x36\x56\x17\x4a\xde\xb9\x6e\x51\x12\x3b\x95\x36\x94\x3f\xf7\xd9\x8d\xe0\xe5\xd9\x60\xb7\xdd\x45\xf1\xa4\x2b\x07\x6b\x25\x6d\x75\xa5\x4e\x64\xdf\x1a\x83\x97\xe7\x5f\xda\x4d\x90\x1f\x68\x82\x87\x2a\xd9\xe3\x9b\x52\xb2\x9f\x56\xc8\x74\x70\xa0\xa1\xee\x9e\xad\xaa\x9c\x66\xd2\x83\xf8\xc3\xba\x02\x27\xc2\x82\x8b\x8b\xbd\xd2\x4a\xfc\x0f\x22\x19\xb6\x61\x6a\x37\xc7\x63\xdd\xb1\xa5\x24\xb6\xab\x23\x96\xa8\x88\x6d\x69\xe8\x69\x32\xda\xc5\x3e\xbb\xb1\x7a\x55\xd8\xf5\x82\xb4\x91\xfe\x87\xa2\x6c\xd7\xa5\xb3\xa3\xb1\x00\xed\x3e\xa5\x39\x6c\x39\xd4\xf5\xae\xe0\x5e\x27\x5a\x7b\x96\xdc\x90\xce\x70\x1c\x34\xb7\xb9\x26\xea\xba\xa5\x3e\x27\xc0\x90\x60\x93\x57\xfe\x89\x08\x4e\x37\xf1\xb6\xa5\xd9\xba\x71\xe0\x02\x24\xde\xf7\xc3\x5c\x54\x59\xe3\xb7\x33\xfc\xde\xd6\x28\x70\xf6\x23\xf2\x66\xdf\x15\x77\xa7\x69\x32\x3b\xd5\x69\x0f\x77\x94\x14\x91\x96\x5c\x74\xfd\xe3\xb0\xb9\x44\x8f\xbf\x26\x23\x27\x9c\x07\xcf\x76\x1e\x3a\xc5\x4d\xa1\x96\x98\x4f\xe9\x03\x8c\xd7\x26\x96\x9a\x82\x18\x36\x46\x8d\x36\x35\x5f\x63\x91\xee\xf4\x8b\xbd\x8c\xf2\x84\xc3\x01\x93\xa8\xfe\xb0\x6f\xc7\x1a\xf7\x06\x55\x26\x38\x45\xf9\xf9\x6a\xfc\x7e\xf3\xd7\x55\xc0\xe0\x09\x6c\xdd\x79\xa3\xa4\x8c\x7d\xee\xe5\xd0\xa2\x48\xc3\xd4\x24\x9e\xbf\x76\xdb\x84\x75\xd7\xb2\xe4\x02\x4e\x4f\x7d\x93\x1a\x06\x72\xc3\x6f\x70\x16\x52\xe0\xcc\xbd\x1c\x5c\xf2\xff\x18\x25\xf3\x5b\x59\x12\x6d\xe6\x44\xc4\x95\x23\x38\x0d\x3c\xb5\x6b\x8a\x46\xb0\x06\xaf\xfd\xc6\xe8\xfe\xc8\x65\x17\x1d\xee\x3b\xc2\x39\x9c\xdc\x65\x23\xe7\x67\x7d\xd9\x45\xac\x37\x5d\xc1\x57\x54\xae\x84\xf0\x70\x84\xa2\xae\xe1\x1c\x3f\xa7\x0c\x6b\x27\x3f\xbf\x08\x91\x2e\x73\x62\x3e\x68\x64\xfc\x21\x09\x9e\x99\x6f\x22\x6b\x44\x75\xa4\xb9\x6c\x18\x77\xc5\x85\x20\x5f\x05\x26\x6d\x73\x6f\x45\x8e\xb4\x9b\xe1\xe1\x2d\x6d\x82\x06\x29\x64\x3e\x9d\xac\xd5\x09\xd2\x36\xfd\xe3\xde\x0e\xbc\xb2\x0e\x90\xb7\x41\xe4\x50\xd4\xc0\xd5\xed\x0e\xd5\x50\x29\x8c\xd3\x87\xfe\xf1\x1e\x55\x12\xf9\xd8\xfc\xf2\x6e\x76\x4c\x86\xf0\x86\x52\x6e\xb9\x92\x0d\x99\xc3\x6f\x96\x7b\xda\xcf\x50\xa2\x26\x8e\x2f\xa5\xa2\x28\xfc\xfc\x5c\x09\xea\xba\xb6\xb3\xb7\xfe\xc0\xfc\x5f\xf7\x81\x14\xfc\xf6\xd0\x25\xcd\xa6\x4d\xb6\x7e\xa6\xf6\xbc\x5c\x0e\x3e\x1c\xda\x77\xca\xfa\x46\x49\x18\x35\xdc\xfc\x33\xa5\xbc\xd9\x82\x2e\x7e\xfd\x1b\x00\x00\xff\xff\xf9\x90\x6c\x5a\xee\x10\x00\x00") func templateDialectSqlDecodeTmplBytes() ([]byte, error) { return bindataRead( @@ -616,7 +616,7 @@ func templateDialectSqlDecodeTmpl() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "template/dialect/sql/decode.tmpl", size: 2780, mode: os.FileMode(420), modTime: time.Unix(1, 0)} + info := bindataFileInfo{name: "template/dialect/sql/decode.tmpl", size: 4334, mode: os.FileMode(420), modTime: time.Unix(1, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -701,7 +701,7 @@ func templateDialectSqlGroupTmpl() (*asset, error) { return a, nil } -var _templateDialectSqlMetaTmpl = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xa4\x54\x41\x6f\x32\x37\x10\x3d\xb3\xbf\xe2\x09\x71\x48\xa2\xc4\x9b\x70\x6b\x25\x0e\x11\x4d\x24\xd4\x10\xa5\x4d\x6e\x55\x55\x99\xf5\x2c\x58\x31\x36\xb1\x0d\x09\xda\xee\x7f\xaf\x6c\x2f\xcb\x42\x21\xad\xf4\xdd\xd6\x1e\xcf\x9b\x37\x6f\xde\x6c\x55\xe5\x57\xd9\xd8\xac\xb6\x56\xce\x17\x1e\xc3\xdb\xbb\x9f\x6e\x56\x96\x1c\x69\x8f\x47\x5e\xd0\xcc\x98\x77\x4c\x74\xc1\x70\xaf\x14\xe2\x23\x87\x10\xb7\x1b\x12\x2c\x7b\x5b\x48\x07\x67\xd6\xb6\x20\x14\x46\x10\xa4\x83\x92\x05\x69\x47\x02\x6b\x2d\xc8\xc2\x2f\x08\xf7\x2b\x5e\x2c\x08\x43\x76\xbb\x8b\xa2\x34\x6b\x2d\x32\xa9\x63\xfc\x69\x32\x7e\x78\x7e\x7d\x40\x29\x15\xa1\xb9\xb3\xc6\x78\x08\x69\xa9\xf0\xc6\x6e\x61\x4a\xf8\x4e\x31\x6f\x89\x58\x76\x95\xd7\x75\x96\x85\x1e\x50\x18\xed\x3c\xd7\xde\x41\x13\x09\x12\x28\x8d\x85\xfb\x50\x10\x92\x2b\x2a\xbc\x63\x88\xaf\xab\x0a\x82\x4a\xa9\x09\xfd\x26\x92\xbb\x0f\x95\x2f\xc9\xf3\xbc\xc5\xe8\xa3\xae\xb3\x5e\x9e\xe3\x8d\xcf\x14\x61\x61\x94\x70\x91\x94\x8f\x67\xcd\x97\x94\x08\x11\xaa\x0a\xca\x7c\x92\xc5\x80\x3d\x87\xeb\xba\xde\x35\x20\xb8\xe7\x33\xee\x88\x65\xbd\x04\x33\x42\xbf\xaa\x30\x60\xe9\x54\xd7\xfd\xac\x57\x55\x37\xb0\x5c\xcf\x09\x83\xbf\xae\x31\x20\xfc\x3c\xc2\x80\x3d\x88\x39\xb9\x48\x21\x70\x08\x39\x94\x92\xc6\x0d\xc1\x58\xa5\xcb\x28\x7c\xed\x59\xa6\x8c\x1d\x1d\x4b\x8a\x7b\x69\x74\x4e\x62\x1e\xc8\xc4\xa2\xb2\x0c\x4f\xa6\xc3\x69\x78\xf1\xb6\x20\xac\xac\x5c\x72\xbb\xc5\x3b\x6d\x21\xa8\x50\xdc\x92\xc0\x8c\x94\xf9\x64\x55\x05\xd2\x22\xf1\x39\x43\xa6\x69\x8d\xd8\xef\xa4\xba\xfd\xed\x6a\xd1\x47\xdb\x77\x48\xdf\xae\x1a\x0c\xfc\x0d\x6d\x7c\x82\xde\xf7\x3a\xd1\x1b\xb2\x8e\xbe\x6f\x39\x0e\x21\x0c\x79\xdf\x71\xc4\xdd\xb5\x4d\xda\x4b\xbf\x65\x0d\xf0\xc4\x83\xbe\xa4\xf3\x2e\x4d\x47\x3a\xac\x78\xf1\xce\xe7\xd1\x6e\xc6\x46\xa3\x1a\xf0\x8d\x91\x02\x85\xb4\xc5\x5a\x71\x0b\x41\x2b\xd2\x82\x74\xb1\xc5\xa7\xf4\x8b\x58\xa9\xdf\x29\xf5\xd2\x40\xd4\x75\x7f\x07\x17\xeb\x7d\xdf\xc5\xe8\x00\xe3\x58\xac\x8e\xd2\x51\xb9\x20\x4f\x3b\xa9\x03\x95\xc6\x46\xad\x97\xfa\xac\x3e\x45\x0c\x43\x90\x36\x5e\xea\xf9\xff\x31\x46\xef\x1c\xf0\xc1\x78\x53\xf8\x04\xe5\xce\xf7\xde\x32\x69\x3b\x37\xdc\xca\xc0\xea\x47\xb6\xb3\xc5\x68\xb7\x33\x31\x71\x8d\xf3\xb9\x52\x78\xfd\xed\xa9\x69\xdc\x81\xdb\x93\xdb\x59\x4a\x52\xc2\xb1\xac\xb7\xe1\xb6\x45\x18\xe1\x8f\x3f\x9d\xb7\x52\xcf\xab\xc6\xe4\x6c\xf2\x0b\xeb\x48\x70\xdd\xf4\xda\x59\xd6\x32\x2d\xeb\x63\xc4\x6b\x86\x13\x32\xcb\x53\x79\x3b\x8d\xea\x2c\xe8\x94\xec\x34\x60\xcf\xeb\x65\x3b\xd8\x40\xe7\x22\x61\xfc\xc7\x3f\xe1\xdf\x1b\x1c\x2f\xf7\xce\x78\xf9\xb5\x3b\x3c\xae\xc5\x39\xc7\x0c\xa3\x48\xc7\x9e\x71\x07\xa6\x69\xb1\xbb\x7f\x88\xc3\xbd\x3b\x36\x14\x2e\xa6\xc3\xe9\x25\x4b\x99\xa7\x28\x75\xe4\x0e\xc6\x92\x5a\xd0\xd7\xa1\xbd\x1c\x6e\x83\xc3\xae\x71\x36\x7e\x17\xe2\x7b\x39\xda\xbd\x39\x3c\x5d\x46\xb9\x8f\x5d\xf9\x4f\x00\x00\x00\xff\xff\x75\xd7\xa4\x71\xf6\x06\x00\x00") +var _templateDialectSqlMetaTmpl = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xa4\x54\x4d\x6f\xeb\x36\x10\x3c\x4b\xbf\x62\x61\xf8\xf0\x5e\x90\x50\x79\xbe\xb5\x80\x0f\x0f\x6e\x02\x18\x89\x83\xb4\xc9\xad\x28\x0a\x5a\x5c\xc9\x84\x69\xd2\x21\x69\x3b\x82\xa0\xff\x5e\x90\xd4\x07\xe5\xda\x41\x81\xde\x24\x2e\x39\x3b\x33\xdc\x61\x5d\x67\x37\xe9\x42\xed\x2b\xcd\xcb\x8d\x85\xd9\xfd\x8f\x5f\xee\xf6\x1a\x0d\x4a\x0b\x8f\x34\xc7\xb5\x52\x5b\x58\xca\x9c\xc0\x4f\x21\xc0\x6f\x32\xe0\xea\xfa\x88\x8c\xa4\xef\x1b\x6e\xc0\xa8\x83\xce\x11\x72\xc5\x10\xb8\x01\xc1\x73\x94\x06\x19\x1c\x24\x43\x0d\x76\x83\xf0\x73\x4f\xf3\x0d\xc2\x8c\xdc\x77\x55\x28\xd4\x41\xb2\x94\x4b\x5f\x7f\x5e\x2e\x1e\x5e\xde\x1e\xa0\xe0\x02\xa1\x5d\xd3\x4a\x59\x60\x5c\x63\x6e\x95\xae\x40\x15\x60\xa3\x66\x56\x23\x92\xf4\x26\x6b\x9a\x34\x75\x1a\x20\x57\xd2\x58\x2a\xad\x01\x89\xc8\x90\x41\xa1\x34\x98\x0f\x01\x8c\x53\x81\xb9\x35\x04\xfc\xee\xba\x06\x86\x05\x97\x08\x93\xb6\x92\x99\x0f\x91\xed\xd0\xd2\xac\xc7\x98\x40\xd3\xa4\x49\x96\xc1\x3b\x5d\x0b\x84\x8d\x12\xcc\x78\x52\xd6\xff\x4b\xba\xc3\x40\x08\xa1\xae\x41\xa8\x13\x6a\x98\x92\x17\xb7\xdc\x34\x9d\x00\x46\x2d\x5d\x53\x83\x24\x4d\x02\xcc\x1c\x26\x75\x0d\x53\x12\xfe\x9a\x66\x92\x26\x75\x7d\x07\x9a\xca\x12\x61\xfa\xf7\x2d\x4c\x11\x7e\x9d\xc3\x94\x3c\xb0\x12\x8d\xa7\xe0\x38\xb8\x33\x18\x0e\x2d\x5a\x82\xbe\x4b\xcc\xc8\x7d\x0d\x2c\xc3\x89\x8e\x8e\x46\x41\x2d\x57\x32\x43\x56\x3a\x32\xbe\x29\x2f\xdc\x96\xd5\x6c\xe5\x76\xbc\x6f\x10\xf6\x9a\xef\xa8\xae\x60\x8b\x15\x30\xcc\x05\xd5\xc8\x60\x8d\x42\x9d\x48\x5d\x03\x4a\x16\xf8\x5c\x21\xd3\x4a\x43\xf2\x07\x8a\x58\x5f\xd7\x4b\x62\xaf\xdb\x1d\xaf\xf6\xd8\xef\x4a\x93\x48\xe5\x52\x1e\x51\x1b\xfc\x5a\xac\xb7\xdf\x5d\xef\xa0\xd5\x23\x76\x82\x51\x5a\x6e\x2b\xd2\x02\x2f\x2d\xe0\x27\x37\xd6\x84\x7b\xe1\x06\xf6\x34\xdf\xd2\xd2\x0f\x9a\xd2\x7e\x44\x15\xd0\xa3\xe2\x0c\x72\xae\xf3\x83\xa0\x1a\x18\xee\x51\x32\x94\x79\x05\x27\x6e\x37\xbe\xd3\x24\x6a\xf5\xda\x42\x34\xcd\xa4\x83\xf3\xfd\xbe\x56\x31\x1f\x61\x9c\xdb\x14\x79\x1c\x3c\x53\x76\xb8\xa3\x91\x4b\x0b\x25\x0e\x3b\x79\xd5\x9f\xdc\x97\x81\xa1\x54\x96\xcb\xf2\xbf\x8c\x44\x72\x0d\x78\x74\xb1\xa1\x7c\x81\x72\xf4\x3d\x0c\x4b\xc8\xe5\x91\x6a\xee\x58\xfd\x9f\x5c\xf6\x18\x7d\x2e\x03\x13\xd3\xce\x3c\x15\x02\xde\x7e\x7f\x6e\x85\x1b\xdf\xe2\x42\x2e\x0b\x8e\x82\x19\x92\x26\x47\xaa\x7b\x84\x39\xfc\xf9\x97\xb1\x9a\xcb\xb2\x6e\xc7\x9b\x2c\x7f\x23\x91\x05\xb7\xad\xd6\x36\xa6\x45\x88\xe8\xa3\xc7\x6a\x2f\xc6\x9d\x2a\x2e\x9d\xe9\xfc\xf1\x16\x65\x37\xee\x56\xa9\xac\xba\xa7\x03\x7d\xcc\xd5\x49\x1a\xa0\x8e\x33\xf2\x52\xde\xb9\xfc\x79\x43\x1c\xaa\x9f\xbd\x29\x79\x0c\xb5\x27\xac\x86\x57\x21\x5e\x1b\x92\xef\x5c\x88\x90\xdc\x22\xb5\x40\x35\xba\x36\x2e\xd0\x55\x3f\x0d\xbd\x2d\xd6\x0d\x63\x9a\x78\x57\x62\xd4\xb1\x33\x23\x0f\xb6\xce\x04\xd2\xaa\x4f\xfc\x84\x14\xdb\xe0\x49\x07\x3b\xb9\xed\x0e\xf5\x73\x1d\x34\x75\xd3\x11\xe9\x7b\x39\xec\xfa\x29\x77\x2c\xbe\x9d\xf5\xbb\xfc\x34\xfe\xfb\x21\xf3\x8b\x43\x4c\x5e\x9f\xe2\x49\xa6\x92\x5d\x8b\xcf\xcc\x3b\x74\x1e\x20\x33\x4a\x50\x8f\x1d\x3f\x94\xe3\x47\xe8\x3c\x5d\xf0\x6d\x35\x5b\x7d\x27\xe1\xe4\x25\x4a\x91\xc3\xce\x43\x2e\x19\x7e\x8e\xb3\x66\xe0\xde\x7b\x09\x57\xeb\x3f\x5c\x7d\xb0\xa3\x37\x7b\xfc\xf7\x3d\xb6\xbe\xff\xfa\x27\x00\x00\xff\xff\x4c\x3d\xd2\x31\xfd\x07\x00\x00") func templateDialectSqlMetaTmplBytes() ([]byte, error) { return bindataRead( @@ -716,7 +716,7 @@ func templateDialectSqlMetaTmpl() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "template/dialect/sql/meta.tmpl", size: 1782, mode: os.FileMode(420), modTime: time.Unix(1, 0)} + info := bindataFileInfo{name: "template/dialect/sql/meta.tmpl", size: 2045, mode: os.FileMode(420), modTime: time.Unix(1, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -761,7 +761,7 @@ func templateDialectSqlPredicateTmpl() (*asset, error) { return a, nil } -var _templateDialectSqlQueryTmpl = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x57\x5b\x4f\xe3\xc8\x12\x7e\x8e\x7f\x45\x1d\x34\x67\x14\xa3\xe0\x00\xe7\xe9\x04\xe5\x48\x1c\x60\xa4\x68\x67\x98\xd9\x09\xbb\xfb\x80\xd0\xaa\xb1\xcb\x49\x8b\x4e\xb7\xe9\xee\x04\x50\xe4\xff\xbe\xaa\x6a\xdb\x71\x2e\x0c\x30\xab\x1d\x69\xa5\x7d\xe0\xd2\x75\xfd\xba\x6e\x5d\x5e\x2e\xfb\xfb\xd1\x99\x29\x9e\xac\x9c\x4c\x3d\x1c\x1f\x1e\xfd\xf7\xa0\xb0\xe8\x50\x7b\xf8\x20\x52\xbc\x35\xe6\x0e\x46\x3a\x4d\xe0\x54\x29\x60\x21\x07\xc4\xb7\x0b\xcc\x92\xe8\x6a\x2a\x1d\x38\x33\xb7\x29\x42\x6a\x32\x04\xe9\x40\xc9\x14\xb5\xc3\x0c\xe6\x3a\x43\x0b\x7e\x8a\x70\x5a\x88\x74\x8a\x70\x9c\x1c\xd6\x5c\xc8\xcd\x5c\x67\x91\xd4\xcc\xff\x38\x3a\xbb\xb8\x1c\x5f\x40\x2e\x15\x42\x45\xb3\xc6\x78\xc8\xa4\xc5\xd4\x1b\xfb\x04\x26\x07\xdf\x72\xe6\x2d\x62\x12\xed\xf7\xcb\x32\x8a\x96\x4b\xc8\x30\x97\x1a\x61\x2f\x93\x42\x61\xea\xfb\xee\x5e\xf5\xef\xe7\x68\x9f\xf6\xa0\x2c\x49\xe0\x5d\x71\x37\x81\xc1\x10\xde\x25\xe3\xd4\x14\x98\x7c\x11\xe9\x9d\x98\x60\xcd\xbd\x9d\x4b\x45\x60\x07\x43\x28\x84\x4b\x85\x6a\x04\xff\x5f\x71\x2a\x41\x8b\x29\xca\x45\x90\x6c\xfe\x6f\xd4\x09\x4d\x3e\xd7\x29\x74\xd7\x64\xcb\x12\xf6\xdb\x5e\xca\x32\x06\x77\xaf\x4e\x95\xea\xa6\xfe\x11\x52\xa3\x3d\x3e\xfa\xe4\x2c\xfc\x8d\xa1\x7b\x7d\xc3\xf2\xc9\xa5\x98\x11\xc4\x1e\xa0\xb5\xc6\xc6\xb0\x8c\x3a\x0b\x61\xa1\x1b\x75\x3a\xda\x64\xe8\x60\x43\x30\xea\x74\x5c\x81\x29\x0c\x61\xc3\x7f\xc2\xc1\x18\x17\x98\x76\xe3\xa8\x13\x47\x2c\x96\x8c\x53\xa1\x7f\x15\x6a\x8e\x0e\x86\x40\xb8\xbb\x31\x5c\xdf\x48\xed\xd1\xe6\x22\xc5\x65\x49\x0e\xd9\x13\x5d\xf7\x7d\xdb\xd3\x32\x35\x3a\x97\x93\xc1\x96\xa3\x40\x2f\x1b\x84\x43\x10\x45\x81\x3a\xeb\xf2\xb1\x07\xf4\x27\x8e\x3a\x1d\x8b\x7e\x6e\x35\x1f\x13\xd7\xe0\x20\x74\x65\x85\xee\xd4\x39\x39\xd1\x35\xb2\x45\xc0\x99\x24\x49\x0b\x5f\x1c\x02\xc3\x30\x65\x0e\x0a\x75\x70\x13\xc3\x70\x08\x87\x4c\xae\x1d\xe5\x33\x9f\x5c\x90\x70\xde\xdd\xab\xeb\xa1\x2c\x07\x50\x79\x49\x85\x52\x98\xc1\x83\xf4\x53\x33\xf7\x7c\x94\x7a\x02\xab\x08\xed\x11\xe8\xb2\x15\x0e\x76\x74\xbd\x72\x79\x70\x74\xb3\x71\x2d\xc1\xa6\xab\x8b\x05\xfc\x49\x92\x84\x1b\xca\x9c\xa0\x93\x1d\x77\xaf\x26\x56\x14\xd3\xe4\x67\x4a\xd1\x25\xd9\xa2\xaa\xe8\x6d\x45\x36\xb3\xf4\x5f\x0f\x28\x38\xf1\x09\xab\xff\x6b\x08\x5a\x2a\xbe\x67\xed\x58\x2a\xae\x16\x76\xd2\x02\x43\x91\x97\x2a\x7a\x53\x7d\x9e\x99\xb9\xf6\xcf\x54\xa8\xd4\xbe\x5d\x95\x5c\x75\x83\x17\xca\xae\x82\xd3\x5c\x98\xed\xbf\xf6\xc2\x6f\x83\x7e\xf1\x28\xdd\x73\xd0\x6f\x8d\x51\x6d\xec\xba\x57\xa7\x62\x13\x40\x3b\x06\x71\x93\xb3\xed\xa0\xe7\x42\x39\xec\x3d\x5b\x62\xe9\x14\xd3\x3b\x40\x82\x84\x3a\xc5\x01\xfc\x7b\xb1\xc7\x3e\xe3\xb5\x2c\xc1\xff\xe0\xf0\xad\x59\x6a\xc5\x17\xf6\xd7\x2b\x89\xa8\xed\xd4\xbc\xdf\x66\xd3\x15\x28\xfe\x83\x16\x93\xce\x35\xaf\x73\x25\x6e\x15\x86\x1e\x6f\x4d\xcc\x84\xc9\x3d\x12\x38\x33\x6a\x3e\xd3\x6e\x5b\xa4\x62\xb0\xd0\xe8\xbc\xed\xe0\x83\x44\x95\x35\x1e\x3a\x57\x4f\x05\x0e\x20\x27\x62\xc2\x46\x46\xe7\x09\xd1\x28\x61\xce\xd7\x03\x90\x45\x83\xcd\x6d\x5f\xb5\x1a\x6b\x08\xed\x6b\x05\xfe\xcd\xbf\x3e\x58\x33\xdb\x1e\x55\xee\x5e\x11\xf3\x17\x2d\xef\xe7\x38\x00\x6f\xe7\x74\xa9\xd0\x9c\x85\xdb\x55\x10\x85\xc5\x4c\xa6\xc2\xa3\x3b\xe1\x61\x53\xb8\x98\xb2\xc6\xb5\xc0\x33\xeb\x4b\x2d\x50\x8f\x2d\x87\x8a\x1f\x30\xce\x4e\x32\xae\x4e\x71\x18\x4d\xb9\xb1\x20\xf9\x25\x11\x7a\x82\xe4\x33\xc4\xa4\x70\xd7\xf2\xa6\x51\x8d\xf9\x2e\x61\xfa\x04\x70\x4a\xce\xa4\xdf\x85\x8f\x19\x27\x15\xbf\x55\xa7\x8c\xed\x23\x53\x87\xb0\xcf\xec\xda\x96\xc9\x73\x87\x3b\x8d\x05\xce\x49\x2d\xb1\x69\xee\x73\x20\x0f\x61\x3f\x08\x7c\x3b\x72\xc6\x66\x68\x9f\x09\xda\x67\xe2\xfd\x75\x01\xab\xc7\x4e\x81\xe9\xdb\x86\x08\xf7\x49\xd5\x58\x0d\x12\xf2\xd8\xda\x16\x88\x75\x1e\x96\x8e\x4d\x9b\xd5\xfc\x6a\xd8\x71\x1c\x75\xfc\x11\x29\x55\xfa\xa1\x8f\xba\x3b\xbb\x8b\x9e\xe8\xda\x63\x4b\x23\xa0\xe8\xfa\xa3\xba\xc1\xb6\xb4\x2b\x3a\x3d\x34\xfc\x43\xa5\xdf\xf5\x47\x61\x7c\xed\xe8\x80\xb5\xbc\xd6\x1e\x77\x8e\xc2\x96\x40\x8d\xa3\x39\xbf\x12\x0d\xe7\x83\x92\xf8\x7b\x0f\x8a\x55\x1e\x9f\x6f\x33\x86\x55\xb4\x33\xfb\x2a\x03\x5c\x6d\x3b\x75\xbf\xb3\xe2\xfb\xfd\xaa\xa9\xa4\x83\x99\xd0\x99\xe0\x9d\x94\x80\x54\xb2\xa9\x12\x73\x87\x09\xfc\x86\xe0\xbc\xb0\x3e\xe8\xd0\x3a\x41\xbb\xa9\x98\x2b\x0f\xbc\x01\xf4\x40\xe8\x0c\xcc\x02\xad\x95\xb4\x2e\x7b\xb8\x45\x65\x1e\x40\xe6\xa0\x11\x33\xda\xa9\x5b\x61\x0e\x3d\xd6\xad\x3a\x2c\x0e\x2d\xdc\x9d\x09\x3f\x4d\x3e\x89\xc7\x91\xf6\xff\x39\x8e\xbf\x7f\x28\xd4\x5e\x82\xd5\x30\x16\xd6\x9e\xa4\x5a\x22\xe2\xdd\x17\x75\x06\x61\xdd\xee\xef\x87\x87\xa7\x5f\x88\x70\x3f\xa9\xd1\xf1\xd2\xce\x64\x98\xa0\x46\x2b\xbc\x34\x9a\x43\xc4\x52\x26\x07\x01\x13\xb9\x40\x0d\x98\x4d\x30\x01\xde\xdd\xbf\xb5\xba\xb3\x75\xde\xdf\x3b\xcb\xe5\x01\xbc\xd3\xbc\xbf\x53\xb3\x32\x02\x72\xc7\xbb\xd8\x43\x15\xf2\x16\x80\xdc\x9a\x59\xe5\x21\xe8\x62\x7b\xf7\xbf\xc8\xb8\x38\x57\x66\x08\x10\x99\xa1\x0c\x80\x37\x8c\x7f\x62\x69\x8a\x13\x97\xe1\x7b\xb3\x66\xaf\xfd\x19\x50\x9b\xfd\xba\x8a\x79\xd4\x71\x1e\x8b\xb5\xed\xee\x12\x1f\xc6\x1e\x0b\x5a\xdd\x57\x4f\x21\xf5\x26\xa5\x4b\x6f\xbf\xae\xb0\x45\x0f\x84\x8d\x77\x6e\x57\xa3\x56\x73\x2b\xee\xb5\x7d\x5d\x19\xf6\x84\xe1\x71\xdd\xed\x6e\x9b\xd9\xa2\xae\x3b\x5e\x37\x4e\x11\xed\x36\xa7\xa0\xf4\x15\x15\x2b\x36\x28\x31\x19\xb9\x91\x5e\xa0\x75\x2b\xda\xd6\x05\x31\xe0\xd9\x7c\xca\x29\xe8\x32\x27\xf6\xa7\xe3\x4f\x70\xc0\x5f\x3a\x44\xdd\x69\xe1\xcb\x4f\x2d\xf5\x24\x49\x6a\x03\xa8\x1c\xbe\xa4\x1b\x06\x56\x4b\xbf\x51\xd6\x59\xa5\x4b\x57\x8f\xa3\x0e\x57\x1a\x4f\xd0\x56\x96\xc7\xe8\x2f\x51\x4e\xa6\xb7\xc6\xba\x17\xdf\x83\x1e\x50\x95\xc4\xcf\xf4\x16\xd5\xf0\xcb\xbd\x25\x42\x3b\xb5\xea\xbe\x69\x33\xfe\x06\x79\x45\x9b\x91\xd2\xdf\xb1\xcd\x38\x1b\x32\xdb\x35\xf2\xea\x72\xe1\x76\x19\xb9\xb1\xb7\xf4\x31\x57\x96\x32\xeb\xc6\x14\x6d\xc5\x25\x38\x3a\x5f\x45\xfe\xc7\x35\xac\xcc\xfe\x69\xcc\x1f\xdf\x98\x7f\xa6\x2b\xff\x08\x00\x00\xff\xff\xe6\xf1\xdb\x78\x24\x13\x00\x00") +var _templateDialectSqlQueryTmpl = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x5a\x5b\x6f\xdb\x48\xb2\x7e\xa6\x7e\x45\x0d\xe1\x33\x10\x0d\x99\xb6\x73\x0e\x0e\xb0\x0a\xbc\x80\x37\x8e\x01\x21\x33\x99\xec\x38\x33\xf3\x60\x08\x3b\x34\x59\x94\x1b\xa2\x9a\x0c\xd9\x72\x1c\x28\xfc\xef\x8b\xaa\xbe\xb0\x29\x51\xb2\x93\xb9\xec\x2e\xb0\x0f\x89\xa5\xee\xea\xae\xea\xba\x7c\x5d\x55\xad\xcd\xe6\xf4\x78\xf4\xaa\xac\x3e\xd5\x62\x71\xaf\xe0\xc5\xd9\xf9\x5f\x4e\xaa\x1a\x1b\x94\x0a\xae\x93\x14\xef\xca\x72\x09\x33\x99\xc6\x70\x59\x14\xc0\x44\x0d\xd0\x7c\xfd\x80\x59\x3c\x7a\x7f\x2f\x1a\x68\xca\x75\x9d\x22\xa4\x65\x86\x20\x1a\x28\x44\x8a\xb2\xc1\x0c\xd6\x32\xc3\x1a\xd4\x3d\xc2\x65\x95\xa4\xf7\x08\x2f\xe2\x33\x3b\x0b\x79\xb9\x96\xd9\x48\x48\x9e\xff\x6e\xf6\xea\xf5\xdb\x9b\xd7\x90\x8b\x02\xc1\x8c\xd5\x65\xa9\x20\x13\x35\xa6\xaa\xac\x3f\x41\x99\x83\xf2\x98\xa9\x1a\x31\x1e\x1d\x9f\xb6\xed\x68\x44\x67\x80\xcb\x2c\x13\x4a\x94\x32\x29\x20\x17\x58\x64\x0d\xe4\xa5\x66\x7e\xb7\x16\x45\x86\x75\x0c\x4c\xbd\xd9\x40\x86\xb9\x90\x08\x61\x26\x92\x02\x53\x75\xda\x7c\x28\x4e\x3f\xac\xb1\xfe\x74\xaa\x57\x86\xd0\xb6\xa3\x60\xb3\x39\x81\x8f\x42\xdd\xc3\x51\x7c\x5d\xd6\x28\x16\xf2\x0d\x7e\x6a\x78\x2a\xa0\xf1\xeb\x37\x0d\xdc\x95\x65\xa1\x29\x51\x66\xc0\xbb\xbb\x8f\x07\x39\x85\x9a\x18\x8e\xaa\xe5\x02\xa6\x17\x70\x14\xdf\xa4\x65\x85\xf1\xbb\x24\x5d\x26\x0b\xb4\xb3\x46\x74\xa2\xa8\x92\x26\x4d\x0a\x47\xf8\x37\x33\x63\x08\x6b\x4c\x51\x3c\x68\x4a\xf7\xd9\x2d\x27\x69\xf2\xb5\x4c\x61\xdc\xa3\x6d\x5b\x38\xf6\xb9\xb4\x6d\x04\xcd\x87\xe2\xb2\x28\xc6\xa9\x7a\x84\xb4\x94\x0a\x1f\x55\xfc\x4a\xff\x8d\x60\x7c\x3b\x67\xfa\xf8\x6d\xb2\x22\x11\x27\x80\x75\x5d\xd6\x11\x6c\x46\xc1\x43\x52\xc3\x78\x14\x04\xb2\xcc\xb0\x81\x2d\xc2\x51\x70\x50\x9b\x4e\x9d\x17\xb0\x25\x5f\x6c\x66\xcc\x06\x46\xb3\x41\xd0\x54\x98\x0e\x50\xb3\x6a\x6f\x2a\x4c\xc7\xd1\x28\x88\x0e\x9b\xb0\x37\xf9\xe6\x75\xb6\x40\x3b\x11\x88\x9c\x76\xae\x13\xb9\x40\x38\x12\x13\x38\x42\x52\x6b\x0c\x6d\xbb\xd9\x80\xc8\x61\xa1\xe0\x48\xc0\x19\xe9\xef\xf3\x67\x22\xd5\x72\x0d\xc9\xbe\xd9\x38\xc3\xa1\xd5\x06\x7c\x73\x01\x52\x14\x6e\x1d\xa9\x2f\xe8\x69\x41\xd5\x6b\xe4\x31\x27\xa8\x3b\x39\x09\x67\x09\xf5\x3a\xd2\x45\xfc\xb6\xcc\x30\x7e\x55\x16\xeb\x95\xa4\x0d\x92\xaa\x42\x99\x8d\x77\xa6\x26\xac\x32\xcf\xcb\x7c\xbd\xc4\x71\x1c\x8d\x34\x53\x9f\x25\x6f\x72\x93\x26\xf2\xe7\xa4\x58\x23\xed\x4e\xae\x34\x8e\xe0\x76\x2e\xa4\xc2\x3a\x4f\x52\xdc\xe8\x43\x90\xf1\x49\x55\xdf\xfa\xc6\xdf\xa4\xa5\xcc\xc5\x62\xba\x63\x2d\x3d\xde\x3a\xa7\x71\x62\xf3\xd7\x09\xd0\x1f\x12\xe8\x41\xf3\x9d\x5e\xf0\x48\xdc\x38\x51\xc8\xca\x87\x6d\xbc\xa3\xac\x07\x7b\x06\xc3\x4a\x7f\xd7\xbc\xe2\x7c\x69\xf7\xf5\x54\xd1\x57\x7f\x8d\x6a\x5d\x4b\xd0\xcb\x46\x81\x55\xcf\x65\xd3\x88\x85\xb4\xaa\x31\x4c\xe2\x38\xf6\x14\x14\xe9\x60\x61\x39\x44\x0e\x05\x4a\x7d\xce\x08\x2e\x2e\xe0\x4c\x8b\x67\x76\xcf\x57\x2a\x7e\x4d\xc4\xf9\x38\xb4\x18\xd1\xb6\x53\x30\x5c\xd2\xa4\x28\x30\xe3\x83\x95\x6b\xc5\x5f\x85\x5c\x40\x67\xa2\x90\x64\x6f\x3d\x7b\x30\xa3\xdb\x8e\xe5\xc9\xf9\xbc\x3b\x0b\x1f\x3d\xe1\xad\xcd\xf1\xb5\xfc\x5a\x07\xed\x88\xa4\xc5\x9a\x91\xa5\xf9\x50\x2c\xea\xa4\xba\x8f\xff\x4e\x81\x46\x8e\xd5\x10\x52\x4c\x76\x4c\x9b\xd5\xf4\x69\x02\xa4\x9c\xe8\x25\x2f\xd7\x4e\xcf\xe7\xb4\x8c\x45\xc1\x08\x32\xb2\x1e\x67\x22\x0e\x35\x20\x7a\x21\xe9\x6c\x8c\x8f\x8a\x4c\x71\x04\xe1\x8f\x98\x86\x1e\xcf\x90\xa8\x43\x5a\x6b\x83\x1b\x14\xae\xaa\x22\x51\x83\x38\x8f\xc9\x02\xeb\xa2\x4c\x32\x21\x17\x21\x47\xf6\x96\xa1\xfd\xcf\x9e\x9e\xc8\x53\x44\x31\xfa\x22\x38\x7d\x55\xae\xa5\xda\x03\xa8\x42\x2a\x1f\x44\x19\xd6\xa6\x4f\xe0\x9a\x11\xc7\xd9\x82\xf7\x7f\xae\x2d\xbe\x4c\xf4\xd7\x8f\xa2\xd9\x27\x3a\x5d\x7d\xbe\xec\x72\x62\xbd\x64\x5b\x00\x5f\x07\x91\x73\xa7\x5d\x7f\xc8\x93\xa2\xc1\xc9\x5e\xef\x4f\xef\x31\x5d\x02\x92\x48\x28\x53\x9c\xc2\xff\x3c\x84\xcc\x53\x7b\xa9\xb5\x12\xfc\x15\xce\xbe\xd4\x4a\x9e\x7e\xe1\xb8\xef\xe4\x34\xea\x9b\xe6\xdb\xdd\x69\x3a\x02\xe9\x7f\xea\x4d\xd2\x77\x3b\x17\xbc\x4f\xee\x0a\x9c\xee\x40\x2f\x0f\x4f\x88\xc0\xa0\xf3\x2e\x89\x85\x6d\x22\x9a\x5d\xf9\x0c\xae\x29\x53\x71\x1c\x82\xf7\x9f\x2a\x9c\xea\xc4\x27\xe6\x4d\x66\x57\x31\x8d\x91\xc1\x1a\x65\xef\x6b\x26\xd5\x7b\xee\xf2\xb2\xcb\x78\x45\x22\x95\x5d\xc0\xff\xf3\x7f\xd7\x75\xb9\xda\x85\xf1\xe6\x43\x41\x93\x3f\x49\xf1\x61\x8d\x53\xbe\xbb\x26\x16\x37\xaa\x66\xc8\x21\xaa\x1a\x33\x91\x26\x0a\x9b\x97\x8c\x83\x55\x13\x91\xd5\xd8\x17\x18\x4e\xdf\x59\x02\x8b\xa8\x0d\x16\x9c\x06\xb2\x75\xe2\x1b\xf3\x2d\xd2\xa8\x49\x79\x9e\xe0\xc4\x87\xf1\xa3\xb2\x58\x5f\x35\xb7\x62\xee\x96\x3a\x3c\x6f\xad\x70\x85\x58\x09\x35\x24\x1f\x4f\xbc\x34\xf3\x9e\x9f\xb2\x6c\xdf\xf1\xe8\x05\x1c\xf3\xb4\xdd\xab\xcc\xf3\x06\x07\x37\xd3\x33\x2f\x2d\xc5\xf6\x76\x3f\xe8\xe1\x0b\x38\xd6\x04\x87\x35\x57\xd6\x19\xd6\x7b\x94\xf6\x03\xcd\xfd\x71\x0a\xb3\xb0\x53\x61\xfa\x65\x20\xc2\x71\x62\x02\xcb\x49\x42\x1c\xbd\xe4\x96\xa6\xae\x34\x4a\x6f\xef\x69\xf0\xcb\x4d\x47\xd1\x28\x50\xe7\xb4\xc8\xe6\xf5\x1c\x47\xe3\xc1\xe8\x8a\x46\x81\xd3\x84\xb7\x42\x4b\x31\x56\xe7\x36\xc0\x76\x56\x9b\x71\xba\x03\xf9\x1f\xb9\xfe\x58\x9d\x6b\xf8\x1a\x88\x80\x9e\x5d\x2d\xc7\x41\x28\xf4\x08\xac\x1c\xee\xfb\x33\xa5\x61\x7b\x90\x11\xff\x31\x81\xaa\xb3\xe3\xfe\x30\x63\xb1\x2a\xdf\xb2\xcf\xda\x80\xbd\x6d\x70\xed\x57\x7a\xfc\xe9\xa9\x09\x2a\xd1\xc0\x2a\x91\x59\xc2\x95\x1d\x09\x62\x68\xd3\x22\x59\x37\x18\xc3\x2f\x08\x8d\x4a\x6a\xa5\xd7\xf0\xd5\x9f\x61\x9e\xac\x0b\xa5\x53\xaf\x09\x24\x32\x83\xf2\x01\xeb\x5a\x50\xd1\xa9\xe0\x0e\x8b\xf2\x23\x25\xe7\x12\x31\xa3\xca\xd4\x53\xb3\x8e\xb1\xb1\x89\xb0\x48\x87\xf0\x78\x95\xa8\xfb\xf8\xfb\xe4\x71\x26\xd5\xff\xbe\x88\xbe\x1e\x14\x2c\x17\xbd\xab\x86\x85\xde\x95\x64\x29\x46\x5c\xaa\x75\xd5\xe1\xe9\xb1\xbe\x78\x4e\xab\x44\x9f\x4f\x48\x6c\xb8\x62\xe5\x61\x58\xa0\xc4\x3a\xa1\x9a\x96\x55\xc4\x54\x65\x0e\x09\x2c\xc4\x03\x4a\xc0\x6c\x81\xcf\xa9\x69\x69\x5d\x57\xd1\x1e\x49\xce\xae\xb8\xec\x20\x09\x88\x1d\xa7\x89\x1f\x8d\xca\x3d\x01\xf2\xba\x5c\x19\x0e\x7a\x2d\xfa\xa5\x2a\x65\x5c\xbd\x6d\x48\x20\xda\x86\x2c\x00\xaa\x64\xf9\x17\x35\xa1\x38\xcd\xb2\xf8\xaa\xec\xed\xe7\x57\xad\x76\xdb\x1f\x3b\x9d\x8f\x82\x46\x61\xd5\x4b\x3c\xdf\xe2\xc7\x1b\x85\x15\x55\x9a\xdd\x55\x48\xb1\x49\xe6\x92\xbb\xb7\x2b\xec\x8c\xeb\x81\xad\x7b\x6e\x28\x50\x0d\x6e\x45\x13\x9f\xd7\xfb\x92\x39\xa1\xbe\x5c\x87\xd9\xed\x4e\x7a\xa3\x7d\xc6\xfd\xcd\x49\xa3\x63\xf7\x4d\x2f\xfa\x11\x0b\x5e\xe8\xa4\xc4\x78\xd6\xcc\xe4\x03\xd6\x4d\x37\xb6\x73\x40\xd4\xf2\x6c\x5f\xe5\xa4\x74\x91\xd3\xf4\xf7\x2f\xbe\x87\x13\x53\xee\xee\xd9\xe1\xdd\x1b\x6f\x79\x1c\xc7\xae\x14\x2d\x1a\x7c\x6a\xad\x06\x2c\x6f\xbd\x5f\xc7\xea\xb5\x74\xf4\x68\x14\xb0\xa7\x31\x82\x7a\x56\xbe\x41\xf5\x16\xc5\xe2\xfe\xae\xac\x9b\x27\xef\x83\x09\x90\x97\x44\x7b\x62\x8b\x7c\xf8\xe9\xd8\x4a\x74\x38\x79\x7e\xef\xc2\x8c\xcb\xa3\xe7\xb4\x8e\xea\x72\xf5\x9f\x18\x66\x6c\x0d\x91\x0d\x41\x9e\x75\x17\x0e\x97\x59\x73\xa3\x6a\xaa\x33\xdb\x56\x64\xe3\x88\xb4\x5d\xb0\x0b\xce\xae\x3a\xcd\xff\x79\x01\x2b\xb2\xff\x06\xe6\x9f\x1f\x98\xbf\x31\x2a\x0f\x84\x4f\xbf\x22\x3f\x18\x0a\x87\xbd\x9a\x09\x44\x6e\x62\x6b\xc0\xab\xf7\xb5\xe5\x5e\x9a\x25\xde\xdd\xde\x37\x8b\x56\x56\xbe\xe4\xcc\x7c\x95\x2c\x71\x7c\x3b\x37\xc7\xfe\x59\x27\x25\x67\x13\xaf\xaf\xc3\xf9\xb3\xc8\x3a\xea\x55\x52\xdd\xfa\xb5\x19\xb4\xed\x76\x3f\x75\x6b\xb5\x49\xd1\x6c\x1b\x47\x67\x69\xba\x59\xa6\x33\x76\x91\x35\xb7\x0c\x50\xb3\xab\x39\xe8\x3e\x0f\x8f\x93\x90\xae\xc7\x95\x2f\x6d\x83\x6b\x76\xe5\x92\x7a\xd7\xb0\x0d\x02\x02\x14\x92\xf3\x76\xde\x0f\x07\x23\xa3\xa3\xa1\x2d\x7b\x07\xd9\x21\x9d\x6f\xf5\x7d\x99\x1b\xff\xb7\x5b\x3e\x93\x31\x7b\x25\x74\x10\xd0\xd0\x74\x8b\xa4\x9b\x0d\x4c\x70\x4d\x87\xa2\x4d\x53\xec\x29\xb4\x0f\x04\xde\x81\xda\x7b\x20\xd8\xf4\x12\xf3\xc7\x15\xa9\x53\x53\x72\x0d\xd6\x5a\x41\xd0\xc4\xbf\xdc\x63\xcd\xf8\x11\xcf\x6c\x8b\xed\x19\xcc\x6e\x75\xb3\x79\xeb\xa4\xe7\x1d\xf0\x9e\xb9\xd8\x9a\x4f\x20\x5f\x72\x79\x10\xf9\x12\xd2\xa6\xe5\x9a\xb3\xd9\x90\xb8\xbf\x5d\x17\xc5\x4c\xaa\xff\xff\xbf\xd0\xb5\xb2\xd9\x19\x7f\x6a\xb0\xbe\xe2\xc8\xb4\x6d\x6c\x5a\x75\xa1\x27\x69\x91\x31\xaf\x87\xf3\x66\x77\x21\x0f\x6e\xde\x39\xc8\x2e\x0b\x21\x89\x43\x47\xb1\x97\x4f\xd7\xd7\x9c\xba\xce\xf3\x0b\xbf\xf5\x6c\xf4\x6c\xb2\xed\xad\xb9\x6f\xed\x71\xda\x76\xd3\x4e\x74\x67\x5a\x48\xfe\xd6\xfa\xba\xd2\xbd\x55\xc3\xa1\x5c\xab\x09\x08\x09\x7b\xda\xb7\x14\x0f\x4c\x52\x2e\xe9\xf8\xe5\x5a\xc5\xe3\xe3\x8e\x8f\xb6\x01\x61\xd0\x37\xe5\x12\x3e\x7f\x06\x64\x75\x76\xb0\x12\x0c\xb7\x7a\xd7\x12\x1f\x2b\x4c\x15\x66\x20\x32\x5d\xe7\x70\x72\x42\xb1\x77\x52\xae\x55\x68\x36\x6e\x8d\x08\x42\x5a\x09\x84\x34\x02\xf0\xc9\x76\xf9\x93\xae\x7f\x1b\x7b\x21\xb7\xb8\x97\x6b\xc5\x46\x31\x08\xbb\xd5\x9d\xbd\xac\x17\x21\x84\x74\xee\x10\x42\x6e\x55\x85\xec\x4d\x10\x5a\x33\x87\xce\x2a\xcf\xef\xd4\x9e\xae\x5e\xac\x74\xa3\x3a\xb4\xaf\x31\x9e\x9f\x04\x42\x3e\x2d\x91\x90\x9e\x40\xce\xf9\x7a\x62\x69\xef\xf8\xdd\xa4\x22\xe0\x75\x76\xca\x9a\x5b\xab\xb8\x79\xcf\x4a\xcf\xb3\x0b\x5f\x04\x22\x23\xd7\x64\x40\x36\x3d\x50\xbb\xe5\x96\x7f\x18\x58\x77\xf7\x80\x19\x20\xcf\xf6\xc9\x79\xa7\x5b\x33\x36\xef\x93\x77\xe3\xdd\x73\x4c\xe0\x37\xf0\xbd\x10\xb2\x0f\x2e\x83\x6f\x05\xdc\xcc\xff\x9a\xb7\x82\xfe\x6b\x81\xa7\x97\x5f\xf5\x6d\xad\x2f\xa6\x50\xe3\xa7\xb9\x76\x42\xd2\xcb\xaf\xb6\x37\x6c\x24\xd3\xf9\x8c\x86\xe2\xe1\x64\x70\x76\x35\x93\x56\x49\x0e\x4b\xa5\xcd\x78\x5c\x7b\x5b\x6f\x64\x1e\x49\x23\xef\xd0\x7b\xa5\xe6\x37\x0e\x23\x86\xbd\xd2\xbd\xfb\xdc\x72\x30\x2b\xcd\x3b\x83\xf6\x18\x6d\x04\x4a\x7f\xe7\xa3\x5d\x77\xd9\xa7\x1a\xcf\x65\xb6\x34\xa3\x5d\x48\xaf\xc3\x4c\xab\x49\xda\xbc\xc0\x78\xce\x56\x7b\xd0\xcf\x37\xb4\x70\xb7\x62\xae\x5f\x67\xcc\xb5\x75\xa3\xea\x75\xaa\x38\xaa\xa8\x52\xe9\xbf\xe3\x1d\x26\x9e\x80\xf4\x58\xbb\x57\x18\xba\xe0\xf4\x05\xf2\xc3\x47\x79\xfd\xc6\xbe\xe6\x65\x7e\xea\x35\x98\x81\x0c\xe5\x60\xf4\x71\x28\x0f\x7b\x5e\xfa\x72\x40\x1b\x22\x87\x7c\xd9\xbd\xaf\x89\x79\xff\x88\x6f\xec\x21\x5f\x12\x59\xcf\x3b\x82\x5e\x60\x72\x50\x1e\xe7\xcb\xa8\xd3\x31\x21\xc5\x71\xbe\x9c\xf7\x95\x69\x47\x27\x8e\xe3\x96\xf2\x9e\xeb\xe5\xff\x46\x1e\x6e\xcf\xf5\x1b\x7c\x3c\xd7\xef\xbe\x27\x4b\xfc\x64\xfd\x7d\xdb\x04\xe1\x1f\xee\xf3\x72\x8f\x1b\x7f\x4d\xd5\xb0\xcf\x63\xf7\x56\x0e\x4f\x79\x2a\x49\x73\x7a\x0c\xaf\x4a\xca\x23\x15\x34\x5c\xc9\x9f\x10\x0b\x75\x9f\x28\x48\x6a\x84\x46\x95\x35\x66\x90\x34\x94\xf5\xd8\xdf\xe0\x64\x89\x4a\xee\x92\x06\xe1\xf8\xd4\xab\x2c\xb9\x28\x4a\x64\x06\x63\x59\xaa\x9d\x14\x32\xd2\x23\x5e\xbf\xc0\xba\xbc\x73\xb0\x46\xd5\x69\x29\x1f\xe2\x4b\x55\x8a\x0e\x25\xac\x45\x06\xbd\x6d\xd0\xdf\xdc\x5d\xb7\x5b\xef\x88\x2c\x72\xd2\x7a\x76\xd8\x57\x1a\xf5\x04\xe8\xff\xe0\xc2\xc5\xa3\x47\x67\x8b\x2d\xfa\xba\x15\x7b\xbb\xbf\xe2\xf0\x63\xd2\xf5\xe4\xfd\xd6\x83\x31\xe2\xf8\x50\x19\xf1\x05\x55\xc4\x4e\x8d\xdf\xaf\x0e\xda\x7f\x55\xd8\x1b\xac\xdc\x03\x92\x1e\xa2\xf6\x73\xd5\x7d\x00\xf0\xac\xa8\x17\x0d\x6f\x45\xc2\xf1\xcd\x37\x18\xfc\x7e\x8a\xe6\xc3\xec\x9f\x83\x46\x5b\xc2\x1d\xe7\xcb\x61\x09\x0f\xc3\x8f\xab\xb8\xf4\x3b\x2c\xb4\xad\xec\x2a\x45\xef\x0a\x79\xe2\x2e\xee\x25\xaf\xdb\x3f\x8a\xd8\x7a\xbf\xf8\x7d\x20\xe5\xd9\x5d\x21\x3f\xcf\x76\x4d\xa0\xa4\xee\xfd\x7c\xee\xb2\x5e\x74\x73\xfc\x1a\xee\xcf\x76\xae\xa6\x5b\xb4\xeb\xa2\x50\x84\xa6\x1e\x89\x57\x85\x8e\x76\x91\x4e\xbf\xaf\xf7\xb1\x4e\x8f\x39\xb4\xd3\x28\x69\x01\xee\xba\xac\x57\x89\x9a\x49\xfd\xaa\x99\xb0\x74\x31\x57\xc9\x13\x38\x3f\x33\xbf\x50\x63\x23\x11\xa3\xfb\xa4\x79\x57\x63\x2e\x1e\x3d\xd9\xa8\xb6\x0e\xcd\xae\xb4\x87\x66\x67\xc5\xd4\x27\x32\x85\xd6\x89\xfb\xe1\x4a\xd7\x08\xec\xd8\xee\xf9\x99\xe2\x3f\x03\x00\x00\xff\xff\xe6\xf5\x84\x4b\x00\x2a\x00\x00") func templateDialectSqlQueryTmplBytes() ([]byte, error) { return bindataRead( @@ -776,7 +776,7 @@ func templateDialectSqlQueryTmpl() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "template/dialect/sql/query.tmpl", size: 4900, mode: os.FileMode(420), modTime: time.Unix(1, 0)} + info := bindataFileInfo{name: "template/dialect/sql/query.tmpl", size: 10752, mode: os.FileMode(420), modTime: time.Unix(1, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -821,7 +821,7 @@ func templateDialectSqlUpdateTmpl() (*asset, error) { return a, nil } -var _templateEntTmpl = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x9c\x58\xdd\x6e\xe3\xba\x11\xbe\x96\x9e\x62\x8e\xe0\xed\xb1\x0d\x87\xde\x9e\xbb\x6e\x9b\x02\x39\xc9\x2e\x60\xa0\xd8\xfe\x24\x8b\x5e\x2c\x16\x1b\x46\x1c\x49\x6c\x24\xd2\x4b\x52\x4e\x0c\x41\xef\x5e\x0c\x29\xc9\x92\xed\x64\xd3\x5e\x45\x26\x87\xdf\x0c\xbf\x19\xce\x4f\x9a\x66\xbd\x8c\xaf\xf5\x76\x6f\x64\x5e\x38\xf8\xed\xfd\x1f\xff\x74\xb1\x35\x68\x51\x39\xf8\xc4\x53\x7c\xd0\xfa\x11\x36\x2a\x65\x70\x55\x96\xe0\x85\x2c\xd0\xbe\xd9\xa1\x60\xf1\x5d\x21\x2d\x58\x5d\x9b\x14\x21\xd5\x02\x41\x5a\x28\x65\x8a\xca\xa2\x80\x5a\x09\x34\xe0\x0a\x84\xab\x2d\x4f\x0b\x84\xdf\xd8\xfb\x7e\x17\x32\x5d\x2b\x11\x4b\xe5\xf7\xff\xb6\xb9\xfe\xf8\xf9\xf6\x23\x64\xb2\x44\xe8\xd6\x8c\xd6\x0e\x84\x34\x98\x3a\x6d\xf6\xa0\x33\x70\x23\x65\xce\x20\xb2\x78\xb9\x6e\xdb\x38\x6e\x1a\x10\x98\x49\x85\x90\x54\x5a\x60\x99\x40\xb7\x3a\xdb\x3e\xe6\xf0\xe1\x12\x1e\xb8\x45\x98\xb1\x6b\xad\x32\x99\xb3\x7f\xf0\xf4\x91\xe7\x48\x42\x4d\x03\x0e\xab\x6d\xc9\x1d\x42\x52\x20\x17\x68\x12\x98\xf5\xc7\x0f\x5b\xb2\xda\x6a\xe3\xfa\xad\xf5\x1a\x08\x9c\x7d\xe6\x15\xa1\xd0\x9d\xc9\x60\xaf\x1b\x50\x39\xe9\xf6\x90\xe9\x70\xf3\x89\xa0\x4d\x0b\xac\x38\x8b\xdd\x7e\x7b\xbc\xe3\x4c\x9d\x3a\x68\xe2\x28\xf5\x46\xc2\x44\xbd\x47\x5e\xeb\x4a\x3a\xc7\x73\xdb\x99\x11\xad\xd7\xb0\xb9\x09\xbc\x20\xa9\x65\x71\xb4\xb9\x09\xb0\x9b\x1b\x76\x47\x3a\xda\x16\xee\xff\x63\xb5\xfa\x90\x48\xb1\xa2\xf3\x58\x6d\xdd\x3e\xb9\x8f\xa3\xa6\x01\xc3\x55\x8e\x30\xfb\xbe\x82\x59\x46\x2c\xcd\xd8\x27\x89\xa5\xb0\x70\x41\xe8\x51\x77\xcb\x8c\xdd\x7a\xdb\xfc\x1e\x01\x16\x9a\x64\x48\xe7\x8e\x97\x35\xf6\x06\x24\x41\xb8\xbb\x50\x02\x19\xc9\xb3\x18\x00\x20\x3a\x8b\xd3\x34\x20\x33\x7f\x44\x96\x25\x7f\x28\xe9\xd8\xb2\x69\x00\x15\x6d\x87\x23\xfd\x25\x82\xac\xd2\xce\xe3\xa0\xb2\xd2\xc9\x1d\xed\xdc\x8f\xa1\xef\x78\xde\x2d\x61\x69\x31\x80\xbc\x4e\xe2\xa0\xce\x13\x42\x9f\x17\xdd\xf7\x88\x1c\x0c\xe4\x7c\x14\x39\xf6\xdc\xd0\xab\x01\x99\x2b\x6d\x10\x72\x54\x68\xb8\x93\x2a\x07\x14\x39\x86\x8b\x5b\xf0\xa1\x49\x92\x17\xf0\x24\x5d\x01\x33\x1c\x59\x19\x50\x7a\x8a\xf1\x8d\x14\x07\xd9\x3e\x64\x48\x19\x83\xbb\x41\xc8\xa2\x03\xa7\x41\xc9\x72\x05\x5c\x09\xb0\x85\xae\x4b\x01\x0f\x08\xf5\x56\x70\x87\x02\x2a\xae\x6a\x5e\x96\x7b\x46\xba\xcf\x2a\x1e\xf1\x8c\xec\x8b\x92\x3f\x6a\x52\xf5\xf5\xdb\xc0\xd3\x32\x1c\x23\xbf\x0c\x86\xdc\x8f\xa1\x3a\x1f\xf8\x9b\x4f\x08\x1d\x7f\x1f\x3b\x25\x50\xb6\xe6\x42\x48\x27\xb5\xe2\x65\x17\xe2\xdd\x2b\x76\xd5\xb6\x24\x1f\x6c\x8d\x54\x2e\x83\x44\x48\x5e\x62\xea\xd6\xef\xec\x5a\x20\x65\x9d\xb5\x56\x98\xc0\x8c\xdd\x3a\x6d\x0e\x2f\xfb\x79\x50\x13\x10\x86\x77\x3d\x33\x98\xa2\xdc\xa1\x09\x8e\xfd\x57\xff\xab\xdb\x7e\xc9\xf5\x9d\xf1\xb3\xac\x56\xe9\x60\x0e\x24\xff\xac\xd1\xec\x93\x53\x32\xe3\xe1\x05\x91\x7c\xdb\xc2\x8f\x1a\x8d\x44\xfb\x82\x2b\xc7\x4e\xee\x37\x58\x1c\xf9\xc3\xf3\x89\xd1\x6d\x0b\xcb\xb1\xd4\x62\xac\x65\xbe\x80\x63\x1f\xb5\xad\x37\x91\x92\x4b\x64\xd0\xd5\x46\xc1\xfc\x0f\x63\x80\xeb\x52\xa2\x72\x0d\x1c\x69\x61\x21\x15\xb5\x0b\x36\xc6\x3f\x12\x5a\xc4\x91\x27\xbb\x7b\x46\x94\x1b\xbf\xf8\x70\x83\xa0\xca\x02\x87\x87\x5a\x96\x54\x09\x28\x27\xfa\x58\xa4\xb7\xe2\x93\xf9\xf4\xb2\xeb\x35\x7c\xd6\x0e\xc1\x15\xdc\xad\x60\xaf\x6b\x50\x88\x82\x82\x3a\xe5\x65\x39\x15\xfe\xa2\x9e\x0c\xdf\xce\x17\xf0\x80\x19\xbd\x42\x92\x18\x60\x2b\x74\x85\x16\x2b\x8a\xe5\x13\x35\xa4\xe5\x89\xdb\xce\x3c\x14\x90\x19\x5d\x01\x07\x67\xb8\xb2\x3c\xa5\xf8\x0b\xef\x87\x9c\x31\x5a\xf4\x87\x52\x5d\x51\xfe\x40\x01\xda\x80\xd1\x65\x89\x02\x1e\x78\xfa\xc8\xe2\x37\xf9\x29\x30\xd3\xbb\xa8\x5f\x0f\xab\x7f\x57\x48\x1e\xfa\xff\x1c\x34\x40\x9c\xba\xa7\xf3\x89\xa7\x0b\x6a\xff\xc7\xf6\x15\x83\x0a\x15\x91\xfd\x33\x42\x80\x67\x0e\x0d\xc8\x20\x98\x96\xda\xa2\x58\x11\xac\xd5\xe1\x3c\xb9\x47\xe1\xb3\x1b\x62\xfc\x49\x96\x25\xa5\x1e\x7c\xc6\xb4\x26\xbe\x5c\x61\x74\x9d\x17\x5e\xb3\x30\xde\xba\xa7\x42\xa6\x05\xa4\x06\x79\x10\x98\xd0\xfd\x56\x46\xfb\x30\x98\xac\x13\x91\xee\x79\x05\xfa\x91\xde\xe9\x79\xd6\x58\xb0\x82\xcd\x97\xee\xf9\xc6\x7f\x2e\xe2\x48\x66\xf0\x8b\x7e\xf4\x2f\x65\xcb\x95\x4c\xe7\x49\xdf\x45\xb4\xed\x87\x93\xa2\x4f\x79\x72\xc2\x13\xef\xcb\x7f\xe2\x9f\x45\xf4\xaa\x66\xb8\x04\xf7\xcc\x84\xd9\x0d\x4e\x3f\x12\xef\x5c\x77\xeb\x0c\x05\xb6\xac\xb6\x25\x56\xa8\x5c\xf0\x5e\x56\x39\x16\x76\xd0\xbc\x91\xab\x20\x3e\x5f\x50\xb3\x41\x88\x4d\x1c\xed\xb8\x19\x5e\x67\x58\xb5\xec\xf7\xf0\x3b\x8e\xba\x0d\xf6\x6f\x23\x1d\x76\x87\x93\x31\xe4\x9c\xae\x79\x4e\xca\x1b\x17\xb2\xf5\x3c\x91\xe2\xf2\xdd\x2e\x59\x9d\xb8\x61\x73\xb3\x58\xc4\xbe\x3e\x76\x09\x57\x9e\x34\x22\x43\x05\x0d\x8d\xc2\xb8\xf0\x53\x09\x3b\x6b\xe0\x0a\x26\x8d\xc8\xe5\x5f\x6c\x7f\xea\xaf\x64\xae\x87\xeb\xda\x83\x50\x06\x2f\x60\x66\xb3\x71\x79\x79\x67\xd9\x3b\xea\x0f\x06\x63\x4f\x7a\x97\xfe\xe0\x49\xff\x42\x1b\x14\x44\xbb\x3e\xee\x6c\x06\x6d\xfb\x67\xd8\xc1\x2f\x97\x54\x99\x7d\x64\xbd\xd5\x72\x6f\xee\x44\xd3\xc6\xde\xc9\x6a\xd0\x73\x1e\x66\xc7\x3e\x69\x53\x71\x37\x77\xb2\x42\x76\xf5\xf9\x76\x73\xbd\x18\x01\xf9\xab\xf7\x68\x5d\x68\xbd\x8a\xb7\xdc\x1d\x9f\x7e\x55\x7c\xe2\x7a\xef\xf7\xe5\x6e\xa2\x5f\xf5\x04\x46\x03\x8d\x63\xd4\xff\x85\x99\x17\x89\x39\x07\x32\x78\xe3\x45\x7e\x7e\x46\xcf\xab\xa8\x47\x10\xaf\x9d\x39\xa5\xe8\x80\x32\x82\x51\xe3\x48\x1b\x7e\x8d\x7f\x8c\xbf\x27\x8a\x7e\xdf\x3b\x9c\xff\xba\xf8\x75\x31\xa4\x96\x7e\xbb\xcf\x01\xa1\xb5\x0a\xf4\xd1\xb4\x30\xbe\xec\x7a\x0d\x52\x0c\xe5\x9b\x92\x0d\xf5\x39\x06\xbb\xb1\x90\xfb\x8a\xd0\x75\x2b\x9b\x9b\xbe\xd1\x7f\x53\x0a\x92\x62\xbe\xf0\x68\x4d\x1c\x49\xb1\x82\xef\xf4\x4e\xac\x33\xa9\x56\x3b\x76\xe5\xb4\x3c\x06\xa0\x2c\x31\x5c\x42\x8a\xb8\x8d\x47\x97\xf6\xbd\x9c\xa5\x81\xd2\x3f\xdf\xb2\x36\xbc\x9c\x16\xfa\x83\x40\xc8\xd8\x1c\xb6\xdc\x58\xff\x58\xc3\xb2\xce\x8e\x7a\x90\x61\x34\x1b\x8e\x7d\xfd\x36\xb9\x84\xd7\xea\x3b\x7a\x7c\x76\x64\xc8\x0c\x92\x5b\x92\x4d\x0e\x67\x42\xe6\xff\x79\xdb\x5a\x71\xb5\x3f\xea\x5b\xa3\x33\x8d\x2b\xeb\x1a\xda\xfe\xda\xe7\xa9\x1e\x1b\xbd\x80\x50\x6a\xe6\x69\x96\x77\x9f\x0b\xe2\x9c\x3a\xb0\xef\x92\x8c\x0a\x39\xf7\x04\x23\xf4\xed\xe3\xb5\xaf\xdf\xe5\xb7\xae\x70\xc1\x25\xa4\x59\x4e\x95\x6d\x62\x8e\x1f\x86\xae\x0e\x4d\x3d\x29\xf1\x63\x16\xc5\x53\x18\x6c\x2f\x68\xe4\x62\x61\x26\x3a\x9e\xd6\x47\x23\x99\x1f\xc8\xc2\xb4\xc4\xee\x78\x4e\x1d\xac\xed\x26\xba\x51\x8d\x70\x64\x3f\x0b\xc2\x34\xad\xd0\x32\xbc\xef\x28\x38\x4c\x8f\x8e\x6a\x76\x72\x91\x0c\x8b\xf7\xe3\xed\x97\x8c\xf7\xed\x4c\xca\x15\x35\x2f\x7a\x87\xc6\x48\x81\x02\xa4\x02\x6d\xfc\x3f\x33\x34\x70\x21\xe0\x30\xa9\xf4\xe3\x9e\xd3\xa1\xa9\xe2\x69\x01\x14\x43\xec\xfc\x5d\xcf\x4c\x3a\x64\x0e\x2a\xd1\xb6\xf1\x7f\x03\x00\x00\xff\xff\x7c\xf3\x8e\x3e\x8c\x11\x00\x00") +var _templateEntTmpl = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x9c\x58\x5f\x6f\xe3\xb8\x11\x7f\x96\x3e\xc5\x9c\xe0\xed\xd9\x86\x43\x6d\xef\xad\xdb\xa6\x40\x2e\xd9\x05\x0c\x14\xdb\x3f\xc9\xa2\x0f\x8b\xc5\x2e\x2d\x8e\x2c\x36\x12\xa9\x23\x29\x27\x86\xe0\xef\x5e\x0c\x29\xc9\x92\xed\xe4\xd2\x3e\x45\xe6\xcc\xfc\x66\x38\x33\x9c\x3f\x69\xdb\x74\x19\xdf\xea\x7a\x6f\xe4\xb6\x70\xf0\xcb\xfb\x3f\xfe\xe9\xaa\x36\x68\x51\x39\xf8\xc4\x33\xdc\x68\xfd\x08\x6b\x95\x31\xb8\x29\x4b\xf0\x4c\x16\x88\x6e\x76\x28\x58\xfc\x50\x48\x0b\x56\x37\x26\x43\xc8\xb4\x40\x90\x16\x4a\x99\xa1\xb2\x28\xa0\x51\x02\x0d\xb8\x02\xe1\xa6\xe6\x59\x81\xf0\x0b\x7b\xdf\x53\x21\xd7\x8d\x12\xb1\x54\x9e\xfe\xb7\xf5\xed\xc7\xcf\xf7\x1f\x21\x97\x25\x42\x77\x66\xb4\x76\x20\xa4\xc1\xcc\x69\xb3\x07\x9d\x83\x1b\x29\x73\x06\x91\xc5\xcb\xf4\x70\x88\xe3\xb6\x05\x81\xb9\x54\x08\x49\xa5\x05\x96\x09\x74\xa7\xb3\xfa\x71\x0b\x1f\xae\x61\xc3\x2d\xc2\x8c\xdd\x6a\x95\xcb\x2d\xfb\x07\xcf\x1e\xf9\x16\x89\xa9\x6d\xc1\x61\x55\x97\xdc\x21\x24\x05\x72\x81\x26\x81\x59\x2f\x7e\x24\xc9\xaa\xd6\xc6\xf5\xa4\x34\x05\x02\x67\x9f\x79\x45\x28\x74\x67\x32\xd8\xeb\x06\x54\x4e\xba\x3d\xe4\x3a\xdc\x7c\xc2\x68\xb3\x02\x2b\xce\x62\xb7\xaf\x4f\x29\xce\x34\x99\x83\x36\x8e\x32\x6f\x24\x4c\xd4\x7b\xe4\x54\x57\xd2\x39\xbe\xb5\x9d\x19\x51\x9a\xc2\xfa\x2e\xf8\x05\x49\x2d\x8b\xa3\xf5\x5d\x80\x5d\xdf\xb1\x07\xd2\x71\x38\xc0\x8f\xff\x58\xad\x3e\x24\x52\xac\x48\x1e\xab\xda\xed\x93\x1f\x71\xd4\xb6\x57\x60\xb8\xda\x22\xcc\xbe\xaf\x60\x96\x93\x9b\x66\xec\x93\xc4\x52\x58\x8f\x1e\x75\xb7\xcc\xd9\xbd\xb7\xcd\x93\x08\xb0\xd0\xc4\x42\x3a\x77\xbc\x6c\xb0\x37\x20\x09\xcc\xdd\x85\x12\xc8\x89\x9f\xc5\x00\x00\xd1\x45\x9c\xb6\x05\x99\x7b\x11\x59\x96\x7c\x53\x92\xd8\xb2\x6d\x01\x15\x91\x83\x48\x7f\x89\xc0\xab\xb4\xf3\x38\xa8\xac\x74\x72\x47\x94\x1f\x63\xe8\x07\xbe\xed\x8e\xb0\xb4\x18\x40\x5e\x77\xe2\xa0\x2e\x38\x64\xfc\xfd\x24\x5d\x01\x33\xf6\x51\x6c\xf1\xe8\x90\xf0\xeb\xe8\x01\x83\x25\x77\x52\x2b\x9b\xa2\xa7\x50\xd4\xb5\x2b\xd0\x80\xd2\x02\x6d\x9f\xca\x5b\xc3\xeb\x82\x05\x88\x87\xde\x71\x16\xb8\x41\xd8\xa0\x54\x5b\xa8\x75\xdd\x90\x95\x02\x36\xfb\xb3\xb4\xf9\x67\x83\x66\x0f\x4f\x05\x2a\x40\xbe\x45\x73\x55\x6a\x2e\x48\x8a\x5e\x03\x52\xd8\xa3\x60\xd7\x31\x89\xa2\x71\x7c\x31\xc4\x76\x74\x93\x21\xb8\xf8\xc6\xe0\x06\xde\x3e\x59\xe9\xae\xcc\xc3\x5c\xc4\x18\x05\x0b\xd9\x17\x25\x7f\x6b\x48\xea\xeb\xb7\xc1\xd9\xcb\x20\x46\xc1\x1d\x30\xdb\xb6\xf3\x38\x9e\x05\x93\xf5\x41\xed\xa2\x13\x4d\x62\x15\x8d\x62\x77\xd5\x07\x2f\x5d\xc2\x8d\x10\x92\x42\xc3\xcb\x90\x8a\x16\x9c\x06\x2e\x06\x0f\x5b\xa7\x0d\x55\x01\x61\xe4\x0e\x0d\x03\x5f\x4a\xbc\xf0\xcc\x55\x75\x49\x3e\xab\x8d\x54\x2e\x87\x44\x48\x5e\x62\xe6\xd2\x77\x36\x0d\x49\x14\x00\x13\x98\xb1\xfb\x0e\xa5\x97\x95\x39\x14\xdc\x3e\xf4\x49\x17\xa0\xbc\x99\x44\x7d\x76\x53\x02\xbb\x98\x79\x6f\x30\xbe\xb1\x63\x93\xcf\x92\x3c\xc8\xa4\x7c\x40\xe9\x4a\x46\x57\x15\xbd\xee\x8b\xd7\x13\x48\x45\x3c\xd5\x0a\x4f\x2e\xd7\xb6\x67\xc6\x0f\x65\x72\x66\x30\x43\x72\x62\xc8\xb3\x7f\xf5\xbf\x3a\xf2\xa8\xca\x9c\x65\xa2\x7f\xbd\x8d\xca\x06\x6f\x43\xe2\xb3\x3d\x39\x4f\xab\x78\x28\x48\xc4\x7f\x38\xc0\x6f\x0d\x1a\x89\xf6\x85\xfc\x1c\x67\x6e\x4f\x60\x71\xe4\x85\xe7\x13\xa3\x0f\x07\x58\x8e\xb9\x16\x63\x2d\xf3\x05\x9c\x66\x6b\xff\x20\xe9\x99\x19\x74\x8d\x51\x30\xff\xc3\x18\xe0\xb6\x94\xa8\x5c\x0b\x27\x5a\x58\xa8\xec\x87\x05\x1b\xe3\x9f\x30\x2d\xe2\xc8\x3b\xbb\xcb\x07\x6a\x35\x5f\x6a\x41\x4e\x0f\xaa\x2c\x70\xd8\x34\xb2\xa4\xc6\x4a\xc5\xa6\x21\x22\xd5\x02\xdf\x1b\xa7\x97\x4d\x53\xf8\xac\x1d\x82\x2b\xb8\x5b\xc1\x5e\x37\xa0\x10\x05\x65\x52\xc6\xcb\x72\xca\xfc\x45\x3d\x19\x5e\xcf\x17\xb0\xc1\x5c\x1b\xf4\x1c\x03\x6c\x85\xae\xd0\x62\x45\xc9\x7d\xa6\x86\xb4\x3c\x71\xdb\x99\x87\x02\x72\xa3\x2b\xe0\xe0\x0c\x57\x96\x67\x94\x7e\x2b\xe0\x4a\xf8\x60\x8c\x0e\xbd\x50\xa6\x2b\x2a\xc7\x28\x40\x1b\x30\xba\x2c\xa9\x00\xf2\xec\x91\xc5\x6f\x8a\x53\xf0\x4c\x1f\xa2\xfe\x3c\x9c\xfe\x5d\x21\x45\xe8\xff\x0b\xd0\x00\x71\x1e\x9e\x2e\x26\xde\x5d\xd0\xf8\x3f\xb6\x6f\xc0\xd4\xf7\xc9\xd9\xbf\xe7\x10\xe0\xb9\x43\x03\x32\x30\x66\xa5\xb6\x28\x56\x04\x6b\x75\x90\xa7\xf0\x28\x7c\x76\x43\x8e\x3f\xc9\xb2\x84\x0d\x02\x3e\x63\xd6\x90\xbf\x5c\x61\x74\xb3\x2d\xbc\xe6\x50\xc1\xe0\xa9\x90\x59\x01\x99\x41\x1e\x18\x26\xee\x7e\xab\x47\xfb\x34\x98\x9c\x93\x23\xdd\xf3\x0a\xf4\x23\xbd\xd3\xcb\x5e\x63\x5d\x1d\x9d\x2f\xdd\xf3\x9d\xff\x5c\xc4\x91\xcc\xe1\x27\xfd\xe8\x5f\x4a\xcd\x95\xcc\xe6\x49\x3f\x94\x1d\x0e\x1f\xce\x66\x28\xea\x18\x13\x3f\xf1\x7e\x9a\x4a\xfc\xb3\x88\x5e\xd5\x0c\xd7\xe0\x9e\x99\x30\xbb\x21\xe8\x27\xec\x5d\xe8\xee\x9d\xf1\xbd\xb3\xaa\x4b\xac\x50\xb9\x10\xbd\xbc\x72\x2c\x50\xd0\xbc\xd1\x57\x81\x7d\xbe\xa0\xb6\x4b\x88\x6d\x1c\xed\xb8\x19\x5e\x67\x38\xb5\xec\xd7\xf0\x3b\x8e\x3a\x02\xfb\xb7\x91\x0e\x3b\xe1\x64\x0c\x39\xa7\x6b\x5e\xe2\xf2\xc6\x85\x6a\x3d\x4f\xa4\xb8\x7e\xb7\x4b\x56\x67\x61\x58\xdf\x2d\x16\x93\xb1\x4e\x5e\x1e\xeb\xba\x2e\x75\x32\x47\x51\x57\xbd\x68\xe0\x0a\x26\x73\xdd\xf5\x5f\x6c\x2f\xf5\x57\x32\x37\x34\xaf\x30\x6d\xf5\x9d\x79\x66\xf3\x71\xf7\x7c\x67\xd9\x3b\xea\x95\x83\xb1\x67\xa3\x60\x2f\x78\x36\x0e\xfa\x19\x43\xe6\xb0\xeb\xf3\xce\xe6\x70\x38\xfc\x19\x76\xf0\xd3\x35\x28\x59\x86\x51\xe7\x8d\x96\x7b\x73\x27\x9a\xd6\xf6\x41\x56\x83\x9e\xcb\x30\x3b\xf6\x49\x9b\x8a\xbb\xb9\x93\x15\xb2\x9b\xcf\xf7\xeb\xdb\xc5\x08\xc8\x5f\xbd\x47\xeb\x52\xeb\x55\xbc\xe5\xee\x54\xfa\x55\xf6\x49\xe8\x7d\xdc\x97\xbb\x89\xfe\x61\x26\x8a\x8e\x93\xd1\x08\xf5\x7f\xf1\xcc\x8b\x8e\xb9\x04\x32\x44\xe3\x45\xff\xfc\x9e\x7b\x5e\x45\x3d\x81\x78\x4d\xe6\xdc\x45\x47\x94\x11\xcc\xe5\xe1\xf1\x74\xf6\xea\xbf\x27\x8a\x7e\xdd\x3b\x9c\xff\xbc\xf8\x79\x31\x94\x96\x9e\xdc\xd7\x80\x30\x59\x05\xf7\xd1\xf2\x35\xbe\x6c\x9a\x82\x14\x43\xfb\xa6\x62\x43\x73\x8e\xc1\x6e\xcb\xf6\xfb\x43\x3f\xad\xac\xef\xfa\xbd\xe9\x4d\x25\x48\x8a\xf9\xc2\xa3\xb5\x71\x24\xc5\x0a\xbe\xd3\x3b\xb1\xce\x64\x5a\xed\xd8\x8d\xd3\xf2\x14\x80\xaa\xc4\x70\x09\x29\xe2\x43\x3c\xba\xb4\x9f\xe5\x2c\xed\xe7\xfe\xf9\x96\x8d\xe1\xe5\xb4\xd1\x1f\x19\x42\xc5\xe6\x50\x73\x63\xfd\x63\x0d\xc7\x3a\x3f\x99\x41\x86\x4d\x77\x10\xfb\xfa\x6d\x72\x09\xaf\xd5\x4f\xfe\xf8\xec\xc8\x90\x19\x24\xf7\xc4\x9b\x1c\x65\x42\xe5\x7f\x65\x2a\xef\xc6\xd6\x8a\xab\xfd\xf9\x50\x7e\x79\xea\x1e\xcd\x58\x97\x5d\x3d\x36\x7a\x01\xa1\xd5\xcc\xb3\x7c\xdb\x7d\x2e\xc8\xe7\x34\x81\x7d\x97\x64\x54\xa8\xb9\x67\x18\x71\x74\xda\xb4\xbe\x7e\x97\xdf\xba\xc6\x05\xd7\x90\xe5\x5b\xea\x6c\x13\x73\xda\x96\x16\x80\xe3\x4c\xef\x77\x4a\x1a\x93\x28\x9f\xc2\x8a\x77\x45\x1b\x6c\x37\xff\x9f\xfe\xf3\x63\xb4\xe1\xfa\xfd\xb6\xdb\x63\x1f\xf8\x96\x26\x58\xdb\xed\x52\xa3\x1e\xe1\xc8\x7e\x16\x98\x69\x6f\xa3\x63\x78\xdf\xb9\xe0\xb8\x8c\x3b\xea\xd9\xc9\x55\x32\x1c\xfe\x18\x93\x5f\x32\xde\x8f\x33\x19\x57\x34\xbc\xe8\x1d\x1a\x23\x05\x0a\x5a\x8b\xb5\xf1\xff\x1b\x0a\x5b\x0d\xbf\xb4\xee\xf8\xa1\x8a\x67\x05\x50\x0e\xb1\xcb\x77\xbd\xb0\xe8\x90\x39\xa8\xc4\xe1\x10\xff\x37\x00\x00\xff\xff\x1c\xfa\x46\xfc\xdb\x12\x00\x00") func templateEntTmplBytes() ([]byte, error) { return bindataRead( @@ -836,7 +836,7 @@ func templateEntTmpl() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "template/ent.tmpl", size: 4492, mode: os.FileMode(420), modTime: time.Unix(1, 0)} + info := bindataFileInfo{name: "template/ent.tmpl", size: 4827, mode: os.FileMode(420), modTime: time.Unix(1, 0)} a := &asset{bytes: bytes, info: info} return a, nil } diff --git a/entc/gen/template/builder/query.tmpl b/entc/gen/template/builder/query.tmpl index a5c66b379..4ba87460e 100644 --- a/entc/gen/template/builder/query.tmpl +++ b/entc/gen/template/builder/query.tmpl @@ -11,7 +11,7 @@ in the LICENSE file in the root directory of this source tree. {{ template "import" $ }} -{{ $builder := print (pascal $.Name) "Query" }} +{{ $builder := $.QueryName }} {{ $receiver := receiver $builder }} // {{ $builder }} is the builder for querying {{ pascal $.Name }} entities. @@ -22,8 +22,19 @@ type {{ $builder }} struct { order []Order unique []string predicates []predicate.{{ $.Name }} + {{- with $.Edges }} + // eager-loading edges. + {{- range $e := . }} + with{{ pascal $e.Name }} *{{ $e.Type.QueryName }} + {{- end }} + {{- end }} + {{- /* Additional fields to add to the builder. */}} + {{- $tmpl := printf "dialect/%s/query/fields" $.Storage }} + {{- if hasTemplate $tmpl }} + {{- xtemplate $tmpl . }} + {{- end }} // intermediate query. - {{ $.Storage }} {{ $.Storage.Builder}} + {{ $.Storage }} {{ $.Storage.Builder }} } // Where adds a new predicate for the builder. @@ -57,8 +68,8 @@ func ({{ $receiver }} *{{ $builder }}) Order(o ...Order) *{{ $builder }} { func ({{ $receiver }} *{{ $builder }}) Query{{ pascal $e.Name }}() *{{ $edge_builder }} { query := &{{ $edge_builder }}{config: {{ $receiver }}.config} {{- with extend $ "Receiver" $receiver "Edge" $e -}} - {{ $tmpl := printf "dialect/%s/query/path" $.Storage }} - {{- xtemplate $tmpl . }} + {{ $tmpl := printf "dialect/%s/query/path" $.Storage }} + {{- xtemplate $tmpl . }} {{- end -}} return query } @@ -233,6 +244,20 @@ func ({{ $receiver }} *{{ $builder }}) Clone() *{{ $builder }} { } } +{{- range $e := $.Edges }} + {{ $ebuilder := $e.Type.QueryName }} + // With{{ pascal $e.Name }} tells the query-builder to eager-loads the nodes that are connected to + // the "{{ $e.Name }}" edge. The optional arguments used to configure the query builder of the edge. + func ({{ $receiver }} *{{ $builder }}) With{{ pascal $e.Name }}(opts ...func(*{{ $ebuilder }})) *{{ $builder }} { + query := &{{ $ebuilder }}{config: {{ $receiver }}.config} + for _, opt := range opts { + opt(query) + } + {{ $receiver }}.with{{ pascal $e.Name }} = query + return {{ $receiver }} + } +{{- end }} + {{ $groupBuilder := pascal $.Name | printf "%sGroupBy" }} // GroupBy used to group vertices by one or more fields/columns. diff --git a/entc/gen/template/client.tmpl b/entc/gen/template/client.tmpl index 0ec1ba335..10a06d507 100644 --- a/entc/gen/template/client.tmpl +++ b/entc/gen/template/client.tmpl @@ -180,10 +180,10 @@ func (c *{{ $client }}) GetX(ctx context.Context, id {{ $n.ID.Type }}) *{{ $n.Na } {{ range $_, $e := $n.Edges }} -{{ $builder := print (pascal $e.Type.Name) "Query" }} +{{ $builder := $e.Type.QueryName }} // Query{{ pascal $e.Name }} queries the {{ $e.Name }} edge of a {{ $n.Name }}. func (c *{{ $client }}) Query{{ pascal $e.Name }}({{ $rec }} *{{ $n.Name }}) *{{ $builder }} { - query := &{{ $e.Type.Name }}Query{config: c.config} + query := &{{ $builder }}{config: c.config} {{- with extend $n "Receiver" $rec "Edge" $e }} {{ $tmpl := printf "dialect/%s/query/from" $.Storage }} {{- xtemplate $tmpl . -}} diff --git a/entc/gen/template/dialect/sql/decode.tmpl b/entc/gen/template/dialect/sql/decode.tmpl index 768c7c87f..213c4bf4f 100644 --- a/entc/gen/template/dialect/sql/decode.tmpl +++ b/entc/gen/template/dialect/sql/decode.tmpl @@ -10,17 +10,29 @@ in the LICENSE file in the root directory of this source tree. // scanValues returns the types for scanning values from sql.Rows. func (*{{ $.Name }}) scanValues() []interface{} { return []interface{} { - &{{ if not $.ID.UserDefined }}sql.NullInt64{{ else }}{{ $.ID.NullType }}{{ end }}{}, + &{{ if not $.ID.UserDefined }}sql.NullInt64{{ else }}{{ $.ID.NullType }}{{ end }}{}, // {{ $.ID.Name }} {{- range $_, $f := $.Fields }} - &{{ $f.NullType }}{}, + &{{ $f.NullType }}{}, // {{ $f.Name }} {{- end }} } } +{{- with $.ForeignKeys }} + // fkValues returns the types for scanning foreign-keys values from sql.Rows. + func (*{{ $.Name }}) fkValues() []interface{} { + return []interface{} { + {{- range $fk := . }} + {{- $f := $fk.Field }} + &{{ if not $f.UserDefined }}sql.NullInt64{{ else }}{{ $f.NullType }}{{ end }}{}, // {{ $f.Name }} + {{- end }} + } + } +{{- end }} + // assignValues assigns the values that were returned from sql.Rows (after scanning) // to the {{ $.Name }} fields. func ({{ $receiver }} *{{ $.Name }}) assignValues(values ...interface{}) error { - if m, n := len(values), len({{ $.Package }}.Columns); m != n { + if m, n := len(values), len({{ $.Package }}.Columns); m < n { return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) } {{- if and $.ID.UserDefined (or $.ID.IsString $.ID.IsUUID) }} @@ -40,6 +52,26 @@ func ({{ $receiver }} *{{ $.Name }}) assignValues(values ...interface{}) error { {{ template "dialect/sql/decode/field" . }} {{- end }} {{- end }} + {{- with $.ForeignKeys }} + values = values[{{ len $.Fields }}:] + if len(values) == len({{ $.Package }}.ForeignKeys) { + {{- range $i, $fk := . }} + {{- $f := $fk.Field }} + {{- if and $f.UserDefined (or $f.IsString $f.IsUUID) }} + {{- with extend $ "Idx" 0 "Field" $f "Rec" $receiver "StructField" $f.Name }} + {{ template "dialect/sql/decode/field" . }} + {{- end }} + {{- else }} + if value, ok := values[{{ $i }}].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for edge-field {{ $f.Name}}", value) + } else if value.Valid { + {{ $receiver }}.{{ $f.Name }} = new({{ $f.Type }}) + *{{ $receiver }}.{{ $f.Name }} = {{ if $f.IsString }}strconv.FormatInt(value.Int64, 10){{ else }}{{ $f.Type }}(value.Int64){{ end }} + } + {{- end }} + {{- end }} + } + {{- end }} return nil } {{ end }} @@ -48,11 +80,12 @@ func ({{ $receiver }} *{{ $.Name }}) assignValues(values ...interface{}) error { {{- $i := $.Scope.Idx -}} {{- $f := $.Scope.Field -}} {{- $ret := $.Scope.Rec -}} + {{- $field := $f.StructField }}{{ with $.Scope.StructField }}{{ $field = . }}{{ end }} {{- if $f.IsJSON }} if value, ok := values[{{ $i }}].(*{{ $f.NullType }}); !ok { return fmt.Errorf("unexpected type %T for field {{ $f.Name }}", values[{{ $i }}]) } else if value != nil && len(*value) > 0 { - if err := json.Unmarshal(*value, &{{ $ret }}.{{ $f.StructField }}); err != nil { + if err := json.Unmarshal(*value, &{{ $ret }}.{{ $field }}); err != nil { return fmt.Errorf("unmarshal field {{ $f.Name }}: %v", err) } } @@ -63,14 +96,14 @@ func ({{ $receiver }} *{{ $.Name }}) assignValues(values ...interface{}) error { {{- if hasPrefix $nulltype "sql" }} } else if value.Valid { {{- if $f.Nillable }} - {{ $ret }}.{{ $f.StructField }} = new({{ $f.Type }}) - *{{ $ret }}.{{ $f.StructField }} = {{ $f.NullTypeField "value" }} + {{ $ret }}.{{ $field }} = new({{ $f.Type }}) + *{{ $ret }}.{{ $field }} = {{ $f.NullTypeField "value" }} {{- else }} - {{ $ret }}.{{ $f.StructField }} = {{ $f.NullTypeField "value" }} + {{ $ret }}.{{ $field }} = {{ $f.NullTypeField "value" }} {{- end }} {{- else }} } else if value != nil { - {{ $ret }}.{{ $f.StructField }} = *value + {{ $ret }}.{{ $field }} = *value {{- end }} } {{- end }} @@ -78,3 +111,11 @@ func ({{ $receiver }} *{{ $.Name }}) assignValues(values ...interface{}) error { {{ define "dialect/sql/decode/many" }} {{ end }} + +{{/* Additional fields for the generated model for holding the foreign-keys */}} +{{ define "dialect/sql/model/fields" }} + {{- range $fk := $.ForeignKeys }} + {{- $f := $fk.Field }} + {{ $f.Name }} {{ if $f.Nillable }}*{{ end }}{{ $f.Type }} + {{- end }} +{{ end }} \ No newline at end of file diff --git a/entc/gen/template/dialect/sql/meta.tmpl b/entc/gen/template/dialect/sql/meta.tmpl index 9966c45dd..710e0dec2 100644 --- a/entc/gen/template/dialect/sql/meta.tmpl +++ b/entc/gen/template/dialect/sql/meta.tmpl @@ -12,7 +12,7 @@ in the LICENSE file in the root directory of this source tree. // {{ $e.TableConstant }} is the table the holds the {{ $e.Name }} relation/edge. {{- if $e.M2M }} The primary key declared below.{{ end }} {{ $e.TableConstant }} = "{{ $e.Rel.Table }}" - {{- if eq $.Table $e.Type.Table | not }} + {{- if ne $.Table $e.Type.Table }} // {{ $e.InverseTableConstant }} is the table name for the {{ $e.Type.Name }} entity. // It exists in this package in order to avoid circular dependency with the "{{ $e.Type.Package }}" package. {{ $e.InverseTableConstant }} = "{{ $e.Type.Table }}" @@ -26,13 +26,22 @@ in the LICENSE file in the root directory of this source tree. {{/* variables needed for sql dialects. */}} {{ define "dialect/sql/meta/variables" }} - // Columns holds all SQL columns are {{ lower $.Name }} fields. + // Columns holds all SQL columns for {{ lower $.Name }} fields. var Columns = []string{ {{ $.ID.Constant }}, - {{- range $_, $f := $.Fields }} + {{- range $f := $.Fields }} {{ $f.Constant }}, {{- end }} } + {{/* if any of the edges owns a foreign-key */}} + {{ with $.ForeignKeys }} + // ForeignKeys holds the SQL foreign-keys that are owned by the {{ $.Name }} type. + var ForeignKeys = []string{ + {{- range $fk := . }} + "{{ $fk.Field.Name }}", + {{- end }} + } + {{ end }} {{ with $.NumM2M }} var ( diff --git a/entc/gen/template/dialect/sql/query.tmpl b/entc/gen/template/dialect/sql/query.tmpl index 96fcdb12f..262b32bf6 100644 --- a/entc/gen/template/dialect/sql/query.tmpl +++ b/entc/gen/template/dialect/sql/query.tmpl @@ -4,6 +4,13 @@ This source code is licensed under the Apache 2.0 license found in the LICENSE file in the root directory of this source tree. */}} +{{/* Additional fields for the builder. */}} +{{ define "dialect/sql/query/fields" }} + {{- with $.ForeignKeys }} + withFKs bool + {{- end }} +{{- end }} + {{ define "dialect/sql/query" }} {{ $pkg := $.Scope.Package }} {{ $builder := pascal $.Scope.Builder }} @@ -12,12 +19,31 @@ in the LICENSE file in the root directory of this source tree. func ({{ $receiver }} *{{ $builder }}) sqlAll(ctx context.Context) ([]*{{ $.Name }}, error) { var ( nodes []*{{ $.Name }} + {{- with $.ForeignKeys }} + withFKs = {{ $receiver }}.withFKs + {{- end }} spec = {{ $receiver }}.querySpec() ) + {{- with $.ForeignKeys }} + {{- with $.FKEdges }} + if {{ range $i, $e := . }}{{ if gt $i 0 }} || {{ end }}{{ $receiver }}.with{{ pascal $e.Name }} != nil{{ end }} { + withFKs = true + } + {{- end }} + if withFKs { + spec.Node.Columns = append(spec.Node.Columns, {{ $.Package }}.ForeignKeys...) + } + {{- end }} spec.ScanValues = func() []interface{} { node := &{{ $.Name }}{config: {{ $receiver }}.config} nodes = append(nodes, node) - return node.scanValues() + values := node.scanValues() + {{- with $.ForeignKeys }} + if withFKs { + values = append(values, node.fkValues()...) + } + {{- end }} + return values } spec.Assign = func(values ...interface{}) error { if len(nodes) == 0 { @@ -29,6 +55,11 @@ func ({{ $receiver }} *{{ $builder }}) sqlAll(ctx context.Context) ([]*{{ $.Name if err := sqlgraph.QueryNodes(ctx, {{ $receiver }}.driver, spec); err != nil { return nil, err } + {{- range $e := $.Edges }} + {{- with extend $ "Rec" $receiver "Edge" $e }} + {{ template "dialect/sql/query/eagerloading" . }} + {{- end }} + {{- end }} return nodes, nil } @@ -145,3 +176,145 @@ func ({{ $receiver }} *{{ $builder }}) sqlQuery() *sql.Selector { ) query.sql = sqlgraph.Neighbors({{ $receiver }}.driver.Dialect(), step) {{ end }} + +{{ define "dialect/sql/query/eagerloading" }} + {{- $e := $.Scope.Edge }} + {{- $receiver := $.Scope.Rec }} + if query := {{ $receiver }}.with{{ pascal $e.Name }}; query != nil { + {{- if $e.M2M }} + fks := make([]driver.Value, 0, len(nodes)) + ids := make(map[{{ $.ID.Type }}]*{{ $.Name }}, len(nodes)) + for _, node := range nodes { + ids[node.ID] = node + fks = append(fks, node.ID) + } + var ( + edgeids []{{ $e.Type.ID.Type }} + edges = make(map[{{ $e.Type.ID.Type }}][]*{{ $.Name }}) + ) + spec := &sqlgraph.EdgeQuerySpec{ + Edge: &sqlgraph.EdgeSpec{ + Inverse: {{ $e.IsInverse }}, + Table: {{ $.Package }}.{{ $e.TableConstant }}, + Columns: {{ $.Package }}.{{ $e.PKConstant }}, + }, + Predicate: func(s *sql.Selector) { + s.Where(sql.InValues({{ $.Package }}.{{ $e.PKConstant }}[{{ if $e.IsInverse }}1{{ else }}0{{ end }}], fks...)) + }, + {{ $out := "sql.NullInt64" }}{{ if $.ID.UserDefined }}{{ $out = $.ID.NullType }}{{ end }} + {{ $in := "sql.NullInt64" }}{{ if $e.Type.ID.UserDefined }}{{ $in = $e.Type.ID.NullType }}{{ end }} + ScanValues: func() [2]interface{}{ + return [2]interface{}{&{{ $out }}{}, &{{ $in }}{}} + }, + Assign: func(out, in interface{}) error { + eout, ok := out.(*{{ $out }}) + if !ok || eout == nil { + return fmt.Errorf("unexpected id value for edge-out") + } + ein, ok := in.(*{{ $in }}) + if !ok || ein == nil { + return fmt.Errorf("unexpected id value for edge-in") + } + outValue := {{ with extend $ "Arg" "eout" "Field" $.ID "NullType" $out }}{{ template "dialect/sql/query/eagerloading/m2massign" . }}{{ end }} + inValue := {{ with extend $ "Arg" "ein" "Field" $e.Type.ID "NullType" $in }}{{ template "dialect/sql/query/eagerloading/m2massign" . }}{{ end }} + node, ok := ids[outValue] + if !ok { + return fmt.Errorf("unexpected node id in edges: %v", outValue) + } + edgeids = append(edgeids, inValue) + edges[inValue] = append(edges[inValue], node) + return nil + }, + } + if err := sqlgraph.QueryEdges(ctx, {{ $receiver }}.driver, spec); err != nil { + return nil, fmt.Errorf(`query edges "{{ $e.Name }}": %v`, err) + } + query.Where({{ $e.Type.Package }}.IDIn(edgeids...)) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + nodes, ok := edges[n.ID] + if !ok { + return nil, fmt.Errorf(`unexpected "{{ $e.Name }}" node returned %v`, n.ID) + } + for i := range nodes { + nodes[i].Edges.{{ $e.StructField }} = append(nodes[i].Edges.{{ $e.StructField }}, n) + } + } + {{- else if $e.OwnFK }} + ids := make([]{{ $e.Type.ID.Type }}, 0, len(nodes)) + nodeids := make(map[{{ $e.Type.ID.Type }}][]*{{ $.Name }}) + for i := range nodes { + if fk := nodes[i].{{ $e.StructFKField }}; fk != nil { + ids = append(ids, *fk) + nodeids[*fk] = append(nodeids[*fk], nodes[i]) + } + } + query.Where({{ $e.Type.Package }}.IDIn(ids...)) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + nodes, ok := nodeids[n.ID] + if !ok { + return nil, fmt.Errorf(`unexpected foreign-key "{{ $e.StructFKField }}" returned %v`, n.ID) + } + for i := range nodes { + nodes[i].Edges.{{ $e.StructField }} = n + } + } + {{- else }} + fks := make([]driver.Value, 0, len(nodes)) + nodeids := make(map[{{ $.ID.Type }}]*{{ $.Name }}) + for i := range nodes { + {{- /* Convert string-ids that are stored as int in the database */ -}} + {{- if and (not $.ID.UserDefined) $.ID.IsString }} + id, err := strconv.Atoi(nodes[i].ID) + if err != nil { + return nil, err + } + fks = append(fks, id) + {{- else }} + fks = append(fks, nodes[i].ID) + {{- end }} + nodeids[nodes[i].ID] = nodes[i] + } + query.withFKs = true + query.Where(predicate.{{ $e.Type.Name }}(func(s *sql.Selector) { + s.Where(sql.InValues({{ $.Package }}.{{ $e.ColumnConstant }}, fks...)) + })) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + fk := n.{{ $e.StructFKField }} + if fk == nil { + return nil, fmt.Errorf(`foreign-key "{{ $e.StructFKField }}" is nil for node %v`, n.ID) + } + node, ok := nodeids[*fk] + if !ok { + return nil, fmt.Errorf(`unexpected foreign-key "{{ $e.StructFKField }}" returned %v for node %v`, *fk, n.ID) + } + node.Edges.{{ $e.StructField }} = {{ if $e.Unique }}n{{ else }}append(node.Edges.{{ $e.StructField }}, n){{ end }} + } + {{- end }} + } +{{ end }} + +{{- /* Convert string-ids that are stored as int in the database */ -}} +{{ define "dialect/sql/query/eagerloading/m2massign" }} + {{- $arg := $.Scope.Arg }} + {{- $field := $.Scope.Field }} + {{- $nulltype := $.Scope.NullType }} + {{- if and (not $field.UserDefined) $field.IsString -}} + strconv.FormatInt({{ $arg }}.Int64, 10) + {{- else if hasPrefix $nulltype "sql" -}} + {{ $field.NullTypeField "eout" -}} + {{- else -}} + {{ $arg }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/entc/gen/template/ent.tmpl b/entc/gen/template/ent.tmpl index 9d37997cf..cd6d57d6c 100644 --- a/entc/gen/template/ent.tmpl +++ b/entc/gen/template/ent.tmpl @@ -16,21 +16,30 @@ type {{ $.Name }} struct { config {{ template "model/omittags" $ }} // ID of the ent. ID {{ $.ID.Type }} `json:"id,omitempty"` - {{ range $_, $f := $.Fields -}} + {{- range $_, $f := $.Fields }} // {{ $f.StructField }} holds the value of the "{{ $f.Name }}" field. {{ $f.StructField }} {{ if $f.Nillable }}*{{ end }}{{ $f.Type }} {{ if not $f.Sensitive }}`{{ $f.StructTag }}`{{ else }}{{ template "model/omittags" $ }}{{ end }} - {{ end -}} - {{ range $_, $e := $.Edges -}} - {{/* ignore generating edge fields */}} - {{- with $e.StructTag -}} - // {{ $e.StructField }} holds the value of the {{ $e.Name }} edge. The value set to nil, and should be updated manually. - {{ $e.StructField }} {{ if not $e.Unique }}[]{{ end }}*{{ $e.Type.Name }} `{{ $e.StructTag }}` - {{ end -}} - {{ end -}} + {{- end }} + {{- with $.Edges }} + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the {{ $.Name }}Query when eager-loading is set. + Edges struct { + {{- range $e := $.Edges }} + // {{ $e.StructField }} holds the value of the {{ $e.Name }} edge. + {{ $e.StructField }} {{ if not $e.Unique }}[]{{ end }}*{{ $e.Type.Name }} {{ with $e.StructTag }}`{{ . }}`{{ end }} + {{- end }} + } + {{- end -}} + {{- /* Additional fields to add by the storage driver. */}} + {{- $tmpl := printf "dialect/%s/model/fields" $.Storage }} + {{- if hasTemplate $tmpl }} + {{- xtemplate $tmpl . }} + {{- end }} + {{- /* Additional fields to add by the user. */}} {{ template "model/fields/additional" $ }} } -{{ $tmpl := printf "dialect/%s/decode/one" $.Storage }} +{{ $tmpl = printf "dialect/%s/decode/one" $.Storage }} {{ xtemplate $tmpl $ }} {{ $receiver := $.Receiver }} diff --git a/entc/gen/type.go b/entc/gen/type.go index 00b77fcf1..a7a9bd7f5 100644 --- a/entc/gen/type.go +++ b/entc/gen/type.go @@ -36,6 +36,10 @@ type ( Edges []*Edge // Indexes are the configured indexes for this type. Indexes []*Index + // ForeignKeys are the foreign-keys that resides in the type table. + ForeignKeys []*ForeignKey + // foreignkeys used for first lookup from edge name to foreign-key. + foreignkeys map[string]*ForeignKey } // Field holds the information of a type field used for the templates. @@ -80,7 +84,7 @@ type ( Optional bool // Unique indicates if this edge is a unique edge. Unique bool - // Inverse holds the name of the inverse edge. + // Inverse holds the name of the reference edge declared in the schema. Inverse string // Owner holds the type of the edge-owner. For assoc-edges it's the // type that holds the edge, for inverse-edges, it's the assoc type. @@ -123,6 +127,16 @@ type ( // Columns are the table columns. Columns []string } + + // ForeignKey holds the information for foreign-key columns of types. + // It's exported only because it's used by the codegen templates and + // should not be used beside that. + ForeignKey struct { + // Field information for the foreign-key column. + Field *Field + // Edge that is associated with this foreign-key. + Edge *Edge + } ) // NewType creates a new type and its fields from the given schema. @@ -134,10 +148,11 @@ func NewType(c *Config, schema *load.Schema) (*Type, error) { Type: c.IDType, StructTag: `json:"id,omitempty"`, }, - schema: schema, - Name: schema.Name, - Fields: make([]*Field, 0, len(schema.Fields)), - fields: make(map[string]*Field, len(schema.Fields)), + schema: schema, + Name: schema.Name, + Fields: make([]*Field, 0, len(schema.Fields)), + fields: make(map[string]*Field, len(schema.Fields)), + foreignkeys: make(map[string]*ForeignKey), } for _, f := range schema.Fields { switch { @@ -257,6 +272,16 @@ func (t Type) HasOptional() bool { return false } +// FKEdges returns all edges that reside on the type table as foreign-keys. +func (t Type) FKEdges() (edges []*Edge) { + for _, e := range t.Edges { + if e.OwnFK() { + edges = append(edges, e) + } + } + return +} + // MixedInWithDefaultOrValidator returns all mixed-in fields with default values for creation or update. func (t Type) MixedInWithDefaultOrValidator() (fields []*Field) { for _, f := range t.Fields { @@ -386,19 +411,43 @@ func (t *Type) AddIndex(idx *load.Index) error { return nil } -// EdgeFields returns the edges that resides in the type's -// table in SQL as foreign-keys (columns). -func (t Type) EdgeFields() []*Edge { - var fks []*Edge +// resolveFKs makes sure all edge-fks are created for the types. +func (t *Type) resolveFKs() { for _, e := range t.Edges { - switch { - case e.M2O(): - fks = append(fks, e) - case e.O2O() && (e.IsInverse() || e.Bidi): - fks = append(fks, e) + if e.IsInverse() || e.M2M() { + continue + } + fk := &ForeignKey{ + Edge: e, + Field: &Field{ + Name: e.Rel.Column(), + Type: e.Type.ID.Type, + Nillable: true, + Optional: true, + Unique: e.Unique, + UserDefined: e.Type.ID.UserDefined, + }, + } + if e.OwnFK() { + t.addFK(fk) + } else { + e.Type.addFK(fk) } } - return fks +} + +// AddForeignKey adds a foreign-key for the type if it doesn't exist. +func (t *Type) addFK(fk *ForeignKey) { + if _, ok := t.foreignkeys[fk.Field.Name]; ok { + return + } + t.foreignkeys[fk.Field.Name] = fk + t.ForeignKeys = append(t.ForeignKeys, fk) +} + +// QueryName returns the struct name of the query builder for this type. +func (t Type) QueryName() string { + return pascal(t.Name) + "Query" } // Constant returns the constant name of the field. @@ -636,6 +685,24 @@ func (e Edge) StructField() string { return pascal(e.Name) } +// StructFKField returns the struct member for holding the edge +// foreign-key in the model. +func (e Edge) StructFKField() string { + return e.Rel.Column() +} + +// OwnFK indicates if the foreign-key of this edge is owned by the edge +// column (reside in the type's table). Used by the SQL storage-driver. +func (e Edge) OwnFK() bool { + switch { + case e.M2O(): + return true + case e.O2O() && (e.IsInverse() || e.Bidi): + return true + } + return false +} + // Column returns the first element from the columns slice. func (r Relation) Column() string { if len(r.Columns) == 0 { diff --git a/entc/integration/config/ent/user.go b/entc/integration/config/ent/user.go index 1c0c6a0bb..8349dbd7d 100644 --- a/entc/integration/config/ent/user.go +++ b/entc/integration/config/ent/user.go @@ -24,14 +24,14 @@ type User struct { // scanValues returns the types for scanning values from sql.Rows. func (*User) scanValues() []interface{} { return []interface{}{ - &sql.NullInt64{}, + &sql.NullInt64{}, // id } } // assignValues assigns the values that were returned from sql.Rows (after scanning) // to the User fields. func (u *User) assignValues(values ...interface{}) error { - if m, n := len(values), len(user.Columns); m != n { + if m, n := len(values), len(user.Columns); m < n { return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) } value, ok := values[0].(*sql.NullInt64) diff --git a/entc/integration/config/ent/user/user.go b/entc/integration/config/ent/user/user.go index 0072f14bd..12323e2c4 100644 --- a/entc/integration/config/ent/user/user.go +++ b/entc/integration/config/ent/user/user.go @@ -16,7 +16,7 @@ const ( Table = "Users" ) -// Columns holds all SQL columns are user fields. +// Columns holds all SQL columns for user fields. var Columns = []string{ FieldID, } diff --git a/entc/integration/config/ent/user_query.go b/entc/integration/config/ent/user_query.go index fbf2e8f94..d21424f9d 100644 --- a/entc/integration/config/ent/user_query.go +++ b/entc/integration/config/ent/user_query.go @@ -249,7 +249,8 @@ func (uq *UserQuery) sqlAll(ctx context.Context) ([]*User, error) { spec.ScanValues = func() []interface{} { node := &User{config: uq.config} nodes = append(nodes, node) - return node.scanValues() + values := node.scanValues() + return values } spec.Assign = func(values ...interface{}) error { if len(nodes) == 0 { diff --git a/entc/integration/customid/ent/blob.go b/entc/integration/customid/ent/blob.go index db9498529..00c7bc0ae 100644 --- a/entc/integration/customid/ent/blob.go +++ b/entc/integration/customid/ent/blob.go @@ -26,15 +26,15 @@ type Blob struct { // scanValues returns the types for scanning values from sql.Rows. func (*Blob) scanValues() []interface{} { return []interface{}{ - &uuid.UUID{}, - &uuid.UUID{}, + &uuid.UUID{}, // id + &uuid.UUID{}, // uuid } } // assignValues assigns the values that were returned from sql.Rows (after scanning) // to the Blob fields. func (b *Blob) assignValues(values ...interface{}) error { - if m, n := len(values), len(blob.Columns); m != n { + if m, n := len(values), len(blob.Columns); m < n { return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) } if value, ok := values[0].(*uuid.UUID); !ok { diff --git a/entc/integration/customid/ent/blob/blob.go b/entc/integration/customid/ent/blob/blob.go index 593b2cfc9..639582361 100644 --- a/entc/integration/customid/ent/blob/blob.go +++ b/entc/integration/customid/ent/blob/blob.go @@ -23,7 +23,7 @@ const ( Table = "blobs" ) -// Columns holds all SQL columns are blob fields. +// Columns holds all SQL columns for blob fields. var Columns = []string{ FieldID, FieldUUID, diff --git a/entc/integration/customid/ent/blob_query.go b/entc/integration/customid/ent/blob_query.go index 956b74083..6b7cdc763 100644 --- a/entc/integration/customid/ent/blob_query.go +++ b/entc/integration/customid/ent/blob_query.go @@ -274,7 +274,8 @@ func (bq *BlobQuery) sqlAll(ctx context.Context) ([]*Blob, error) { spec.ScanValues = func() []interface{} { node := &Blob{config: bq.config} nodes = append(nodes, node) - return node.scanValues() + values := node.scanValues() + return values } spec.Assign = func(values ...interface{}) error { if len(nodes) == 0 { diff --git a/entc/integration/customid/ent/client.go b/entc/integration/customid/ent/client.go index 3063ac9e8..18c1ea3e6 100644 --- a/entc/integration/customid/ent/client.go +++ b/entc/integration/customid/ent/client.go @@ -328,3 +328,31 @@ func (c *UserClient) QueryGroups(u *User) *GroupQuery { return query } + +// QueryParent queries the parent edge of a User. +func (c *UserClient) QueryParent(u *User) *UserQuery { + query := &UserQuery{config: c.config} + id := u.ID + step := sqlgraph.NewStep( + sqlgraph.From(user.Table, user.FieldID, id), + sqlgraph.To(user.Table, user.FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, user.ParentTable, user.ParentColumn), + ) + query.sql = sqlgraph.Neighbors(u.driver.Dialect(), step) + + return query +} + +// QueryChildren queries the children edge of a User. +func (c *UserClient) QueryChildren(u *User) *UserQuery { + query := &UserQuery{config: c.config} + id := u.ID + step := sqlgraph.NewStep( + sqlgraph.From(user.Table, user.FieldID, id), + sqlgraph.To(user.Table, user.FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, user.ChildrenTable, user.ChildrenColumn), + ) + query.sql = sqlgraph.Neighbors(u.driver.Dialect(), step) + + return query +} diff --git a/entc/integration/customid/ent/example_test.go b/entc/integration/customid/ent/example_test.go index 2f520fda9..1bc4d8621 100644 --- a/entc/integration/customid/ent/example_test.go +++ b/entc/integration/customid/ent/example_test.go @@ -89,14 +89,25 @@ func ExampleUser() { defer drv.Close() client := NewClient(Driver(drv)) // creating vertices for the user's edges. + u2 := client.User. + Create(). + SaveX(ctx) + log.Println("user created:", u2) // create user vertex with its edges. u := client.User. Create(). + AddChildren(u2). SaveX(ctx) log.Println("user created:", u) // query edges. + u2, err = u.QueryChildren().First(ctx) + if err != nil { + log.Fatalf("failed querying children: %v", err) + } + log.Println("children found:", u2) + // Output: } diff --git a/entc/integration/customid/ent/group.go b/entc/integration/customid/ent/group.go index 6db6a8c15..25fd013e8 100644 --- a/entc/integration/customid/ent/group.go +++ b/entc/integration/customid/ent/group.go @@ -19,19 +19,25 @@ type Group struct { config // ID of the ent. ID int `json:"id,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the GroupQuery when eager-loading is set. + Edges struct { + // Users holds the value of the users edge. + Users []*User + } } // scanValues returns the types for scanning values from sql.Rows. func (*Group) scanValues() []interface{} { return []interface{}{ - &sql.NullInt64{}, + &sql.NullInt64{}, // id } } // assignValues assigns the values that were returned from sql.Rows (after scanning) // to the Group fields. func (gr *Group) assignValues(values ...interface{}) error { - if m, n := len(values), len(group.Columns); m != n { + if m, n := len(values), len(group.Columns); m < n { return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) } value, ok := values[0].(*sql.NullInt64) diff --git a/entc/integration/customid/ent/group/group.go b/entc/integration/customid/ent/group/group.go index a8c431d2d..a9fef33fa 100644 --- a/entc/integration/customid/ent/group/group.go +++ b/entc/integration/customid/ent/group/group.go @@ -21,7 +21,7 @@ const ( UsersInverseTable = "users" ) -// Columns holds all SQL columns are group fields. +// Columns holds all SQL columns for group fields. var Columns = []string{ FieldID, } diff --git a/entc/integration/customid/ent/group_query.go b/entc/integration/customid/ent/group_query.go index bad2b58f6..5ce7c16d8 100644 --- a/entc/integration/customid/ent/group_query.go +++ b/entc/integration/customid/ent/group_query.go @@ -8,6 +8,7 @@ package ent import ( "context" + "database/sql/driver" "errors" "fmt" "math" @@ -28,6 +29,8 @@ type GroupQuery struct { order []Order unique []string predicates []predicate.Group + // eager-loading edges. + withUsers *UserQuery // intermediate query. sql *sql.Selector } @@ -237,6 +240,17 @@ func (gq *GroupQuery) Clone() *GroupQuery { } } +// WithUsers tells the query-builder to eager-loads the nodes that are connected to +// the "users" edge. The optional arguments used to configure the query builder of the edge. +func (gq *GroupQuery) WithUsers(opts ...func(*UserQuery)) *GroupQuery { + query := &UserQuery{config: gq.config} + for _, opt := range opts { + opt(query) + } + gq.withUsers = query + return gq +} + // GroupBy used to group vertices by one or more fields/columns. // It is often used with aggregate functions, like: count, max, mean, min, sum. func (gq *GroupQuery) GroupBy(field string, fields ...string) *GroupGroupBy { @@ -262,7 +276,8 @@ func (gq *GroupQuery) sqlAll(ctx context.Context) ([]*Group, error) { spec.ScanValues = func() []interface{} { node := &Group{config: gq.config} nodes = append(nodes, node) - return node.scanValues() + values := node.scanValues() + return values } spec.Assign = func(values ...interface{}) error { if len(nodes) == 0 { @@ -274,6 +289,70 @@ func (gq *GroupQuery) sqlAll(ctx context.Context) ([]*Group, error) { if err := sqlgraph.QueryNodes(ctx, gq.driver, spec); err != nil { return nil, err } + + if query := gq.withUsers; query != nil { + fks := make([]driver.Value, 0, len(nodes)) + ids := make(map[int]*Group, len(nodes)) + for _, node := range nodes { + ids[node.ID] = node + fks = append(fks, node.ID) + } + var ( + edgeids []int + edges = make(map[int][]*Group) + ) + spec := &sqlgraph.EdgeQuerySpec{ + Edge: &sqlgraph.EdgeSpec{ + Inverse: false, + Table: group.UsersTable, + Columns: group.UsersPrimaryKey, + }, + Predicate: func(s *sql.Selector) { + s.Where(sql.InValues(group.UsersPrimaryKey[0], fks...)) + }, + + ScanValues: func() [2]interface{} { + return [2]interface{}{&sql.NullInt64{}, &sql.NullInt64{}} + }, + Assign: func(out, in interface{}) error { + eout, ok := out.(*sql.NullInt64) + if !ok || eout == nil { + return fmt.Errorf("unexpected id value for edge-out") + } + ein, ok := in.(*sql.NullInt64) + if !ok || ein == nil { + return fmt.Errorf("unexpected id value for edge-in") + } + outValue := int(eout.Int64) + inValue := int(eout.Int64) + node, ok := ids[outValue] + if !ok { + return fmt.Errorf("unexpected node id in edges: %v", outValue) + } + edgeids = append(edgeids, inValue) + edges[inValue] = append(edges[inValue], node) + return nil + }, + } + if err := sqlgraph.QueryEdges(ctx, gq.driver, spec); err != nil { + return nil, fmt.Errorf(`query edges "users": %v`, err) + } + query.Where(user.IDIn(edgeids...)) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + nodes, ok := edges[n.ID] + if !ok { + return nil, fmt.Errorf(`unexpected "users" node returned %v`, n.ID) + } + for i := range nodes { + nodes[i].Edges.Users = append(nodes[i].Edges.Users, n) + } + } + } + return nodes, nil } diff --git a/entc/integration/customid/ent/migrate/schema.go b/entc/integration/customid/ent/migrate/schema.go index a74888f71..0a57818fc 100644 --- a/entc/integration/customid/ent/migrate/schema.go +++ b/entc/integration/customid/ent/migrate/schema.go @@ -40,13 +40,22 @@ var ( // UsersColumns holds the columns for the "users" table. UsersColumns = []*schema.Column{ {Name: "id", Type: field.TypeInt, Increment: true}, + {Name: "parent_id", Type: field.TypeInt, Nullable: true}, } // UsersTable holds the schema information for the "users" table. UsersTable = &schema.Table{ - Name: "users", - Columns: UsersColumns, - PrimaryKey: []*schema.Column{UsersColumns[0]}, - ForeignKeys: []*schema.ForeignKey{}, + Name: "users", + Columns: UsersColumns, + PrimaryKey: []*schema.Column{UsersColumns[0]}, + ForeignKeys: []*schema.ForeignKey{ + { + Symbol: "users_users_children", + Columns: []*schema.Column{UsersColumns[1]}, + + RefColumns: []*schema.Column{UsersColumns[0]}, + OnDelete: schema.SetNull, + }, + }, } // GroupUsersColumns holds the columns for the "group_users" table. GroupUsersColumns = []*schema.Column{ @@ -85,6 +94,7 @@ var ( ) func init() { + UsersTable.ForeignKeys[0].RefTable = UsersTable GroupUsersTable.ForeignKeys[0].RefTable = GroupsTable GroupUsersTable.ForeignKeys[1].RefTable = UsersTable } diff --git a/entc/integration/customid/ent/schema/user.go b/entc/integration/customid/ent/schema/user.go index 2e8cb9693..19ba8e6ca 100644 --- a/entc/integration/customid/ent/schema/user.go +++ b/entc/integration/customid/ent/schema/user.go @@ -27,5 +27,8 @@ func (User) Edges() []ent.Edge { return []ent.Edge{ edge.From("groups", Group.Type). Ref("users"), + edge.To("children", User.Type). + From("parent"). + Unique(), } } diff --git a/entc/integration/customid/ent/user.go b/entc/integration/customid/ent/user.go index ad079d2d1..27209bd64 100644 --- a/entc/integration/customid/ent/user.go +++ b/entc/integration/customid/ent/user.go @@ -19,19 +19,37 @@ type User struct { config // ID of the ent. ID int `json:"id,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the UserQuery when eager-loading is set. + Edges struct { + // Groups holds the value of the groups edge. + Groups []*Group + // Parent holds the value of the parent edge. + Parent *User + // Children holds the value of the children edge. + Children []*User + } + parent_id *int } // scanValues returns the types for scanning values from sql.Rows. func (*User) scanValues() []interface{} { return []interface{}{ - &sql.NullInt64{}, + &sql.NullInt64{}, // id + } +} + +// fkValues returns the types for scanning foreign-keys values from sql.Rows. +func (*User) fkValues() []interface{} { + return []interface{}{ + &sql.NullInt64{}, // parent_id } } // assignValues assigns the values that were returned from sql.Rows (after scanning) // to the User fields. func (u *User) assignValues(values ...interface{}) error { - if m, n := len(values), len(user.Columns); m != n { + if m, n := len(values), len(user.Columns); m < n { return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) } value, ok := values[0].(*sql.NullInt64) @@ -40,6 +58,15 @@ func (u *User) assignValues(values ...interface{}) error { } u.ID = int(value.Int64) values = values[1:] + values = values[0:] + if len(values) == len(user.ForeignKeys) { + if value, ok := values[0].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for edge-field parent_id", value) + } else if value.Valid { + u.parent_id = new(int) + *u.parent_id = int(value.Int64) + } + } return nil } @@ -48,6 +75,16 @@ func (u *User) QueryGroups() *GroupQuery { return (&UserClient{u.config}).QueryGroups(u) } +// QueryParent queries the parent edge of the User. +func (u *User) QueryParent() *UserQuery { + return (&UserClient{u.config}).QueryParent(u) +} + +// QueryChildren queries the children edge of the User. +func (u *User) QueryChildren() *UserQuery { + return (&UserClient{u.config}).QueryChildren(u) +} + // Update returns a builder for updating this User. // Note that, you need to call User.Unwrap() before calling this method, if this User // was returned from a transaction, and the transaction was committed or rolled back. diff --git a/entc/integration/customid/ent/user/user.go b/entc/integration/customid/ent/user/user.go index 66631ba98..ec8d304f5 100644 --- a/entc/integration/customid/ent/user/user.go +++ b/entc/integration/customid/ent/user/user.go @@ -19,13 +19,26 @@ const ( // GroupsInverseTable is the table name for the Group entity. // It exists in this package in order to avoid circular dependency with the "group" package. GroupsInverseTable = "groups" + // ParentTable is the table the holds the parent relation/edge. + ParentTable = "users" + // ParentColumn is the table column denoting the parent relation/edge. + ParentColumn = "parent_id" + // ChildrenTable is the table the holds the children relation/edge. + ChildrenTable = "users" + // ChildrenColumn is the table column denoting the children relation/edge. + ChildrenColumn = "parent_id" ) -// Columns holds all SQL columns are user fields. +// Columns holds all SQL columns for user fields. var Columns = []string{ FieldID, } +// ForeignKeys holds the SQL foreign-keys that are owned by the User type. +var ForeignKeys = []string{ + "parent_id", +} + var ( // GroupsPrimaryKey and GroupsColumn2 are the table columns denoting the // primary key for the groups relation (M2M). diff --git a/entc/integration/customid/ent/user/where.go b/entc/integration/customid/ent/user/where.go index 1303298b7..a5e8c3565 100644 --- a/entc/integration/customid/ent/user/where.go +++ b/entc/integration/customid/ent/user/where.go @@ -135,6 +135,66 @@ func HasGroupsWith(preds ...predicate.Group) predicate.User { ) } +// HasParent applies the HasEdge predicate on the "parent" edge. +func HasParent() predicate.User { + return predicate.User(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(ParentTable, FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, ParentTable, ParentColumn), + ) + sqlgraph.HasNeighbors(s, step) + }, + ) +} + +// HasParentWith applies the HasEdge predicate on the "parent" edge with a given conditions (other predicates). +func HasParentWith(preds ...predicate.User) predicate.User { + return predicate.User(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(Table, FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, ParentTable, ParentColumn), + ) + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }, + ) +} + +// HasChildren applies the HasEdge predicate on the "children" edge. +func HasChildren() predicate.User { + return predicate.User(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(ChildrenTable, FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, ChildrenTable, ChildrenColumn), + ) + sqlgraph.HasNeighbors(s, step) + }, + ) +} + +// HasChildrenWith applies the HasEdge predicate on the "children" edge with a given conditions (other predicates). +func HasChildrenWith(preds ...predicate.User) predicate.User { + return predicate.User(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(Table, FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, ChildrenTable, ChildrenColumn), + ) + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }, + ) +} + // And groups list of predicates with the AND operator between them. func And(predicates ...predicate.User) predicate.User { return predicate.User( diff --git a/entc/integration/customid/ent/user_create.go b/entc/integration/customid/ent/user_create.go index ea3b320d4..db085d141 100644 --- a/entc/integration/customid/ent/user_create.go +++ b/entc/integration/customid/ent/user_create.go @@ -8,6 +8,7 @@ package ent import ( "context" + "errors" "github.com/facebookincubator/ent/dialect/sql/sqlgraph" "github.com/facebookincubator/ent/entc/integration/customid/ent/group" @@ -18,8 +19,10 @@ import ( // UserCreate is the builder for creating a User entity. type UserCreate struct { config - id *int - groups map[int]struct{} + id *int + groups map[int]struct{} + parent map[int]struct{} + children map[int]struct{} } // SetID sets the id field. @@ -48,8 +51,53 @@ func (uc *UserCreate) AddGroups(g ...*Group) *UserCreate { return uc.AddGroupIDs(ids...) } +// SetParentID sets the parent edge to User by id. +func (uc *UserCreate) SetParentID(id int) *UserCreate { + if uc.parent == nil { + uc.parent = make(map[int]struct{}) + } + uc.parent[id] = struct{}{} + return uc +} + +// SetNillableParentID sets the parent edge to User by id if the given value is not nil. +func (uc *UserCreate) SetNillableParentID(id *int) *UserCreate { + if id != nil { + uc = uc.SetParentID(*id) + } + return uc +} + +// SetParent sets the parent edge to User. +func (uc *UserCreate) SetParent(u *User) *UserCreate { + return uc.SetParentID(u.ID) +} + +// AddChildIDs adds the children edge to User by ids. +func (uc *UserCreate) AddChildIDs(ids ...int) *UserCreate { + if uc.children == nil { + uc.children = make(map[int]struct{}) + } + for i := range ids { + uc.children[ids[i]] = struct{}{} + } + return uc +} + +// AddChildren adds the children edges to User. +func (uc *UserCreate) AddChildren(u ...*User) *UserCreate { + ids := make([]int, len(u)) + for i := range u { + ids[i] = u[i].ID + } + return uc.AddChildIDs(ids...) +} + // Save creates the User in the database. func (uc *UserCreate) Save(ctx context.Context) (*User, error) { + if len(uc.parent) > 1 { + return nil, errors.New("ent: multiple assignments on a unique edge \"parent\"") + } return uc.sqlSave(ctx) } @@ -96,6 +144,44 @@ func (uc *UserCreate) sqlSave(ctx context.Context) (*User, error) { } spec.Edges = append(spec.Edges, edge) } + if nodes := uc.parent; len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: user.ParentTable, + Columns: []string{user.ParentColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: &sqlgraph.FieldSpec{ + Type: field.TypeInt, + Column: user.FieldID, + }, + }, + } + for k, _ := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + spec.Edges = append(spec.Edges, edge) + } + if nodes := uc.children; len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: user.ChildrenTable, + Columns: []string{user.ChildrenColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: &sqlgraph.FieldSpec{ + Type: field.TypeInt, + Column: user.FieldID, + }, + }, + } + for k, _ := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + spec.Edges = append(spec.Edges, edge) + } if err := sqlgraph.CreateNode(ctx, uc.driver, spec); err != nil { if cerr, ok := isSQLConstraintError(err); ok { err = cerr diff --git a/entc/integration/customid/ent/user_query.go b/entc/integration/customid/ent/user_query.go index c00229dc8..f5efa24d4 100644 --- a/entc/integration/customid/ent/user_query.go +++ b/entc/integration/customid/ent/user_query.go @@ -8,6 +8,7 @@ package ent import ( "context" + "database/sql/driver" "errors" "fmt" "math" @@ -28,6 +29,11 @@ type UserQuery struct { order []Order unique []string predicates []predicate.User + // eager-loading edges. + withGroups *GroupQuery + withParent *UserQuery + withChildren *UserQuery + withFKs bool // intermediate query. sql *sql.Selector } @@ -68,6 +74,30 @@ func (uq *UserQuery) QueryGroups() *GroupQuery { return query } +// QueryParent chains the current query on the parent edge. +func (uq *UserQuery) QueryParent() *UserQuery { + query := &UserQuery{config: uq.config} + step := sqlgraph.NewStep( + sqlgraph.From(user.Table, user.FieldID, uq.sqlQuery()), + sqlgraph.To(user.Table, user.FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, user.ParentTable, user.ParentColumn), + ) + query.sql = sqlgraph.SetNeighbors(uq.driver.Dialect(), step) + return query +} + +// QueryChildren chains the current query on the children edge. +func (uq *UserQuery) QueryChildren() *UserQuery { + query := &UserQuery{config: uq.config} + step := sqlgraph.NewStep( + sqlgraph.From(user.Table, user.FieldID, uq.sqlQuery()), + sqlgraph.To(user.Table, user.FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, user.ChildrenTable, user.ChildrenColumn), + ) + query.sql = sqlgraph.SetNeighbors(uq.driver.Dialect(), step) + return query +} + // First returns the first User entity in the query. Returns *ErrNotFound when no user was found. func (uq *UserQuery) First(ctx context.Context) (*User, error) { us, err := uq.Limit(1).All(ctx) @@ -237,6 +267,39 @@ func (uq *UserQuery) Clone() *UserQuery { } } +// WithGroups tells the query-builder to eager-loads the nodes that are connected to +// the "groups" edge. The optional arguments used to configure the query builder of the edge. +func (uq *UserQuery) WithGroups(opts ...func(*GroupQuery)) *UserQuery { + query := &GroupQuery{config: uq.config} + for _, opt := range opts { + opt(query) + } + uq.withGroups = query + return uq +} + +// WithParent tells the query-builder to eager-loads the nodes that are connected to +// the "parent" edge. The optional arguments used to configure the query builder of the edge. +func (uq *UserQuery) WithParent(opts ...func(*UserQuery)) *UserQuery { + query := &UserQuery{config: uq.config} + for _, opt := range opts { + opt(query) + } + uq.withParent = query + return uq +} + +// WithChildren tells the query-builder to eager-loads the nodes that are connected to +// the "children" edge. The optional arguments used to configure the query builder of the edge. +func (uq *UserQuery) WithChildren(opts ...func(*UserQuery)) *UserQuery { + query := &UserQuery{config: uq.config} + for _, opt := range opts { + opt(query) + } + uq.withChildren = query + return uq +} + // GroupBy used to group vertices by one or more fields/columns. // It is often used with aggregate functions, like: count, max, mean, min, sum. func (uq *UserQuery) GroupBy(field string, fields ...string) *UserGroupBy { @@ -256,13 +319,24 @@ func (uq *UserQuery) Select(field string, fields ...string) *UserSelect { func (uq *UserQuery) sqlAll(ctx context.Context) ([]*User, error) { var ( - nodes []*User - spec = uq.querySpec() + nodes []*User + withFKs = uq.withFKs + spec = uq.querySpec() ) + if uq.withParent != nil { + withFKs = true + } + if withFKs { + spec.Node.Columns = append(spec.Node.Columns, user.ForeignKeys...) + } spec.ScanValues = func() []interface{} { node := &User{config: uq.config} nodes = append(nodes, node) - return node.scanValues() + values := node.scanValues() + if withFKs { + values = append(values, node.fkValues()...) + } + return values } spec.Assign = func(values ...interface{}) error { if len(nodes) == 0 { @@ -274,6 +348,123 @@ func (uq *UserQuery) sqlAll(ctx context.Context) ([]*User, error) { if err := sqlgraph.QueryNodes(ctx, uq.driver, spec); err != nil { return nil, err } + + if query := uq.withGroups; query != nil { + fks := make([]driver.Value, 0, len(nodes)) + ids := make(map[int]*User, len(nodes)) + for _, node := range nodes { + ids[node.ID] = node + fks = append(fks, node.ID) + } + var ( + edgeids []int + edges = make(map[int][]*User) + ) + spec := &sqlgraph.EdgeQuerySpec{ + Edge: &sqlgraph.EdgeSpec{ + Inverse: true, + Table: user.GroupsTable, + Columns: user.GroupsPrimaryKey, + }, + Predicate: func(s *sql.Selector) { + s.Where(sql.InValues(user.GroupsPrimaryKey[1], fks...)) + }, + + ScanValues: func() [2]interface{} { + return [2]interface{}{&sql.NullInt64{}, &sql.NullInt64{}} + }, + Assign: func(out, in interface{}) error { + eout, ok := out.(*sql.NullInt64) + if !ok || eout == nil { + return fmt.Errorf("unexpected id value for edge-out") + } + ein, ok := in.(*sql.NullInt64) + if !ok || ein == nil { + return fmt.Errorf("unexpected id value for edge-in") + } + outValue := int(eout.Int64) + inValue := int(eout.Int64) + node, ok := ids[outValue] + if !ok { + return fmt.Errorf("unexpected node id in edges: %v", outValue) + } + edgeids = append(edgeids, inValue) + edges[inValue] = append(edges[inValue], node) + return nil + }, + } + if err := sqlgraph.QueryEdges(ctx, uq.driver, spec); err != nil { + return nil, fmt.Errorf(`query edges "groups": %v`, err) + } + query.Where(group.IDIn(edgeids...)) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + nodes, ok := edges[n.ID] + if !ok { + return nil, fmt.Errorf(`unexpected "groups" node returned %v`, n.ID) + } + for i := range nodes { + nodes[i].Edges.Groups = append(nodes[i].Edges.Groups, n) + } + } + } + + if query := uq.withParent; query != nil { + ids := make([]int, 0, len(nodes)) + nodeids := make(map[int][]*User) + for i := range nodes { + if fk := nodes[i].parent_id; fk != nil { + ids = append(ids, *fk) + nodeids[*fk] = append(nodeids[*fk], nodes[i]) + } + } + query.Where(user.IDIn(ids...)) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + nodes, ok := nodeids[n.ID] + if !ok { + return nil, fmt.Errorf(`unexpected foreign-key "parent_id" returned %v`, n.ID) + } + for i := range nodes { + nodes[i].Edges.Parent = n + } + } + } + + if query := uq.withChildren; query != nil { + fks := make([]driver.Value, 0, len(nodes)) + nodeids := make(map[int]*User) + for i := range nodes { + fks = append(fks, nodes[i].ID) + nodeids[nodes[i].ID] = nodes[i] + } + query.withFKs = true + query.Where(predicate.User(func(s *sql.Selector) { + s.Where(sql.InValues(user.ChildrenColumn, fks...)) + })) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + fk := n.parent_id + if fk == nil { + return nil, fmt.Errorf(`foreign-key "parent_id" is nil for node %v`, n.ID) + } + node, ok := nodeids[*fk] + if !ok { + return nil, fmt.Errorf(`unexpected foreign-key "parent_id" returned %v for node %v`, *fk, n.ID) + } + node.Edges.Children = append(node.Edges.Children, n) + } + } + return nodes, nil } diff --git a/entc/integration/customid/ent/user_update.go b/entc/integration/customid/ent/user_update.go index 23bac331b..71dd12ffa 100644 --- a/entc/integration/customid/ent/user_update.go +++ b/entc/integration/customid/ent/user_update.go @@ -8,6 +8,7 @@ package ent import ( "context" + "errors" "github.com/facebookincubator/ent/dialect/sql" "github.com/facebookincubator/ent/dialect/sql/sqlgraph" @@ -20,9 +21,13 @@ import ( // UserUpdate is the builder for updating User entities. type UserUpdate struct { config - groups map[int]struct{} - removedGroups map[int]struct{} - predicates []predicate.User + groups map[int]struct{} + parent map[int]struct{} + children map[int]struct{} + removedGroups map[int]struct{} + clearedParent bool + removedChildren map[int]struct{} + predicates []predicate.User } // Where adds a new predicate for the builder. @@ -51,6 +56,48 @@ func (uu *UserUpdate) AddGroups(g ...*Group) *UserUpdate { return uu.AddGroupIDs(ids...) } +// SetParentID sets the parent edge to User by id. +func (uu *UserUpdate) SetParentID(id int) *UserUpdate { + if uu.parent == nil { + uu.parent = make(map[int]struct{}) + } + uu.parent[id] = struct{}{} + return uu +} + +// SetNillableParentID sets the parent edge to User by id if the given value is not nil. +func (uu *UserUpdate) SetNillableParentID(id *int) *UserUpdate { + if id != nil { + uu = uu.SetParentID(*id) + } + return uu +} + +// SetParent sets the parent edge to User. +func (uu *UserUpdate) SetParent(u *User) *UserUpdate { + return uu.SetParentID(u.ID) +} + +// AddChildIDs adds the children edge to User by ids. +func (uu *UserUpdate) AddChildIDs(ids ...int) *UserUpdate { + if uu.children == nil { + uu.children = make(map[int]struct{}) + } + for i := range ids { + uu.children[ids[i]] = struct{}{} + } + return uu +} + +// AddChildren adds the children edges to User. +func (uu *UserUpdate) AddChildren(u ...*User) *UserUpdate { + ids := make([]int, len(u)) + for i := range u { + ids[i] = u[i].ID + } + return uu.AddChildIDs(ids...) +} + // RemoveGroupIDs removes the groups edge to Group by ids. func (uu *UserUpdate) RemoveGroupIDs(ids ...int) *UserUpdate { if uu.removedGroups == nil { @@ -71,8 +118,37 @@ func (uu *UserUpdate) RemoveGroups(g ...*Group) *UserUpdate { return uu.RemoveGroupIDs(ids...) } +// ClearParent clears the parent edge to User. +func (uu *UserUpdate) ClearParent() *UserUpdate { + uu.clearedParent = true + return uu +} + +// RemoveChildIDs removes the children edge to User by ids. +func (uu *UserUpdate) RemoveChildIDs(ids ...int) *UserUpdate { + if uu.removedChildren == nil { + uu.removedChildren = make(map[int]struct{}) + } + for i := range ids { + uu.removedChildren[ids[i]] = struct{}{} + } + return uu +} + +// RemoveChildren removes children edges to User. +func (uu *UserUpdate) RemoveChildren(u ...*User) *UserUpdate { + ids := make([]int, len(u)) + for i := range u { + ids[i] = u[i].ID + } + return uu.RemoveChildIDs(ids...) +} + // Save executes the query and returns the number of rows/vertices matched by this operation. func (uu *UserUpdate) Save(ctx context.Context) (int, error) { + if len(uu.parent) > 1 { + return 0, errors.New("ent: multiple assignments on a unique edge \"parent\"") + } return uu.sqlSave(ctx) } @@ -154,6 +230,79 @@ func (uu *UserUpdate) sqlSave(ctx context.Context) (n int, err error) { } spec.Edges.Add = append(spec.Edges.Add, edge) } + if uu.clearedParent { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: user.ParentTable, + Columns: []string{user.ParentColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: &sqlgraph.FieldSpec{ + Type: field.TypeInt, + Column: user.FieldID, + }, + }, + } + spec.Edges.Clear = append(spec.Edges.Clear, edge) + } + if nodes := uu.parent; len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: user.ParentTable, + Columns: []string{user.ParentColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: &sqlgraph.FieldSpec{ + Type: field.TypeInt, + Column: user.FieldID, + }, + }, + } + for k, _ := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + spec.Edges.Add = append(spec.Edges.Add, edge) + } + if nodes := uu.removedChildren; len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: user.ChildrenTable, + Columns: []string{user.ChildrenColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: &sqlgraph.FieldSpec{ + Type: field.TypeInt, + Column: user.FieldID, + }, + }, + } + for k, _ := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + spec.Edges.Clear = append(spec.Edges.Clear, edge) + } + if nodes := uu.children; len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: user.ChildrenTable, + Columns: []string{user.ChildrenColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: &sqlgraph.FieldSpec{ + Type: field.TypeInt, + Column: user.FieldID, + }, + }, + } + for k, _ := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + spec.Edges.Add = append(spec.Edges.Add, edge) + } if n, err = sqlgraph.UpdateNodes(ctx, uu.driver, spec); err != nil { if cerr, ok := isSQLConstraintError(err); ok { err = cerr @@ -166,9 +315,13 @@ func (uu *UserUpdate) sqlSave(ctx context.Context) (n int, err error) { // UserUpdateOne is the builder for updating a single User entity. type UserUpdateOne struct { config - id int - groups map[int]struct{} - removedGroups map[int]struct{} + id int + groups map[int]struct{} + parent map[int]struct{} + children map[int]struct{} + removedGroups map[int]struct{} + clearedParent bool + removedChildren map[int]struct{} } // AddGroupIDs adds the groups edge to Group by ids. @@ -191,6 +344,48 @@ func (uuo *UserUpdateOne) AddGroups(g ...*Group) *UserUpdateOne { return uuo.AddGroupIDs(ids...) } +// SetParentID sets the parent edge to User by id. +func (uuo *UserUpdateOne) SetParentID(id int) *UserUpdateOne { + if uuo.parent == nil { + uuo.parent = make(map[int]struct{}) + } + uuo.parent[id] = struct{}{} + return uuo +} + +// SetNillableParentID sets the parent edge to User by id if the given value is not nil. +func (uuo *UserUpdateOne) SetNillableParentID(id *int) *UserUpdateOne { + if id != nil { + uuo = uuo.SetParentID(*id) + } + return uuo +} + +// SetParent sets the parent edge to User. +func (uuo *UserUpdateOne) SetParent(u *User) *UserUpdateOne { + return uuo.SetParentID(u.ID) +} + +// AddChildIDs adds the children edge to User by ids. +func (uuo *UserUpdateOne) AddChildIDs(ids ...int) *UserUpdateOne { + if uuo.children == nil { + uuo.children = make(map[int]struct{}) + } + for i := range ids { + uuo.children[ids[i]] = struct{}{} + } + return uuo +} + +// AddChildren adds the children edges to User. +func (uuo *UserUpdateOne) AddChildren(u ...*User) *UserUpdateOne { + ids := make([]int, len(u)) + for i := range u { + ids[i] = u[i].ID + } + return uuo.AddChildIDs(ids...) +} + // RemoveGroupIDs removes the groups edge to Group by ids. func (uuo *UserUpdateOne) RemoveGroupIDs(ids ...int) *UserUpdateOne { if uuo.removedGroups == nil { @@ -211,8 +406,37 @@ func (uuo *UserUpdateOne) RemoveGroups(g ...*Group) *UserUpdateOne { return uuo.RemoveGroupIDs(ids...) } +// ClearParent clears the parent edge to User. +func (uuo *UserUpdateOne) ClearParent() *UserUpdateOne { + uuo.clearedParent = true + return uuo +} + +// RemoveChildIDs removes the children edge to User by ids. +func (uuo *UserUpdateOne) RemoveChildIDs(ids ...int) *UserUpdateOne { + if uuo.removedChildren == nil { + uuo.removedChildren = make(map[int]struct{}) + } + for i := range ids { + uuo.removedChildren[ids[i]] = struct{}{} + } + return uuo +} + +// RemoveChildren removes children edges to User. +func (uuo *UserUpdateOne) RemoveChildren(u ...*User) *UserUpdateOne { + ids := make([]int, len(u)) + for i := range u { + ids[i] = u[i].ID + } + return uuo.RemoveChildIDs(ids...) +} + // Save executes the query and returns the updated entity. func (uuo *UserUpdateOne) Save(ctx context.Context) (*User, error) { + if len(uuo.parent) > 1 { + return nil, errors.New("ent: multiple assignments on a unique edge \"parent\"") + } return uuo.sqlSave(ctx) } @@ -288,6 +512,79 @@ func (uuo *UserUpdateOne) sqlSave(ctx context.Context) (u *User, err error) { } spec.Edges.Add = append(spec.Edges.Add, edge) } + if uuo.clearedParent { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: user.ParentTable, + Columns: []string{user.ParentColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: &sqlgraph.FieldSpec{ + Type: field.TypeInt, + Column: user.FieldID, + }, + }, + } + spec.Edges.Clear = append(spec.Edges.Clear, edge) + } + if nodes := uuo.parent; len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: user.ParentTable, + Columns: []string{user.ParentColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: &sqlgraph.FieldSpec{ + Type: field.TypeInt, + Column: user.FieldID, + }, + }, + } + for k, _ := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + spec.Edges.Add = append(spec.Edges.Add, edge) + } + if nodes := uuo.removedChildren; len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: user.ChildrenTable, + Columns: []string{user.ChildrenColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: &sqlgraph.FieldSpec{ + Type: field.TypeInt, + Column: user.FieldID, + }, + }, + } + for k, _ := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + spec.Edges.Clear = append(spec.Edges.Clear, edge) + } + if nodes := uuo.children; len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: user.ChildrenTable, + Columns: []string{user.ChildrenColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: &sqlgraph.FieldSpec{ + Type: field.TypeInt, + Column: user.FieldID, + }, + }, + } + for k, _ := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + spec.Edges.Add = append(spec.Edges.Add, edge) + } u = &User{config: uuo.config} spec.Assign = u.assignValues spec.ScanValues = u.scanValues() diff --git a/entc/integration/ent/card.go b/entc/integration/ent/card.go index 10f83fca1..2bcbb3410 100644 --- a/entc/integration/ent/card.go +++ b/entc/integration/ent/card.go @@ -29,6 +29,13 @@ type Card struct { Number string `json:"number,omitempty"` // Name holds the value of the "name" field. Name string `json:"name,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the CardQuery when eager-loading is set. + Edges struct { + // Owner holds the value of the owner edge. + Owner *User + } + owner_id *string // StaticField defined by templates. StaticField string `json:"boring,omitempty"` @@ -37,18 +44,25 @@ type Card struct { // scanValues returns the types for scanning values from sql.Rows. func (*Card) scanValues() []interface{} { return []interface{}{ - &sql.NullInt64{}, - &sql.NullTime{}, - &sql.NullTime{}, - &sql.NullString{}, - &sql.NullString{}, + &sql.NullInt64{}, // id + &sql.NullTime{}, // create_time + &sql.NullTime{}, // update_time + &sql.NullString{}, // number + &sql.NullString{}, // name + } +} + +// fkValues returns the types for scanning foreign-keys values from sql.Rows. +func (*Card) fkValues() []interface{} { + return []interface{}{ + &sql.NullInt64{}, // owner_id } } // assignValues assigns the values that were returned from sql.Rows (after scanning) // to the Card fields. func (c *Card) assignValues(values ...interface{}) error { - if m, n := len(values), len(card.Columns); m != n { + if m, n := len(values), len(card.Columns); m < n { return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) } value, ok := values[0].(*sql.NullInt64) @@ -77,6 +91,15 @@ func (c *Card) assignValues(values ...interface{}) error { } else if value.Valid { c.Name = value.String } + values = values[4:] + if len(values) == len(card.ForeignKeys) { + if value, ok := values[0].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for edge-field owner_id", value) + } else if value.Valid { + c.owner_id = new(string) + *c.owner_id = strconv.FormatInt(value.Int64, 10) + } + } return nil } diff --git a/entc/integration/ent/card/card.go b/entc/integration/ent/card/card.go index b152b0990..cb539cd8f 100644 --- a/entc/integration/ent/card/card.go +++ b/entc/integration/ent/card/card.go @@ -38,7 +38,7 @@ const ( OwnerColumn = "owner_id" ) -// Columns holds all SQL columns are card fields. +// Columns holds all SQL columns for card fields. var Columns = []string{ FieldID, FieldCreateTime, @@ -47,6 +47,11 @@ var Columns = []string{ FieldName, } +// ForeignKeys holds the SQL foreign-keys that are owned by the Card type. +var ForeignKeys = []string{ + "owner_id", +} + var ( mixin = schema.Card{}.Mixin() mixinFields = [...][]ent.Field{ diff --git a/entc/integration/ent/card_query.go b/entc/integration/ent/card_query.go index c54b95ebb..deef3c038 100644 --- a/entc/integration/ent/card_query.go +++ b/entc/integration/ent/card_query.go @@ -28,6 +28,9 @@ type CardQuery struct { order []Order unique []string predicates []predicate.Card + // eager-loading edges. + withOwner *UserQuery + withFKs bool // intermediate query. sql *sql.Selector } @@ -237,6 +240,17 @@ func (cq *CardQuery) Clone() *CardQuery { } } +// WithOwner tells the query-builder to eager-loads the nodes that are connected to +// the "owner" edge. The optional arguments used to configure the query builder of the edge. +func (cq *CardQuery) WithOwner(opts ...func(*UserQuery)) *CardQuery { + query := &UserQuery{config: cq.config} + for _, opt := range opts { + opt(query) + } + cq.withOwner = query + return cq +} + // GroupBy used to group vertices by one or more fields/columns. // It is often used with aggregate functions, like: count, max, mean, min, sum. // @@ -280,13 +294,24 @@ func (cq *CardQuery) Select(field string, fields ...string) *CardSelect { func (cq *CardQuery) sqlAll(ctx context.Context) ([]*Card, error) { var ( - nodes []*Card - spec = cq.querySpec() + nodes []*Card + withFKs = cq.withFKs + spec = cq.querySpec() ) + if cq.withOwner != nil { + withFKs = true + } + if withFKs { + spec.Node.Columns = append(spec.Node.Columns, card.ForeignKeys...) + } spec.ScanValues = func() []interface{} { node := &Card{config: cq.config} nodes = append(nodes, node) - return node.scanValues() + values := node.scanValues() + if withFKs { + values = append(values, node.fkValues()...) + } + return values } spec.Assign = func(values ...interface{}) error { if len(nodes) == 0 { @@ -298,6 +323,32 @@ func (cq *CardQuery) sqlAll(ctx context.Context) ([]*Card, error) { if err := sqlgraph.QueryNodes(ctx, cq.driver, spec); err != nil { return nil, err } + + if query := cq.withOwner; query != nil { + ids := make([]string, 0, len(nodes)) + nodeids := make(map[string][]*Card) + for i := range nodes { + if fk := nodes[i].owner_id; fk != nil { + ids = append(ids, *fk) + nodeids[*fk] = append(nodeids[*fk], nodes[i]) + } + } + query.Where(user.IDIn(ids...)) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + nodes, ok := nodeids[n.ID] + if !ok { + return nil, fmt.Errorf(`unexpected foreign-key "owner_id" returned %v`, n.ID) + } + for i := range nodes { + nodes[i].Edges.Owner = n + } + } + } + return nodes, nil } diff --git a/entc/integration/ent/comment.go b/entc/integration/ent/comment.go index b5e25816d..c08847431 100644 --- a/entc/integration/ent/comment.go +++ b/entc/integration/ent/comment.go @@ -31,17 +31,17 @@ type Comment struct { // scanValues returns the types for scanning values from sql.Rows. func (*Comment) scanValues() []interface{} { return []interface{}{ - &sql.NullInt64{}, - &sql.NullInt64{}, - &sql.NullFloat64{}, - &sql.NullInt64{}, + &sql.NullInt64{}, // id + &sql.NullInt64{}, // unique_int + &sql.NullFloat64{}, // unique_float + &sql.NullInt64{}, // nillable_int } } // assignValues assigns the values that were returned from sql.Rows (after scanning) // to the Comment fields. func (c *Comment) assignValues(values ...interface{}) error { - if m, n := len(values), len(comment.Columns); m != n { + if m, n := len(values), len(comment.Columns); m < n { return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) } value, ok := values[0].(*sql.NullInt64) diff --git a/entc/integration/ent/comment/comment.go b/entc/integration/ent/comment/comment.go index b482e7d4e..55f6694ba 100644 --- a/entc/integration/ent/comment/comment.go +++ b/entc/integration/ent/comment/comment.go @@ -22,7 +22,7 @@ const ( Table = "comments" ) -// Columns holds all SQL columns are comment fields. +// Columns holds all SQL columns for comment fields. var Columns = []string{ FieldID, FieldUniqueInt, diff --git a/entc/integration/ent/comment_query.go b/entc/integration/ent/comment_query.go index 475aad1dc..41103b942 100644 --- a/entc/integration/ent/comment_query.go +++ b/entc/integration/ent/comment_query.go @@ -273,7 +273,8 @@ func (cq *CommentQuery) sqlAll(ctx context.Context) ([]*Comment, error) { spec.ScanValues = func() []interface{} { node := &Comment{config: cq.config} nodes = append(nodes, node) - return node.scanValues() + values := node.scanValues() + return values } spec.Assign = func(values ...interface{}) error { if len(nodes) == 0 { diff --git a/entc/integration/ent/fieldtype.go b/entc/integration/ent/fieldtype.go index 0f2162e84..20c75c2b7 100644 --- a/entc/integration/ent/fieldtype.go +++ b/entc/integration/ent/fieldtype.go @@ -59,31 +59,31 @@ type FieldType struct { // scanValues returns the types for scanning values from sql.Rows. func (*FieldType) scanValues() []interface{} { return []interface{}{ - &sql.NullInt64{}, - &sql.NullInt64{}, - &sql.NullInt64{}, - &sql.NullInt64{}, - &sql.NullInt64{}, - &sql.NullInt64{}, - &sql.NullInt64{}, - &sql.NullInt64{}, - &sql.NullInt64{}, - &sql.NullInt64{}, - &sql.NullInt64{}, - &sql.NullInt64{}, - &sql.NullInt64{}, - &sql.NullInt64{}, - &sql.NullInt64{}, - &sql.NullInt64{}, - &sql.NullInt64{}, - &sql.NullString{}, + &sql.NullInt64{}, // id + &sql.NullInt64{}, // int + &sql.NullInt64{}, // int8 + &sql.NullInt64{}, // int16 + &sql.NullInt64{}, // int32 + &sql.NullInt64{}, // int64 + &sql.NullInt64{}, // optional_int + &sql.NullInt64{}, // optional_int8 + &sql.NullInt64{}, // optional_int16 + &sql.NullInt64{}, // optional_int32 + &sql.NullInt64{}, // optional_int64 + &sql.NullInt64{}, // nillable_int + &sql.NullInt64{}, // nillable_int8 + &sql.NullInt64{}, // nillable_int16 + &sql.NullInt64{}, // nillable_int32 + &sql.NullInt64{}, // nillable_int64 + &sql.NullInt64{}, // validate_optional_int32 + &sql.NullString{}, // state } } // assignValues assigns the values that were returned from sql.Rows (after scanning) // to the FieldType fields. func (ft *FieldType) assignValues(values ...interface{}) error { - if m, n := len(values), len(fieldtype.Columns); m != n { + if m, n := len(values), len(fieldtype.Columns); m < n { return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) } value, ok := values[0].(*sql.NullInt64) diff --git a/entc/integration/ent/fieldtype/fieldtype.go b/entc/integration/ent/fieldtype/fieldtype.go index 6dde800d1..12706947b 100644 --- a/entc/integration/ent/fieldtype/fieldtype.go +++ b/entc/integration/ent/fieldtype/fieldtype.go @@ -56,7 +56,7 @@ const ( Table = "field_types" ) -// Columns holds all SQL columns are fieldtype fields. +// Columns holds all SQL columns for fieldtype fields. var Columns = []string{ FieldID, FieldInt, diff --git a/entc/integration/ent/fieldtype_query.go b/entc/integration/ent/fieldtype_query.go index 4f17d1662..567984960 100644 --- a/entc/integration/ent/fieldtype_query.go +++ b/entc/integration/ent/fieldtype_query.go @@ -273,7 +273,8 @@ func (ftq *FieldTypeQuery) sqlAll(ctx context.Context) ([]*FieldType, error) { spec.ScanValues = func() []interface{} { node := &FieldType{config: ftq.config} nodes = append(nodes, node) - return node.scanValues() + values := node.scanValues() + return values } spec.Assign = func(values ...interface{}) error { if len(nodes) == 0 { diff --git a/entc/integration/ent/file.go b/entc/integration/ent/file.go index a1eac32fd..52cb4155a 100644 --- a/entc/integration/ent/file.go +++ b/entc/integration/ent/file.go @@ -28,23 +28,43 @@ type File struct { User *string `json:"user,omitempty"` // Group holds the value of the "group" field. Group string `json:"group,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the FileQuery when eager-loading is set. + Edges struct { + // Owner holds the value of the owner edge. + Owner *User + // Type holds the value of the type edge. + Type *FileType + } + type_id *string + group_file_id *string + owner_id *string } // scanValues returns the types for scanning values from sql.Rows. func (*File) scanValues() []interface{} { return []interface{}{ - &sql.NullInt64{}, - &sql.NullInt64{}, - &sql.NullString{}, - &sql.NullString{}, - &sql.NullString{}, + &sql.NullInt64{}, // id + &sql.NullInt64{}, // size + &sql.NullString{}, // name + &sql.NullString{}, // user + &sql.NullString{}, // group + } +} + +// fkValues returns the types for scanning foreign-keys values from sql.Rows. +func (*File) fkValues() []interface{} { + return []interface{}{ + &sql.NullInt64{}, // type_id + &sql.NullInt64{}, // group_file_id + &sql.NullInt64{}, // owner_id } } // assignValues assigns the values that were returned from sql.Rows (after scanning) // to the File fields. func (f *File) assignValues(values ...interface{}) error { - if m, n := len(values), len(file.Columns); m != n { + if m, n := len(values), len(file.Columns); m < n { return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) } value, ok := values[0].(*sql.NullInt64) @@ -74,6 +94,27 @@ func (f *File) assignValues(values ...interface{}) error { } else if value.Valid { f.Group = value.String } + values = values[4:] + if len(values) == len(file.ForeignKeys) { + if value, ok := values[0].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for edge-field type_id", value) + } else if value.Valid { + f.type_id = new(string) + *f.type_id = strconv.FormatInt(value.Int64, 10) + } + if value, ok := values[1].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for edge-field group_file_id", value) + } else if value.Valid { + f.group_file_id = new(string) + *f.group_file_id = strconv.FormatInt(value.Int64, 10) + } + if value, ok := values[2].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for edge-field owner_id", value) + } else if value.Valid { + f.owner_id = new(string) + *f.owner_id = strconv.FormatInt(value.Int64, 10) + } + } return nil } diff --git a/entc/integration/ent/file/file.go b/entc/integration/ent/file/file.go index 28809d038..520ddd8ed 100644 --- a/entc/integration/ent/file/file.go +++ b/entc/integration/ent/file/file.go @@ -42,7 +42,7 @@ const ( TypeColumn = "type_id" ) -// Columns holds all SQL columns are file fields. +// Columns holds all SQL columns for file fields. var Columns = []string{ FieldID, FieldSize, @@ -51,6 +51,13 @@ var Columns = []string{ FieldGroup, } +// ForeignKeys holds the SQL foreign-keys that are owned by the File type. +var ForeignKeys = []string{ + "type_id", + "group_file_id", + "owner_id", +} + var ( fields = schema.File{}.Fields() diff --git a/entc/integration/ent/file_query.go b/entc/integration/ent/file_query.go index 263edeb01..f18389185 100644 --- a/entc/integration/ent/file_query.go +++ b/entc/integration/ent/file_query.go @@ -29,6 +29,10 @@ type FileQuery struct { order []Order unique []string predicates []predicate.File + // eager-loading edges. + withOwner *UserQuery + withType *FileTypeQuery + withFKs bool // intermediate query. sql *sql.Selector } @@ -250,6 +254,28 @@ func (fq *FileQuery) Clone() *FileQuery { } } +// WithOwner tells the query-builder to eager-loads the nodes that are connected to +// the "owner" edge. The optional arguments used to configure the query builder of the edge. +func (fq *FileQuery) WithOwner(opts ...func(*UserQuery)) *FileQuery { + query := &UserQuery{config: fq.config} + for _, opt := range opts { + opt(query) + } + fq.withOwner = query + return fq +} + +// WithType tells the query-builder to eager-loads the nodes that are connected to +// the "type" edge. The optional arguments used to configure the query builder of the edge. +func (fq *FileQuery) WithType(opts ...func(*FileTypeQuery)) *FileQuery { + query := &FileTypeQuery{config: fq.config} + for _, opt := range opts { + opt(query) + } + fq.withType = query + return fq +} + // GroupBy used to group vertices by one or more fields/columns. // It is often used with aggregate functions, like: count, max, mean, min, sum. // @@ -293,13 +319,24 @@ func (fq *FileQuery) Select(field string, fields ...string) *FileSelect { func (fq *FileQuery) sqlAll(ctx context.Context) ([]*File, error) { var ( - nodes []*File - spec = fq.querySpec() + nodes []*File + withFKs = fq.withFKs + spec = fq.querySpec() ) + if fq.withOwner != nil || fq.withType != nil { + withFKs = true + } + if withFKs { + spec.Node.Columns = append(spec.Node.Columns, file.ForeignKeys...) + } spec.ScanValues = func() []interface{} { node := &File{config: fq.config} nodes = append(nodes, node) - return node.scanValues() + values := node.scanValues() + if withFKs { + values = append(values, node.fkValues()...) + } + return values } spec.Assign = func(values ...interface{}) error { if len(nodes) == 0 { @@ -311,6 +348,57 @@ func (fq *FileQuery) sqlAll(ctx context.Context) ([]*File, error) { if err := sqlgraph.QueryNodes(ctx, fq.driver, spec); err != nil { return nil, err } + + if query := fq.withOwner; query != nil { + ids := make([]string, 0, len(nodes)) + nodeids := make(map[string][]*File) + for i := range nodes { + if fk := nodes[i].owner_id; fk != nil { + ids = append(ids, *fk) + nodeids[*fk] = append(nodeids[*fk], nodes[i]) + } + } + query.Where(user.IDIn(ids...)) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + nodes, ok := nodeids[n.ID] + if !ok { + return nil, fmt.Errorf(`unexpected foreign-key "owner_id" returned %v`, n.ID) + } + for i := range nodes { + nodes[i].Edges.Owner = n + } + } + } + + if query := fq.withType; query != nil { + ids := make([]string, 0, len(nodes)) + nodeids := make(map[string][]*File) + for i := range nodes { + if fk := nodes[i].type_id; fk != nil { + ids = append(ids, *fk) + nodeids[*fk] = append(nodeids[*fk], nodes[i]) + } + } + query.Where(filetype.IDIn(ids...)) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + nodes, ok := nodeids[n.ID] + if !ok { + return nil, fmt.Errorf(`unexpected foreign-key "type_id" returned %v`, n.ID) + } + for i := range nodes { + nodes[i].Edges.Type = n + } + } + } + return nodes, nil } diff --git a/entc/integration/ent/filetype.go b/entc/integration/ent/filetype.go index 9e97e1871..1ff6188ae 100644 --- a/entc/integration/ent/filetype.go +++ b/entc/integration/ent/filetype.go @@ -22,20 +22,26 @@ type FileType struct { ID string `json:"id,omitempty"` // Name holds the value of the "name" field. Name string `json:"name,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the FileTypeQuery when eager-loading is set. + Edges struct { + // Files holds the value of the files edge. + Files []*File + } } // scanValues returns the types for scanning values from sql.Rows. func (*FileType) scanValues() []interface{} { return []interface{}{ - &sql.NullInt64{}, - &sql.NullString{}, + &sql.NullInt64{}, // id + &sql.NullString{}, // name } } // assignValues assigns the values that were returned from sql.Rows (after scanning) // to the FileType fields. func (ft *FileType) assignValues(values ...interface{}) error { - if m, n := len(values), len(filetype.Columns); m != n { + if m, n := len(values), len(filetype.Columns); m < n { return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) } value, ok := values[0].(*sql.NullInt64) diff --git a/entc/integration/ent/filetype/filetype.go b/entc/integration/ent/filetype/filetype.go index bd9736ce8..69a96d844 100644 --- a/entc/integration/ent/filetype/filetype.go +++ b/entc/integration/ent/filetype/filetype.go @@ -25,7 +25,7 @@ const ( FilesColumn = "type_id" ) -// Columns holds all SQL columns are filetype fields. +// Columns holds all SQL columns for filetype fields. var Columns = []string{ FieldID, FieldName, diff --git a/entc/integration/ent/filetype_query.go b/entc/integration/ent/filetype_query.go index cb2e50690..e7e125571 100644 --- a/entc/integration/ent/filetype_query.go +++ b/entc/integration/ent/filetype_query.go @@ -8,9 +8,11 @@ package ent import ( "context" + "database/sql/driver" "errors" "fmt" "math" + "strconv" "github.com/facebookincubator/ent/dialect/sql" "github.com/facebookincubator/ent/dialect/sql/sqlgraph" @@ -28,6 +30,8 @@ type FileTypeQuery struct { order []Order unique []string predicates []predicate.FileType + // eager-loading edges. + withFiles *FileQuery // intermediate query. sql *sql.Selector } @@ -237,6 +241,17 @@ func (ftq *FileTypeQuery) Clone() *FileTypeQuery { } } +// WithFiles tells the query-builder to eager-loads the nodes that are connected to +// the "files" edge. The optional arguments used to configure the query builder of the edge. +func (ftq *FileTypeQuery) WithFiles(opts ...func(*FileQuery)) *FileTypeQuery { + query := &FileQuery{config: ftq.config} + for _, opt := range opts { + opt(query) + } + ftq.withFiles = query + return ftq +} + // GroupBy used to group vertices by one or more fields/columns. // It is often used with aggregate functions, like: count, max, mean, min, sum. // @@ -286,7 +301,8 @@ func (ftq *FileTypeQuery) sqlAll(ctx context.Context) ([]*FileType, error) { spec.ScanValues = func() []interface{} { node := &FileType{config: ftq.config} nodes = append(nodes, node) - return node.scanValues() + values := node.scanValues() + return values } spec.Assign = func(values ...interface{}) error { if len(nodes) == 0 { @@ -298,6 +314,39 @@ func (ftq *FileTypeQuery) sqlAll(ctx context.Context) ([]*FileType, error) { if err := sqlgraph.QueryNodes(ctx, ftq.driver, spec); err != nil { return nil, err } + + if query := ftq.withFiles; query != nil { + fks := make([]driver.Value, 0, len(nodes)) + nodeids := make(map[string]*FileType) + for i := range nodes { + id, err := strconv.Atoi(nodes[i].ID) + if err != nil { + return nil, err + } + fks = append(fks, id) + nodeids[nodes[i].ID] = nodes[i] + } + query.withFKs = true + query.Where(predicate.File(func(s *sql.Selector) { + s.Where(sql.InValues(filetype.FilesColumn, fks...)) + })) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + fk := n.type_id + if fk == nil { + return nil, fmt.Errorf(`foreign-key "type_id" is nil for node %v`, n.ID) + } + node, ok := nodeids[*fk] + if !ok { + return nil, fmt.Errorf(`unexpected foreign-key "type_id" returned %v for node %v`, *fk, n.ID) + } + node.Edges.Files = append(node.Edges.Files, n) + } + } + return nodes, nil } diff --git a/entc/integration/ent/group.go b/entc/integration/ent/group.go index 158a04b93..8ae6e25a9 100644 --- a/entc/integration/ent/group.go +++ b/entc/integration/ent/group.go @@ -31,24 +31,44 @@ type Group struct { MaxUsers int `json:"max_users,omitempty"` // Name holds the value of the "name" field. Name string `json:"name,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the GroupQuery when eager-loading is set. + Edges struct { + // Files holds the value of the files edge. + Files []*File + // Blocked holds the value of the blocked edge. + Blocked []*User + // Users holds the value of the users edge. + Users []*User + // Info holds the value of the info edge. + Info *GroupInfo + } + info_id *string } // scanValues returns the types for scanning values from sql.Rows. func (*Group) scanValues() []interface{} { return []interface{}{ - &sql.NullInt64{}, - &sql.NullBool{}, - &sql.NullTime{}, - &sql.NullString{}, - &sql.NullInt64{}, - &sql.NullString{}, + &sql.NullInt64{}, // id + &sql.NullBool{}, // active + &sql.NullTime{}, // expire + &sql.NullString{}, // type + &sql.NullInt64{}, // max_users + &sql.NullString{}, // name + } +} + +// fkValues returns the types for scanning foreign-keys values from sql.Rows. +func (*Group) fkValues() []interface{} { + return []interface{}{ + &sql.NullInt64{}, // info_id } } // assignValues assigns the values that were returned from sql.Rows (after scanning) // to the Group fields. func (gr *Group) assignValues(values ...interface{}) error { - if m, n := len(values), len(group.Columns); m != n { + if m, n := len(values), len(group.Columns); m < n { return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) } value, ok := values[0].(*sql.NullInt64) @@ -83,6 +103,15 @@ func (gr *Group) assignValues(values ...interface{}) error { } else if value.Valid { gr.Name = value.String } + values = values[5:] + if len(values) == len(group.ForeignKeys) { + if value, ok := values[0].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for edge-field info_id", value) + } else if value.Valid { + gr.info_id = new(string) + *gr.info_id = strconv.FormatInt(value.Int64, 10) + } + } return nil } diff --git a/entc/integration/ent/group/group.go b/entc/integration/ent/group/group.go index de763ddb2..e859d5deb 100644 --- a/entc/integration/ent/group/group.go +++ b/entc/integration/ent/group/group.go @@ -56,7 +56,7 @@ const ( InfoColumn = "info_id" ) -// Columns holds all SQL columns are group fields. +// Columns holds all SQL columns for group fields. var Columns = []string{ FieldID, FieldActive, @@ -66,6 +66,11 @@ var Columns = []string{ FieldName, } +// ForeignKeys holds the SQL foreign-keys that are owned by the Group type. +var ForeignKeys = []string{ + "info_id", +} + var ( // UsersPrimaryKey and UsersColumn2 are the table columns denoting the // primary key for the users relation (M2M). diff --git a/entc/integration/ent/group_query.go b/entc/integration/ent/group_query.go index 818290b29..eb883a484 100644 --- a/entc/integration/ent/group_query.go +++ b/entc/integration/ent/group_query.go @@ -8,9 +8,11 @@ package ent import ( "context" + "database/sql/driver" "errors" "fmt" "math" + "strconv" "github.com/facebookincubator/ent/dialect/sql" "github.com/facebookincubator/ent/dialect/sql/sqlgraph" @@ -30,6 +32,12 @@ type GroupQuery struct { order []Order unique []string predicates []predicate.Group + // eager-loading edges. + withFiles *FileQuery + withBlocked *UserQuery + withUsers *UserQuery + withInfo *GroupInfoQuery + withFKs bool // intermediate query. sql *sql.Selector } @@ -275,6 +283,50 @@ func (gq *GroupQuery) Clone() *GroupQuery { } } +// WithFiles tells the query-builder to eager-loads the nodes that are connected to +// the "files" edge. The optional arguments used to configure the query builder of the edge. +func (gq *GroupQuery) WithFiles(opts ...func(*FileQuery)) *GroupQuery { + query := &FileQuery{config: gq.config} + for _, opt := range opts { + opt(query) + } + gq.withFiles = query + return gq +} + +// WithBlocked tells the query-builder to eager-loads the nodes that are connected to +// the "blocked" edge. The optional arguments used to configure the query builder of the edge. +func (gq *GroupQuery) WithBlocked(opts ...func(*UserQuery)) *GroupQuery { + query := &UserQuery{config: gq.config} + for _, opt := range opts { + opt(query) + } + gq.withBlocked = query + return gq +} + +// WithUsers tells the query-builder to eager-loads the nodes that are connected to +// the "users" edge. The optional arguments used to configure the query builder of the edge. +func (gq *GroupQuery) WithUsers(opts ...func(*UserQuery)) *GroupQuery { + query := &UserQuery{config: gq.config} + for _, opt := range opts { + opt(query) + } + gq.withUsers = query + return gq +} + +// WithInfo tells the query-builder to eager-loads the nodes that are connected to +// the "info" edge. The optional arguments used to configure the query builder of the edge. +func (gq *GroupQuery) WithInfo(opts ...func(*GroupInfoQuery)) *GroupQuery { + query := &GroupInfoQuery{config: gq.config} + for _, opt := range opts { + opt(query) + } + gq.withInfo = query + return gq +} + // GroupBy used to group vertices by one or more fields/columns. // It is often used with aggregate functions, like: count, max, mean, min, sum. // @@ -318,13 +370,24 @@ func (gq *GroupQuery) Select(field string, fields ...string) *GroupSelect { func (gq *GroupQuery) sqlAll(ctx context.Context) ([]*Group, error) { var ( - nodes []*Group - spec = gq.querySpec() + nodes []*Group + withFKs = gq.withFKs + spec = gq.querySpec() ) + if gq.withInfo != nil { + withFKs = true + } + if withFKs { + spec.Node.Columns = append(spec.Node.Columns, group.ForeignKeys...) + } spec.ScanValues = func() []interface{} { node := &Group{config: gq.config} nodes = append(nodes, node) - return node.scanValues() + values := node.scanValues() + if withFKs { + values = append(values, node.fkValues()...) + } + return values } spec.Assign = func(values ...interface{}) error { if len(nodes) == 0 { @@ -336,6 +399,159 @@ func (gq *GroupQuery) sqlAll(ctx context.Context) ([]*Group, error) { if err := sqlgraph.QueryNodes(ctx, gq.driver, spec); err != nil { return nil, err } + + if query := gq.withFiles; query != nil { + fks := make([]driver.Value, 0, len(nodes)) + nodeids := make(map[string]*Group) + for i := range nodes { + id, err := strconv.Atoi(nodes[i].ID) + if err != nil { + return nil, err + } + fks = append(fks, id) + nodeids[nodes[i].ID] = nodes[i] + } + query.withFKs = true + query.Where(predicate.File(func(s *sql.Selector) { + s.Where(sql.InValues(group.FilesColumn, fks...)) + })) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + fk := n.group_file_id + if fk == nil { + return nil, fmt.Errorf(`foreign-key "group_file_id" is nil for node %v`, n.ID) + } + node, ok := nodeids[*fk] + if !ok { + return nil, fmt.Errorf(`unexpected foreign-key "group_file_id" returned %v for node %v`, *fk, n.ID) + } + node.Edges.Files = append(node.Edges.Files, n) + } + } + + if query := gq.withBlocked; query != nil { + fks := make([]driver.Value, 0, len(nodes)) + nodeids := make(map[string]*Group) + for i := range nodes { + id, err := strconv.Atoi(nodes[i].ID) + if err != nil { + return nil, err + } + fks = append(fks, id) + nodeids[nodes[i].ID] = nodes[i] + } + query.withFKs = true + query.Where(predicate.User(func(s *sql.Selector) { + s.Where(sql.InValues(group.BlockedColumn, fks...)) + })) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + fk := n.group_blocked_id + if fk == nil { + return nil, fmt.Errorf(`foreign-key "group_blocked_id" is nil for node %v`, n.ID) + } + node, ok := nodeids[*fk] + if !ok { + return nil, fmt.Errorf(`unexpected foreign-key "group_blocked_id" returned %v for node %v`, *fk, n.ID) + } + node.Edges.Blocked = append(node.Edges.Blocked, n) + } + } + + if query := gq.withUsers; query != nil { + fks := make([]driver.Value, 0, len(nodes)) + ids := make(map[string]*Group, len(nodes)) + for _, node := range nodes { + ids[node.ID] = node + fks = append(fks, node.ID) + } + var ( + edgeids []string + edges = make(map[string][]*Group) + ) + spec := &sqlgraph.EdgeQuerySpec{ + Edge: &sqlgraph.EdgeSpec{ + Inverse: true, + Table: group.UsersTable, + Columns: group.UsersPrimaryKey, + }, + Predicate: func(s *sql.Selector) { + s.Where(sql.InValues(group.UsersPrimaryKey[1], fks...)) + }, + + ScanValues: func() [2]interface{} { + return [2]interface{}{&sql.NullInt64{}, &sql.NullInt64{}} + }, + Assign: func(out, in interface{}) error { + eout, ok := out.(*sql.NullInt64) + if !ok || eout == nil { + return fmt.Errorf("unexpected id value for edge-out") + } + ein, ok := in.(*sql.NullInt64) + if !ok || ein == nil { + return fmt.Errorf("unexpected id value for edge-in") + } + outValue := strconv.FormatInt(eout.Int64, 10) + inValue := strconv.FormatInt(ein.Int64, 10) + node, ok := ids[outValue] + if !ok { + return fmt.Errorf("unexpected node id in edges: %v", outValue) + } + edgeids = append(edgeids, inValue) + edges[inValue] = append(edges[inValue], node) + return nil + }, + } + if err := sqlgraph.QueryEdges(ctx, gq.driver, spec); err != nil { + return nil, fmt.Errorf(`query edges "users": %v`, err) + } + query.Where(user.IDIn(edgeids...)) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + nodes, ok := edges[n.ID] + if !ok { + return nil, fmt.Errorf(`unexpected "users" node returned %v`, n.ID) + } + for i := range nodes { + nodes[i].Edges.Users = append(nodes[i].Edges.Users, n) + } + } + } + + if query := gq.withInfo; query != nil { + ids := make([]string, 0, len(nodes)) + nodeids := make(map[string][]*Group) + for i := range nodes { + if fk := nodes[i].info_id; fk != nil { + ids = append(ids, *fk) + nodeids[*fk] = append(nodeids[*fk], nodes[i]) + } + } + query.Where(groupinfo.IDIn(ids...)) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + nodes, ok := nodeids[n.ID] + if !ok { + return nil, fmt.Errorf(`unexpected foreign-key "info_id" returned %v`, n.ID) + } + for i := range nodes { + nodes[i].Edges.Info = n + } + } + } + return nodes, nil } diff --git a/entc/integration/ent/groupinfo.go b/entc/integration/ent/groupinfo.go index b212a173d..36f6a1f54 100644 --- a/entc/integration/ent/groupinfo.go +++ b/entc/integration/ent/groupinfo.go @@ -24,21 +24,27 @@ type GroupInfo struct { Desc string `json:"desc,omitempty"` // MaxUsers holds the value of the "max_users" field. MaxUsers int `json:"max_users,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the GroupInfoQuery when eager-loading is set. + Edges struct { + // Groups holds the value of the groups edge. + Groups []*Group + } } // scanValues returns the types for scanning values from sql.Rows. func (*GroupInfo) scanValues() []interface{} { return []interface{}{ - &sql.NullInt64{}, - &sql.NullString{}, - &sql.NullInt64{}, + &sql.NullInt64{}, // id + &sql.NullString{}, // desc + &sql.NullInt64{}, // max_users } } // assignValues assigns the values that were returned from sql.Rows (after scanning) // to the GroupInfo fields. func (gi *GroupInfo) assignValues(values ...interface{}) error { - if m, n := len(values), len(groupinfo.Columns); m != n { + if m, n := len(values), len(groupinfo.Columns); m < n { return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) } value, ok := values[0].(*sql.NullInt64) diff --git a/entc/integration/ent/groupinfo/groupinfo.go b/entc/integration/ent/groupinfo/groupinfo.go index b8b9efaad..611ac226d 100644 --- a/entc/integration/ent/groupinfo/groupinfo.go +++ b/entc/integration/ent/groupinfo/groupinfo.go @@ -31,7 +31,7 @@ const ( GroupsColumn = "info_id" ) -// Columns holds all SQL columns are groupinfo fields. +// Columns holds all SQL columns for groupinfo fields. var Columns = []string{ FieldID, FieldDesc, diff --git a/entc/integration/ent/groupinfo_query.go b/entc/integration/ent/groupinfo_query.go index c5c47731d..9198e08fd 100644 --- a/entc/integration/ent/groupinfo_query.go +++ b/entc/integration/ent/groupinfo_query.go @@ -8,9 +8,11 @@ package ent import ( "context" + "database/sql/driver" "errors" "fmt" "math" + "strconv" "github.com/facebookincubator/ent/dialect/sql" "github.com/facebookincubator/ent/dialect/sql/sqlgraph" @@ -28,6 +30,8 @@ type GroupInfoQuery struct { order []Order unique []string predicates []predicate.GroupInfo + // eager-loading edges. + withGroups *GroupQuery // intermediate query. sql *sql.Selector } @@ -237,6 +241,17 @@ func (giq *GroupInfoQuery) Clone() *GroupInfoQuery { } } +// WithGroups tells the query-builder to eager-loads the nodes that are connected to +// the "groups" edge. The optional arguments used to configure the query builder of the edge. +func (giq *GroupInfoQuery) WithGroups(opts ...func(*GroupQuery)) *GroupInfoQuery { + query := &GroupQuery{config: giq.config} + for _, opt := range opts { + opt(query) + } + giq.withGroups = query + return giq +} + // GroupBy used to group vertices by one or more fields/columns. // It is often used with aggregate functions, like: count, max, mean, min, sum. // @@ -286,7 +301,8 @@ func (giq *GroupInfoQuery) sqlAll(ctx context.Context) ([]*GroupInfo, error) { spec.ScanValues = func() []interface{} { node := &GroupInfo{config: giq.config} nodes = append(nodes, node) - return node.scanValues() + values := node.scanValues() + return values } spec.Assign = func(values ...interface{}) error { if len(nodes) == 0 { @@ -298,6 +314,39 @@ func (giq *GroupInfoQuery) sqlAll(ctx context.Context) ([]*GroupInfo, error) { if err := sqlgraph.QueryNodes(ctx, giq.driver, spec); err != nil { return nil, err } + + if query := giq.withGroups; query != nil { + fks := make([]driver.Value, 0, len(nodes)) + nodeids := make(map[string]*GroupInfo) + for i := range nodes { + id, err := strconv.Atoi(nodes[i].ID) + if err != nil { + return nil, err + } + fks = append(fks, id) + nodeids[nodes[i].ID] = nodes[i] + } + query.withFKs = true + query.Where(predicate.Group(func(s *sql.Selector) { + s.Where(sql.InValues(groupinfo.GroupsColumn, fks...)) + })) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + fk := n.info_id + if fk == nil { + return nil, fmt.Errorf(`foreign-key "info_id" is nil for node %v`, n.ID) + } + node, ok := nodeids[*fk] + if !ok { + return nil, fmt.Errorf(`unexpected foreign-key "info_id" returned %v for node %v`, *fk, n.ID) + } + node.Edges.Groups = append(node.Edges.Groups, n) + } + } + return nodes, nil } diff --git a/entc/integration/ent/item.go b/entc/integration/ent/item.go index 39c2bb6ed..75b5e46b7 100644 --- a/entc/integration/ent/item.go +++ b/entc/integration/ent/item.go @@ -25,14 +25,14 @@ type Item struct { // scanValues returns the types for scanning values from sql.Rows. func (*Item) scanValues() []interface{} { return []interface{}{ - &sql.NullInt64{}, + &sql.NullInt64{}, // id } } // assignValues assigns the values that were returned from sql.Rows (after scanning) // to the Item fields. func (i *Item) assignValues(values ...interface{}) error { - if m, n := len(values), len(item.Columns); m != n { + if m, n := len(values), len(item.Columns); m < n { return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) } value, ok := values[0].(*sql.NullInt64) diff --git a/entc/integration/ent/item/item.go b/entc/integration/ent/item/item.go index caaa1d933..9442d7998 100644 --- a/entc/integration/ent/item/item.go +++ b/entc/integration/ent/item/item.go @@ -16,7 +16,7 @@ const ( Table = "items" ) -// Columns holds all SQL columns are item fields. +// Columns holds all SQL columns for item fields. var Columns = []string{ FieldID, } diff --git a/entc/integration/ent/item_query.go b/entc/integration/ent/item_query.go index ae089bc3f..502d19cd2 100644 --- a/entc/integration/ent/item_query.go +++ b/entc/integration/ent/item_query.go @@ -249,7 +249,8 @@ func (iq *ItemQuery) sqlAll(ctx context.Context) ([]*Item, error) { spec.ScanValues = func() []interface{} { node := &Item{config: iq.config} nodes = append(nodes, node) - return node.scanValues() + values := node.scanValues() + return values } spec.Assign = func(values ...interface{}) error { if len(nodes) == 0 { diff --git a/entc/integration/ent/node.go b/entc/integration/ent/node.go index 6a764aebe..7fc336c6e 100644 --- a/entc/integration/ent/node.go +++ b/entc/integration/ent/node.go @@ -22,20 +22,36 @@ type Node struct { ID string `json:"id,omitempty"` // Value holds the value of the "value" field. Value int `json:"value,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the NodeQuery when eager-loading is set. + Edges struct { + // Prev holds the value of the prev edge. + Prev *Node + // Next holds the value of the next edge. + Next *Node + } + prev_id *string } // scanValues returns the types for scanning values from sql.Rows. func (*Node) scanValues() []interface{} { return []interface{}{ - &sql.NullInt64{}, - &sql.NullInt64{}, + &sql.NullInt64{}, // id + &sql.NullInt64{}, // value + } +} + +// fkValues returns the types for scanning foreign-keys values from sql.Rows. +func (*Node) fkValues() []interface{} { + return []interface{}{ + &sql.NullInt64{}, // prev_id } } // assignValues assigns the values that were returned from sql.Rows (after scanning) // to the Node fields. func (n *Node) assignValues(values ...interface{}) error { - if m, n := len(values), len(node.Columns); m != n { + if m, n := len(values), len(node.Columns); m < n { return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) } value, ok := values[0].(*sql.NullInt64) @@ -49,6 +65,15 @@ func (n *Node) assignValues(values ...interface{}) error { } else if value.Valid { n.Value = int(value.Int64) } + values = values[1:] + if len(values) == len(node.ForeignKeys) { + if value, ok := values[0].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for edge-field prev_id", value) + } else if value.Valid { + n.prev_id = new(string) + *n.prev_id = strconv.FormatInt(value.Int64, 10) + } + } return nil } diff --git a/entc/integration/ent/node/node.go b/entc/integration/ent/node/node.go index 64bbdf029..dfc9ae0d2 100644 --- a/entc/integration/ent/node/node.go +++ b/entc/integration/ent/node/node.go @@ -26,8 +26,13 @@ const ( NextColumn = "prev_id" ) -// Columns holds all SQL columns are node fields. +// Columns holds all SQL columns for node fields. var Columns = []string{ FieldID, FieldValue, } + +// ForeignKeys holds the SQL foreign-keys that are owned by the Node type. +var ForeignKeys = []string{ + "prev_id", +} diff --git a/entc/integration/ent/node_query.go b/entc/integration/ent/node_query.go index 94558f142..bbc20b3de 100644 --- a/entc/integration/ent/node_query.go +++ b/entc/integration/ent/node_query.go @@ -8,9 +8,11 @@ package ent import ( "context" + "database/sql/driver" "errors" "fmt" "math" + "strconv" "github.com/facebookincubator/ent/dialect/sql" "github.com/facebookincubator/ent/dialect/sql/sqlgraph" @@ -27,6 +29,10 @@ type NodeQuery struct { order []Order unique []string predicates []predicate.Node + // eager-loading edges. + withPrev *NodeQuery + withNext *NodeQuery + withFKs bool // intermediate query. sql *sql.Selector } @@ -248,6 +254,28 @@ func (nq *NodeQuery) Clone() *NodeQuery { } } +// WithPrev tells the query-builder to eager-loads the nodes that are connected to +// the "prev" edge. The optional arguments used to configure the query builder of the edge. +func (nq *NodeQuery) WithPrev(opts ...func(*NodeQuery)) *NodeQuery { + query := &NodeQuery{config: nq.config} + for _, opt := range opts { + opt(query) + } + nq.withPrev = query + return nq +} + +// WithNext tells the query-builder to eager-loads the nodes that are connected to +// the "next" edge. The optional arguments used to configure the query builder of the edge. +func (nq *NodeQuery) WithNext(opts ...func(*NodeQuery)) *NodeQuery { + query := &NodeQuery{config: nq.config} + for _, opt := range opts { + opt(query) + } + nq.withNext = query + return nq +} + // GroupBy used to group vertices by one or more fields/columns. // It is often used with aggregate functions, like: count, max, mean, min, sum. // @@ -291,13 +319,24 @@ func (nq *NodeQuery) Select(field string, fields ...string) *NodeSelect { func (nq *NodeQuery) sqlAll(ctx context.Context) ([]*Node, error) { var ( - nodes []*Node - spec = nq.querySpec() + nodes []*Node + withFKs = nq.withFKs + spec = nq.querySpec() ) + if nq.withPrev != nil { + withFKs = true + } + if withFKs { + spec.Node.Columns = append(spec.Node.Columns, node.ForeignKeys...) + } spec.ScanValues = func() []interface{} { node := &Node{config: nq.config} nodes = append(nodes, node) - return node.scanValues() + values := node.scanValues() + if withFKs { + values = append(values, node.fkValues()...) + } + return values } spec.Assign = func(values ...interface{}) error { if len(nodes) == 0 { @@ -309,6 +348,64 @@ func (nq *NodeQuery) sqlAll(ctx context.Context) ([]*Node, error) { if err := sqlgraph.QueryNodes(ctx, nq.driver, spec); err != nil { return nil, err } + + if query := nq.withPrev; query != nil { + ids := make([]string, 0, len(nodes)) + nodeids := make(map[string][]*Node) + for i := range nodes { + if fk := nodes[i].prev_id; fk != nil { + ids = append(ids, *fk) + nodeids[*fk] = append(nodeids[*fk], nodes[i]) + } + } + query.Where(node.IDIn(ids...)) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + nodes, ok := nodeids[n.ID] + if !ok { + return nil, fmt.Errorf(`unexpected foreign-key "prev_id" returned %v`, n.ID) + } + for i := range nodes { + nodes[i].Edges.Prev = n + } + } + } + + if query := nq.withNext; query != nil { + fks := make([]driver.Value, 0, len(nodes)) + nodeids := make(map[string]*Node) + for i := range nodes { + id, err := strconv.Atoi(nodes[i].ID) + if err != nil { + return nil, err + } + fks = append(fks, id) + nodeids[nodes[i].ID] = nodes[i] + } + query.withFKs = true + query.Where(predicate.Node(func(s *sql.Selector) { + s.Where(sql.InValues(node.NextColumn, fks...)) + })) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + fk := n.prev_id + if fk == nil { + return nil, fmt.Errorf(`foreign-key "prev_id" is nil for node %v`, n.ID) + } + node, ok := nodeids[*fk] + if !ok { + return nil, fmt.Errorf(`unexpected foreign-key "prev_id" returned %v for node %v`, *fk, n.ID) + } + node.Edges.Next = n + } + } + return nodes, nil } diff --git a/entc/integration/ent/pet.go b/entc/integration/ent/pet.go index 929bc018a..b115a6d73 100644 --- a/entc/integration/ent/pet.go +++ b/entc/integration/ent/pet.go @@ -22,20 +22,38 @@ type Pet struct { ID string `json:"id,omitempty"` // Name holds the value of the "name" field. Name string `json:"name,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the PetQuery when eager-loading is set. + Edges struct { + // Team holds the value of the team edge. + Team *User + // Owner holds the value of the owner edge. + Owner *User + } + owner_id *string + team_id *string } // scanValues returns the types for scanning values from sql.Rows. func (*Pet) scanValues() []interface{} { return []interface{}{ - &sql.NullInt64{}, - &sql.NullString{}, + &sql.NullInt64{}, // id + &sql.NullString{}, // name + } +} + +// fkValues returns the types for scanning foreign-keys values from sql.Rows. +func (*Pet) fkValues() []interface{} { + return []interface{}{ + &sql.NullInt64{}, // owner_id + &sql.NullInt64{}, // team_id } } // assignValues assigns the values that were returned from sql.Rows (after scanning) // to the Pet fields. func (pe *Pet) assignValues(values ...interface{}) error { - if m, n := len(values), len(pet.Columns); m != n { + if m, n := len(values), len(pet.Columns); m < n { return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) } value, ok := values[0].(*sql.NullInt64) @@ -49,6 +67,21 @@ func (pe *Pet) assignValues(values ...interface{}) error { } else if value.Valid { pe.Name = value.String } + values = values[1:] + if len(values) == len(pet.ForeignKeys) { + if value, ok := values[0].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for edge-field owner_id", value) + } else if value.Valid { + pe.owner_id = new(string) + *pe.owner_id = strconv.FormatInt(value.Int64, 10) + } + if value, ok := values[1].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for edge-field team_id", value) + } else if value.Valid { + pe.team_id = new(string) + *pe.team_id = strconv.FormatInt(value.Int64, 10) + } + } return nil } diff --git a/entc/integration/ent/pet/pet.go b/entc/integration/ent/pet/pet.go index 078e395e6..b7c90a31e 100644 --- a/entc/integration/ent/pet/pet.go +++ b/entc/integration/ent/pet/pet.go @@ -32,8 +32,14 @@ const ( OwnerColumn = "owner_id" ) -// Columns holds all SQL columns are pet fields. +// Columns holds all SQL columns for pet fields. var Columns = []string{ FieldID, FieldName, } + +// ForeignKeys holds the SQL foreign-keys that are owned by the Pet type. +var ForeignKeys = []string{ + "owner_id", + "team_id", +} diff --git a/entc/integration/ent/pet_query.go b/entc/integration/ent/pet_query.go index 9d494dd7c..9fa1cda95 100644 --- a/entc/integration/ent/pet_query.go +++ b/entc/integration/ent/pet_query.go @@ -28,6 +28,10 @@ type PetQuery struct { order []Order unique []string predicates []predicate.Pet + // eager-loading edges. + withTeam *UserQuery + withOwner *UserQuery + withFKs bool // intermediate query. sql *sql.Selector } @@ -249,6 +253,28 @@ func (pq *PetQuery) Clone() *PetQuery { } } +// WithTeam tells the query-builder to eager-loads the nodes that are connected to +// the "team" edge. The optional arguments used to configure the query builder of the edge. +func (pq *PetQuery) WithTeam(opts ...func(*UserQuery)) *PetQuery { + query := &UserQuery{config: pq.config} + for _, opt := range opts { + opt(query) + } + pq.withTeam = query + return pq +} + +// WithOwner tells the query-builder to eager-loads the nodes that are connected to +// the "owner" edge. The optional arguments used to configure the query builder of the edge. +func (pq *PetQuery) WithOwner(opts ...func(*UserQuery)) *PetQuery { + query := &UserQuery{config: pq.config} + for _, opt := range opts { + opt(query) + } + pq.withOwner = query + return pq +} + // GroupBy used to group vertices by one or more fields/columns. // It is often used with aggregate functions, like: count, max, mean, min, sum. // @@ -292,13 +318,24 @@ func (pq *PetQuery) Select(field string, fields ...string) *PetSelect { func (pq *PetQuery) sqlAll(ctx context.Context) ([]*Pet, error) { var ( - nodes []*Pet - spec = pq.querySpec() + nodes []*Pet + withFKs = pq.withFKs + spec = pq.querySpec() ) + if pq.withTeam != nil || pq.withOwner != nil { + withFKs = true + } + if withFKs { + spec.Node.Columns = append(spec.Node.Columns, pet.ForeignKeys...) + } spec.ScanValues = func() []interface{} { node := &Pet{config: pq.config} nodes = append(nodes, node) - return node.scanValues() + values := node.scanValues() + if withFKs { + values = append(values, node.fkValues()...) + } + return values } spec.Assign = func(values ...interface{}) error { if len(nodes) == 0 { @@ -310,6 +347,57 @@ func (pq *PetQuery) sqlAll(ctx context.Context) ([]*Pet, error) { if err := sqlgraph.QueryNodes(ctx, pq.driver, spec); err != nil { return nil, err } + + if query := pq.withTeam; query != nil { + ids := make([]string, 0, len(nodes)) + nodeids := make(map[string][]*Pet) + for i := range nodes { + if fk := nodes[i].team_id; fk != nil { + ids = append(ids, *fk) + nodeids[*fk] = append(nodeids[*fk], nodes[i]) + } + } + query.Where(user.IDIn(ids...)) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + nodes, ok := nodeids[n.ID] + if !ok { + return nil, fmt.Errorf(`unexpected foreign-key "team_id" returned %v`, n.ID) + } + for i := range nodes { + nodes[i].Edges.Team = n + } + } + } + + if query := pq.withOwner; query != nil { + ids := make([]string, 0, len(nodes)) + nodeids := make(map[string][]*Pet) + for i := range nodes { + if fk := nodes[i].owner_id; fk != nil { + ids = append(ids, *fk) + nodeids[*fk] = append(nodeids[*fk], nodes[i]) + } + } + query.Where(user.IDIn(ids...)) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + nodes, ok := nodeids[n.ID] + if !ok { + return nil, fmt.Errorf(`unexpected foreign-key "owner_id" returned %v`, n.ID) + } + for i := range nodes { + nodes[i].Edges.Owner = n + } + } + } + return nodes, nil } diff --git a/entc/integration/ent/user.go b/entc/integration/ent/user.go index 6955c0910..885e2cb97 100644 --- a/entc/integration/ent/user.go +++ b/entc/integration/ent/user.go @@ -36,27 +36,65 @@ type User struct { Password string `graphql:"-" json:"-"` // Role holds the value of the "role" field. Role user.Role `json:"role,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the UserQuery when eager-loading is set. + Edges struct { + // Card holds the value of the card edge. + Card *Card + // Pets holds the value of the pets edge. + Pets []*Pet + // Files holds the value of the files edge. + Files []*File + // Groups holds the value of the groups edge. + Groups []*Group + // Friends holds the value of the friends edge. + Friends []*User + // Followers holds the value of the followers edge. + Followers []*User + // Following holds the value of the following edge. + Following []*User + // Team holds the value of the team edge. + Team *Pet + // Spouse holds the value of the spouse edge. + Spouse *User + // Children holds the value of the children edge. + Children []*User + // Parent holds the value of the parent edge. + Parent *User + } + group_blocked_id *string + user_spouse_id *string + parent_id *string } // scanValues returns the types for scanning values from sql.Rows. func (*User) scanValues() []interface{} { return []interface{}{ - &sql.NullInt64{}, - &sql.NullInt64{}, - &sql.NullInt64{}, - &sql.NullString{}, - &sql.NullString{}, - &sql.NullString{}, - &sql.NullString{}, - &sql.NullString{}, - &sql.NullString{}, + &sql.NullInt64{}, // id + &sql.NullInt64{}, // optional_int + &sql.NullInt64{}, // age + &sql.NullString{}, // name + &sql.NullString{}, // last + &sql.NullString{}, // nickname + &sql.NullString{}, // phone + &sql.NullString{}, // password + &sql.NullString{}, // role + } +} + +// fkValues returns the types for scanning foreign-keys values from sql.Rows. +func (*User) fkValues() []interface{} { + return []interface{}{ + &sql.NullInt64{}, // group_blocked_id + &sql.NullInt64{}, // user_spouse_id + &sql.NullInt64{}, // parent_id } } // assignValues assigns the values that were returned from sql.Rows (after scanning) // to the User fields. func (u *User) assignValues(values ...interface{}) error { - if m, n := len(values), len(user.Columns); m != n { + if m, n := len(values), len(user.Columns); m < n { return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) } value, ok := values[0].(*sql.NullInt64) @@ -105,6 +143,27 @@ func (u *User) assignValues(values ...interface{}) error { } else if value.Valid { u.Role = user.Role(value.String) } + values = values[8:] + if len(values) == len(user.ForeignKeys) { + if value, ok := values[0].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for edge-field group_blocked_id", value) + } else if value.Valid { + u.group_blocked_id = new(string) + *u.group_blocked_id = strconv.FormatInt(value.Int64, 10) + } + if value, ok := values[1].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for edge-field user_spouse_id", value) + } else if value.Valid { + u.user_spouse_id = new(string) + *u.user_spouse_id = strconv.FormatInt(value.Int64, 10) + } + if value, ok := values[2].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for edge-field parent_id", value) + } else if value.Valid { + u.parent_id = new(string) + *u.parent_id = strconv.FormatInt(value.Int64, 10) + } + } return nil } diff --git a/entc/integration/ent/user/user.go b/entc/integration/ent/user/user.go index 5ddbbc4b4..e8c05ad59 100644 --- a/entc/integration/ent/user/user.go +++ b/entc/integration/ent/user/user.go @@ -90,7 +90,7 @@ const ( ParentColumn = "parent_id" ) -// Columns holds all SQL columns are user fields. +// Columns holds all SQL columns for user fields. var Columns = []string{ FieldID, FieldOptionalInt, @@ -103,6 +103,13 @@ var Columns = []string{ FieldRole, } +// ForeignKeys holds the SQL foreign-keys that are owned by the User type. +var ForeignKeys = []string{ + "group_blocked_id", + "user_spouse_id", + "parent_id", +} + var ( // GroupsPrimaryKey and GroupsColumn2 are the table columns denoting the // primary key for the groups relation (M2M). diff --git a/entc/integration/ent/user_query.go b/entc/integration/ent/user_query.go index 96d6b8caa..0502798da 100644 --- a/entc/integration/ent/user_query.go +++ b/entc/integration/ent/user_query.go @@ -8,9 +8,11 @@ package ent import ( "context" + "database/sql/driver" "errors" "fmt" "math" + "strconv" "github.com/facebookincubator/ent/dialect/sql" "github.com/facebookincubator/ent/dialect/sql/sqlgraph" @@ -31,6 +33,19 @@ type UserQuery struct { order []Order unique []string predicates []predicate.User + // eager-loading edges. + withCard *CardQuery + withPets *PetQuery + withFiles *FileQuery + withGroups *GroupQuery + withFriends *UserQuery + withFollowers *UserQuery + withFollowing *UserQuery + withTeam *PetQuery + withSpouse *UserQuery + withChildren *UserQuery + withParent *UserQuery + withFKs bool // intermediate query. sql *sql.Selector } @@ -360,6 +375,127 @@ func (uq *UserQuery) Clone() *UserQuery { } } +// WithCard tells the query-builder to eager-loads the nodes that are connected to +// the "card" edge. The optional arguments used to configure the query builder of the edge. +func (uq *UserQuery) WithCard(opts ...func(*CardQuery)) *UserQuery { + query := &CardQuery{config: uq.config} + for _, opt := range opts { + opt(query) + } + uq.withCard = query + return uq +} + +// WithPets tells the query-builder to eager-loads the nodes that are connected to +// the "pets" edge. The optional arguments used to configure the query builder of the edge. +func (uq *UserQuery) WithPets(opts ...func(*PetQuery)) *UserQuery { + query := &PetQuery{config: uq.config} + for _, opt := range opts { + opt(query) + } + uq.withPets = query + return uq +} + +// WithFiles tells the query-builder to eager-loads the nodes that are connected to +// the "files" edge. The optional arguments used to configure the query builder of the edge. +func (uq *UserQuery) WithFiles(opts ...func(*FileQuery)) *UserQuery { + query := &FileQuery{config: uq.config} + for _, opt := range opts { + opt(query) + } + uq.withFiles = query + return uq +} + +// WithGroups tells the query-builder to eager-loads the nodes that are connected to +// the "groups" edge. The optional arguments used to configure the query builder of the edge. +func (uq *UserQuery) WithGroups(opts ...func(*GroupQuery)) *UserQuery { + query := &GroupQuery{config: uq.config} + for _, opt := range opts { + opt(query) + } + uq.withGroups = query + return uq +} + +// WithFriends tells the query-builder to eager-loads the nodes that are connected to +// the "friends" edge. The optional arguments used to configure the query builder of the edge. +func (uq *UserQuery) WithFriends(opts ...func(*UserQuery)) *UserQuery { + query := &UserQuery{config: uq.config} + for _, opt := range opts { + opt(query) + } + uq.withFriends = query + return uq +} + +// WithFollowers tells the query-builder to eager-loads the nodes that are connected to +// the "followers" edge. The optional arguments used to configure the query builder of the edge. +func (uq *UserQuery) WithFollowers(opts ...func(*UserQuery)) *UserQuery { + query := &UserQuery{config: uq.config} + for _, opt := range opts { + opt(query) + } + uq.withFollowers = query + return uq +} + +// WithFollowing tells the query-builder to eager-loads the nodes that are connected to +// the "following" edge. The optional arguments used to configure the query builder of the edge. +func (uq *UserQuery) WithFollowing(opts ...func(*UserQuery)) *UserQuery { + query := &UserQuery{config: uq.config} + for _, opt := range opts { + opt(query) + } + uq.withFollowing = query + return uq +} + +// WithTeam tells the query-builder to eager-loads the nodes that are connected to +// the "team" edge. The optional arguments used to configure the query builder of the edge. +func (uq *UserQuery) WithTeam(opts ...func(*PetQuery)) *UserQuery { + query := &PetQuery{config: uq.config} + for _, opt := range opts { + opt(query) + } + uq.withTeam = query + return uq +} + +// WithSpouse tells the query-builder to eager-loads the nodes that are connected to +// the "spouse" edge. The optional arguments used to configure the query builder of the edge. +func (uq *UserQuery) WithSpouse(opts ...func(*UserQuery)) *UserQuery { + query := &UserQuery{config: uq.config} + for _, opt := range opts { + opt(query) + } + uq.withSpouse = query + return uq +} + +// WithChildren tells the query-builder to eager-loads the nodes that are connected to +// the "children" edge. The optional arguments used to configure the query builder of the edge. +func (uq *UserQuery) WithChildren(opts ...func(*UserQuery)) *UserQuery { + query := &UserQuery{config: uq.config} + for _, opt := range opts { + opt(query) + } + uq.withChildren = query + return uq +} + +// WithParent tells the query-builder to eager-loads the nodes that are connected to +// the "parent" edge. The optional arguments used to configure the query builder of the edge. +func (uq *UserQuery) WithParent(opts ...func(*UserQuery)) *UserQuery { + query := &UserQuery{config: uq.config} + for _, opt := range opts { + opt(query) + } + uq.withParent = query + return uq +} + // GroupBy used to group vertices by one or more fields/columns. // It is often used with aggregate functions, like: count, max, mean, min, sum. // @@ -403,13 +539,24 @@ func (uq *UserQuery) Select(field string, fields ...string) *UserSelect { func (uq *UserQuery) sqlAll(ctx context.Context) ([]*User, error) { var ( - nodes []*User - spec = uq.querySpec() + nodes []*User + withFKs = uq.withFKs + spec = uq.querySpec() ) + if uq.withSpouse != nil || uq.withParent != nil { + withFKs = true + } + if withFKs { + spec.Node.Columns = append(spec.Node.Columns, user.ForeignKeys...) + } spec.ScanValues = func() []interface{} { node := &User{config: uq.config} nodes = append(nodes, node) - return node.scanValues() + values := node.scanValues() + if withFKs { + values = append(values, node.fkValues()...) + } + return values } spec.Assign = func(values ...interface{}) error { if len(nodes) == 0 { @@ -421,6 +568,469 @@ func (uq *UserQuery) sqlAll(ctx context.Context) ([]*User, error) { if err := sqlgraph.QueryNodes(ctx, uq.driver, spec); err != nil { return nil, err } + + if query := uq.withCard; query != nil { + fks := make([]driver.Value, 0, len(nodes)) + nodeids := make(map[string]*User) + for i := range nodes { + id, err := strconv.Atoi(nodes[i].ID) + if err != nil { + return nil, err + } + fks = append(fks, id) + nodeids[nodes[i].ID] = nodes[i] + } + query.withFKs = true + query.Where(predicate.Card(func(s *sql.Selector) { + s.Where(sql.InValues(user.CardColumn, fks...)) + })) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + fk := n.owner_id + if fk == nil { + return nil, fmt.Errorf(`foreign-key "owner_id" is nil for node %v`, n.ID) + } + node, ok := nodeids[*fk] + if !ok { + return nil, fmt.Errorf(`unexpected foreign-key "owner_id" returned %v for node %v`, *fk, n.ID) + } + node.Edges.Card = n + } + } + + if query := uq.withPets; query != nil { + fks := make([]driver.Value, 0, len(nodes)) + nodeids := make(map[string]*User) + for i := range nodes { + id, err := strconv.Atoi(nodes[i].ID) + if err != nil { + return nil, err + } + fks = append(fks, id) + nodeids[nodes[i].ID] = nodes[i] + } + query.withFKs = true + query.Where(predicate.Pet(func(s *sql.Selector) { + s.Where(sql.InValues(user.PetsColumn, fks...)) + })) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + fk := n.owner_id + if fk == nil { + return nil, fmt.Errorf(`foreign-key "owner_id" is nil for node %v`, n.ID) + } + node, ok := nodeids[*fk] + if !ok { + return nil, fmt.Errorf(`unexpected foreign-key "owner_id" returned %v for node %v`, *fk, n.ID) + } + node.Edges.Pets = append(node.Edges.Pets, n) + } + } + + if query := uq.withFiles; query != nil { + fks := make([]driver.Value, 0, len(nodes)) + nodeids := make(map[string]*User) + for i := range nodes { + id, err := strconv.Atoi(nodes[i].ID) + if err != nil { + return nil, err + } + fks = append(fks, id) + nodeids[nodes[i].ID] = nodes[i] + } + query.withFKs = true + query.Where(predicate.File(func(s *sql.Selector) { + s.Where(sql.InValues(user.FilesColumn, fks...)) + })) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + fk := n.owner_id + if fk == nil { + return nil, fmt.Errorf(`foreign-key "owner_id" is nil for node %v`, n.ID) + } + node, ok := nodeids[*fk] + if !ok { + return nil, fmt.Errorf(`unexpected foreign-key "owner_id" returned %v for node %v`, *fk, n.ID) + } + node.Edges.Files = append(node.Edges.Files, n) + } + } + + if query := uq.withGroups; query != nil { + fks := make([]driver.Value, 0, len(nodes)) + ids := make(map[string]*User, len(nodes)) + for _, node := range nodes { + ids[node.ID] = node + fks = append(fks, node.ID) + } + var ( + edgeids []string + edges = make(map[string][]*User) + ) + spec := &sqlgraph.EdgeQuerySpec{ + Edge: &sqlgraph.EdgeSpec{ + Inverse: false, + Table: user.GroupsTable, + Columns: user.GroupsPrimaryKey, + }, + Predicate: func(s *sql.Selector) { + s.Where(sql.InValues(user.GroupsPrimaryKey[0], fks...)) + }, + + ScanValues: func() [2]interface{} { + return [2]interface{}{&sql.NullInt64{}, &sql.NullInt64{}} + }, + Assign: func(out, in interface{}) error { + eout, ok := out.(*sql.NullInt64) + if !ok || eout == nil { + return fmt.Errorf("unexpected id value for edge-out") + } + ein, ok := in.(*sql.NullInt64) + if !ok || ein == nil { + return fmt.Errorf("unexpected id value for edge-in") + } + outValue := strconv.FormatInt(eout.Int64, 10) + inValue := strconv.FormatInt(ein.Int64, 10) + node, ok := ids[outValue] + if !ok { + return fmt.Errorf("unexpected node id in edges: %v", outValue) + } + edgeids = append(edgeids, inValue) + edges[inValue] = append(edges[inValue], node) + return nil + }, + } + if err := sqlgraph.QueryEdges(ctx, uq.driver, spec); err != nil { + return nil, fmt.Errorf(`query edges "groups": %v`, err) + } + query.Where(group.IDIn(edgeids...)) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + nodes, ok := edges[n.ID] + if !ok { + return nil, fmt.Errorf(`unexpected "groups" node returned %v`, n.ID) + } + for i := range nodes { + nodes[i].Edges.Groups = append(nodes[i].Edges.Groups, n) + } + } + } + + if query := uq.withFriends; query != nil { + fks := make([]driver.Value, 0, len(nodes)) + ids := make(map[string]*User, len(nodes)) + for _, node := range nodes { + ids[node.ID] = node + fks = append(fks, node.ID) + } + var ( + edgeids []string + edges = make(map[string][]*User) + ) + spec := &sqlgraph.EdgeQuerySpec{ + Edge: &sqlgraph.EdgeSpec{ + Inverse: false, + Table: user.FriendsTable, + Columns: user.FriendsPrimaryKey, + }, + Predicate: func(s *sql.Selector) { + s.Where(sql.InValues(user.FriendsPrimaryKey[0], fks...)) + }, + + ScanValues: func() [2]interface{} { + return [2]interface{}{&sql.NullInt64{}, &sql.NullInt64{}} + }, + Assign: func(out, in interface{}) error { + eout, ok := out.(*sql.NullInt64) + if !ok || eout == nil { + return fmt.Errorf("unexpected id value for edge-out") + } + ein, ok := in.(*sql.NullInt64) + if !ok || ein == nil { + return fmt.Errorf("unexpected id value for edge-in") + } + outValue := strconv.FormatInt(eout.Int64, 10) + inValue := strconv.FormatInt(ein.Int64, 10) + node, ok := ids[outValue] + if !ok { + return fmt.Errorf("unexpected node id in edges: %v", outValue) + } + edgeids = append(edgeids, inValue) + edges[inValue] = append(edges[inValue], node) + return nil + }, + } + if err := sqlgraph.QueryEdges(ctx, uq.driver, spec); err != nil { + return nil, fmt.Errorf(`query edges "friends": %v`, err) + } + query.Where(user.IDIn(edgeids...)) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + nodes, ok := edges[n.ID] + if !ok { + return nil, fmt.Errorf(`unexpected "friends" node returned %v`, n.ID) + } + for i := range nodes { + nodes[i].Edges.Friends = append(nodes[i].Edges.Friends, n) + } + } + } + + if query := uq.withFollowers; query != nil { + fks := make([]driver.Value, 0, len(nodes)) + ids := make(map[string]*User, len(nodes)) + for _, node := range nodes { + ids[node.ID] = node + fks = append(fks, node.ID) + } + var ( + edgeids []string + edges = make(map[string][]*User) + ) + spec := &sqlgraph.EdgeQuerySpec{ + Edge: &sqlgraph.EdgeSpec{ + Inverse: true, + Table: user.FollowersTable, + Columns: user.FollowersPrimaryKey, + }, + Predicate: func(s *sql.Selector) { + s.Where(sql.InValues(user.FollowersPrimaryKey[1], fks...)) + }, + + ScanValues: func() [2]interface{} { + return [2]interface{}{&sql.NullInt64{}, &sql.NullInt64{}} + }, + Assign: func(out, in interface{}) error { + eout, ok := out.(*sql.NullInt64) + if !ok || eout == nil { + return fmt.Errorf("unexpected id value for edge-out") + } + ein, ok := in.(*sql.NullInt64) + if !ok || ein == nil { + return fmt.Errorf("unexpected id value for edge-in") + } + outValue := strconv.FormatInt(eout.Int64, 10) + inValue := strconv.FormatInt(ein.Int64, 10) + node, ok := ids[outValue] + if !ok { + return fmt.Errorf("unexpected node id in edges: %v", outValue) + } + edgeids = append(edgeids, inValue) + edges[inValue] = append(edges[inValue], node) + return nil + }, + } + if err := sqlgraph.QueryEdges(ctx, uq.driver, spec); err != nil { + return nil, fmt.Errorf(`query edges "followers": %v`, err) + } + query.Where(user.IDIn(edgeids...)) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + nodes, ok := edges[n.ID] + if !ok { + return nil, fmt.Errorf(`unexpected "followers" node returned %v`, n.ID) + } + for i := range nodes { + nodes[i].Edges.Followers = append(nodes[i].Edges.Followers, n) + } + } + } + + if query := uq.withFollowing; query != nil { + fks := make([]driver.Value, 0, len(nodes)) + ids := make(map[string]*User, len(nodes)) + for _, node := range nodes { + ids[node.ID] = node + fks = append(fks, node.ID) + } + var ( + edgeids []string + edges = make(map[string][]*User) + ) + spec := &sqlgraph.EdgeQuerySpec{ + Edge: &sqlgraph.EdgeSpec{ + Inverse: false, + Table: user.FollowingTable, + Columns: user.FollowingPrimaryKey, + }, + Predicate: func(s *sql.Selector) { + s.Where(sql.InValues(user.FollowingPrimaryKey[0], fks...)) + }, + + ScanValues: func() [2]interface{} { + return [2]interface{}{&sql.NullInt64{}, &sql.NullInt64{}} + }, + Assign: func(out, in interface{}) error { + eout, ok := out.(*sql.NullInt64) + if !ok || eout == nil { + return fmt.Errorf("unexpected id value for edge-out") + } + ein, ok := in.(*sql.NullInt64) + if !ok || ein == nil { + return fmt.Errorf("unexpected id value for edge-in") + } + outValue := strconv.FormatInt(eout.Int64, 10) + inValue := strconv.FormatInt(ein.Int64, 10) + node, ok := ids[outValue] + if !ok { + return fmt.Errorf("unexpected node id in edges: %v", outValue) + } + edgeids = append(edgeids, inValue) + edges[inValue] = append(edges[inValue], node) + return nil + }, + } + if err := sqlgraph.QueryEdges(ctx, uq.driver, spec); err != nil { + return nil, fmt.Errorf(`query edges "following": %v`, err) + } + query.Where(user.IDIn(edgeids...)) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + nodes, ok := edges[n.ID] + if !ok { + return nil, fmt.Errorf(`unexpected "following" node returned %v`, n.ID) + } + for i := range nodes { + nodes[i].Edges.Following = append(nodes[i].Edges.Following, n) + } + } + } + + if query := uq.withTeam; query != nil { + fks := make([]driver.Value, 0, len(nodes)) + nodeids := make(map[string]*User) + for i := range nodes { + id, err := strconv.Atoi(nodes[i].ID) + if err != nil { + return nil, err + } + fks = append(fks, id) + nodeids[nodes[i].ID] = nodes[i] + } + query.withFKs = true + query.Where(predicate.Pet(func(s *sql.Selector) { + s.Where(sql.InValues(user.TeamColumn, fks...)) + })) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + fk := n.team_id + if fk == nil { + return nil, fmt.Errorf(`foreign-key "team_id" is nil for node %v`, n.ID) + } + node, ok := nodeids[*fk] + if !ok { + return nil, fmt.Errorf(`unexpected foreign-key "team_id" returned %v for node %v`, *fk, n.ID) + } + node.Edges.Team = n + } + } + + if query := uq.withSpouse; query != nil { + ids := make([]string, 0, len(nodes)) + nodeids := make(map[string][]*User) + for i := range nodes { + if fk := nodes[i].user_spouse_id; fk != nil { + ids = append(ids, *fk) + nodeids[*fk] = append(nodeids[*fk], nodes[i]) + } + } + query.Where(user.IDIn(ids...)) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + nodes, ok := nodeids[n.ID] + if !ok { + return nil, fmt.Errorf(`unexpected foreign-key "user_spouse_id" returned %v`, n.ID) + } + for i := range nodes { + nodes[i].Edges.Spouse = n + } + } + } + + if query := uq.withChildren; query != nil { + fks := make([]driver.Value, 0, len(nodes)) + nodeids := make(map[string]*User) + for i := range nodes { + id, err := strconv.Atoi(nodes[i].ID) + if err != nil { + return nil, err + } + fks = append(fks, id) + nodeids[nodes[i].ID] = nodes[i] + } + query.withFKs = true + query.Where(predicate.User(func(s *sql.Selector) { + s.Where(sql.InValues(user.ChildrenColumn, fks...)) + })) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + fk := n.parent_id + if fk == nil { + return nil, fmt.Errorf(`foreign-key "parent_id" is nil for node %v`, n.ID) + } + node, ok := nodeids[*fk] + if !ok { + return nil, fmt.Errorf(`unexpected foreign-key "parent_id" returned %v for node %v`, *fk, n.ID) + } + node.Edges.Children = append(node.Edges.Children, n) + } + } + + if query := uq.withParent; query != nil { + ids := make([]string, 0, len(nodes)) + nodeids := make(map[string][]*User) + for i := range nodes { + if fk := nodes[i].parent_id; fk != nil { + ids = append(ids, *fk) + nodeids[*fk] = append(nodeids[*fk], nodes[i]) + } + } + query.Where(user.IDIn(ids...)) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + nodes, ok := nodeids[n.ID] + if !ok { + return nil, fmt.Errorf(`unexpected foreign-key "parent_id" returned %v`, n.ID) + } + for i := range nodes { + nodes[i].Edges.Parent = n + } + } + } + return nodes, nil } diff --git a/entc/integration/gremlin/ent/card.go b/entc/integration/gremlin/ent/card.go index 3d1e5d909..9f26b2806 100644 --- a/entc/integration/gremlin/ent/card.go +++ b/entc/integration/gremlin/ent/card.go @@ -28,6 +28,12 @@ type Card struct { Number string `json:"number,omitempty"` // Name holds the value of the "name" field. Name string `json:"name,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the CardQuery when eager-loading is set. + Edges struct { + // Owner holds the value of the owner edge. + Owner *User + } // StaticField defined by templates. StaticField string `json:"boring,omitempty"` diff --git a/entc/integration/gremlin/ent/card_query.go b/entc/integration/gremlin/ent/card_query.go index cdb6896d7..97572166c 100644 --- a/entc/integration/gremlin/ent/card_query.go +++ b/entc/integration/gremlin/ent/card_query.go @@ -28,6 +28,8 @@ type CardQuery struct { order []Order unique []string predicates []predicate.Card + // eager-loading edges. + withOwner *UserQuery // intermediate query. gremlin *dsl.Traversal } @@ -233,6 +235,17 @@ func (cq *CardQuery) Clone() *CardQuery { } } +// WithOwner tells the query-builder to eager-loads the nodes that are connected to +// the "owner" edge. The optional arguments used to configure the query builder of the edge. +func (cq *CardQuery) WithOwner(opts ...func(*UserQuery)) *CardQuery { + query := &UserQuery{config: cq.config} + for _, opt := range opts { + opt(query) + } + cq.withOwner = query + return cq +} + // GroupBy used to group vertices by one or more fields/columns. // It is often used with aggregate functions, like: count, max, mean, min, sum. // diff --git a/entc/integration/gremlin/ent/file.go b/entc/integration/gremlin/ent/file.go index 74aa2cfc6..ce17f89d8 100644 --- a/entc/integration/gremlin/ent/file.go +++ b/entc/integration/gremlin/ent/file.go @@ -27,6 +27,14 @@ type File struct { User *string `json:"user,omitempty"` // Group holds the value of the "group" field. Group string `json:"group,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the FileQuery when eager-loading is set. + Edges struct { + // Owner holds the value of the owner edge. + Owner *User + // Type holds the value of the type edge. + Type *FileType + } } // FromResponse scans the gremlin response data into File. diff --git a/entc/integration/gremlin/ent/file_query.go b/entc/integration/gremlin/ent/file_query.go index fcf370da2..46533af35 100644 --- a/entc/integration/gremlin/ent/file_query.go +++ b/entc/integration/gremlin/ent/file_query.go @@ -29,6 +29,9 @@ type FileQuery struct { order []Order unique []string predicates []predicate.File + // eager-loading edges. + withOwner *UserQuery + withType *FileTypeQuery // intermediate query. gremlin *dsl.Traversal } @@ -242,6 +245,28 @@ func (fq *FileQuery) Clone() *FileQuery { } } +// WithOwner tells the query-builder to eager-loads the nodes that are connected to +// the "owner" edge. The optional arguments used to configure the query builder of the edge. +func (fq *FileQuery) WithOwner(opts ...func(*UserQuery)) *FileQuery { + query := &UserQuery{config: fq.config} + for _, opt := range opts { + opt(query) + } + fq.withOwner = query + return fq +} + +// WithType tells the query-builder to eager-loads the nodes that are connected to +// the "type" edge. The optional arguments used to configure the query builder of the edge. +func (fq *FileQuery) WithType(opts ...func(*FileTypeQuery)) *FileQuery { + query := &FileTypeQuery{config: fq.config} + for _, opt := range opts { + opt(query) + } + fq.withType = query + return fq +} + // GroupBy used to group vertices by one or more fields/columns. // It is often used with aggregate functions, like: count, max, mean, min, sum. // diff --git a/entc/integration/gremlin/ent/filetype.go b/entc/integration/gremlin/ent/filetype.go index dcc333598..b3ae42f45 100644 --- a/entc/integration/gremlin/ent/filetype.go +++ b/entc/integration/gremlin/ent/filetype.go @@ -21,6 +21,12 @@ type FileType struct { ID string `json:"id,omitempty"` // Name holds the value of the "name" field. Name string `json:"name,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the FileTypeQuery when eager-loading is set. + Edges struct { + // Files holds the value of the files edge. + Files []*File + } } // FromResponse scans the gremlin response data into FileType. diff --git a/entc/integration/gremlin/ent/filetype_query.go b/entc/integration/gremlin/ent/filetype_query.go index 1a07c8d1d..f8bfca6e4 100644 --- a/entc/integration/gremlin/ent/filetype_query.go +++ b/entc/integration/gremlin/ent/filetype_query.go @@ -27,6 +27,8 @@ type FileTypeQuery struct { order []Order unique []string predicates []predicate.FileType + // eager-loading edges. + withFiles *FileQuery // intermediate query. gremlin *dsl.Traversal } @@ -232,6 +234,17 @@ func (ftq *FileTypeQuery) Clone() *FileTypeQuery { } } +// WithFiles tells the query-builder to eager-loads the nodes that are connected to +// the "files" edge. The optional arguments used to configure the query builder of the edge. +func (ftq *FileTypeQuery) WithFiles(opts ...func(*FileQuery)) *FileTypeQuery { + query := &FileQuery{config: ftq.config} + for _, opt := range opts { + opt(query) + } + ftq.withFiles = query + return ftq +} + // GroupBy used to group vertices by one or more fields/columns. // It is often used with aggregate functions, like: count, max, mean, min, sum. // diff --git a/entc/integration/gremlin/ent/group.go b/entc/integration/gremlin/ent/group.go index eee3b864b..d4f162751 100644 --- a/entc/integration/gremlin/ent/group.go +++ b/entc/integration/gremlin/ent/group.go @@ -30,6 +30,18 @@ type Group struct { MaxUsers int `json:"max_users,omitempty"` // Name holds the value of the "name" field. Name string `json:"name,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the GroupQuery when eager-loading is set. + Edges struct { + // Files holds the value of the files edge. + Files []*File + // Blocked holds the value of the blocked edge. + Blocked []*User + // Users holds the value of the users edge. + Users []*User + // Info holds the value of the info edge. + Info *GroupInfo + } } // FromResponse scans the gremlin response data into Group. diff --git a/entc/integration/gremlin/ent/group_query.go b/entc/integration/gremlin/ent/group_query.go index 4f5c37746..de8a8f439 100644 --- a/entc/integration/gremlin/ent/group_query.go +++ b/entc/integration/gremlin/ent/group_query.go @@ -28,6 +28,11 @@ type GroupQuery struct { order []Order unique []string predicates []predicate.Group + // eager-loading edges. + withFiles *FileQuery + withBlocked *UserQuery + withUsers *UserQuery + withInfo *GroupInfoQuery // intermediate query. gremlin *dsl.Traversal } @@ -257,6 +262,50 @@ func (gq *GroupQuery) Clone() *GroupQuery { } } +// WithFiles tells the query-builder to eager-loads the nodes that are connected to +// the "files" edge. The optional arguments used to configure the query builder of the edge. +func (gq *GroupQuery) WithFiles(opts ...func(*FileQuery)) *GroupQuery { + query := &FileQuery{config: gq.config} + for _, opt := range opts { + opt(query) + } + gq.withFiles = query + return gq +} + +// WithBlocked tells the query-builder to eager-loads the nodes that are connected to +// the "blocked" edge. The optional arguments used to configure the query builder of the edge. +func (gq *GroupQuery) WithBlocked(opts ...func(*UserQuery)) *GroupQuery { + query := &UserQuery{config: gq.config} + for _, opt := range opts { + opt(query) + } + gq.withBlocked = query + return gq +} + +// WithUsers tells the query-builder to eager-loads the nodes that are connected to +// the "users" edge. The optional arguments used to configure the query builder of the edge. +func (gq *GroupQuery) WithUsers(opts ...func(*UserQuery)) *GroupQuery { + query := &UserQuery{config: gq.config} + for _, opt := range opts { + opt(query) + } + gq.withUsers = query + return gq +} + +// WithInfo tells the query-builder to eager-loads the nodes that are connected to +// the "info" edge. The optional arguments used to configure the query builder of the edge. +func (gq *GroupQuery) WithInfo(opts ...func(*GroupInfoQuery)) *GroupQuery { + query := &GroupInfoQuery{config: gq.config} + for _, opt := range opts { + opt(query) + } + gq.withInfo = query + return gq +} + // GroupBy used to group vertices by one or more fields/columns. // It is often used with aggregate functions, like: count, max, mean, min, sum. // diff --git a/entc/integration/gremlin/ent/groupinfo.go b/entc/integration/gremlin/ent/groupinfo.go index abb996fb9..a6523218f 100644 --- a/entc/integration/gremlin/ent/groupinfo.go +++ b/entc/integration/gremlin/ent/groupinfo.go @@ -23,6 +23,12 @@ type GroupInfo struct { Desc string `json:"desc,omitempty"` // MaxUsers holds the value of the "max_users" field. MaxUsers int `json:"max_users,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the GroupInfoQuery when eager-loading is set. + Edges struct { + // Groups holds the value of the groups edge. + Groups []*Group + } } // FromResponse scans the gremlin response data into GroupInfo. diff --git a/entc/integration/gremlin/ent/groupinfo_query.go b/entc/integration/gremlin/ent/groupinfo_query.go index 7b9086719..d4b03b432 100644 --- a/entc/integration/gremlin/ent/groupinfo_query.go +++ b/entc/integration/gremlin/ent/groupinfo_query.go @@ -28,6 +28,8 @@ type GroupInfoQuery struct { order []Order unique []string predicates []predicate.GroupInfo + // eager-loading edges. + withGroups *GroupQuery // intermediate query. gremlin *dsl.Traversal } @@ -233,6 +235,17 @@ func (giq *GroupInfoQuery) Clone() *GroupInfoQuery { } } +// WithGroups tells the query-builder to eager-loads the nodes that are connected to +// the "groups" edge. The optional arguments used to configure the query builder of the edge. +func (giq *GroupInfoQuery) WithGroups(opts ...func(*GroupQuery)) *GroupInfoQuery { + query := &GroupQuery{config: giq.config} + for _, opt := range opts { + opt(query) + } + giq.withGroups = query + return giq +} + // GroupBy used to group vertices by one or more fields/columns. // It is often used with aggregate functions, like: count, max, mean, min, sum. // diff --git a/entc/integration/gremlin/ent/node.go b/entc/integration/gremlin/ent/node.go index 836d11ea9..0d52dbecb 100644 --- a/entc/integration/gremlin/ent/node.go +++ b/entc/integration/gremlin/ent/node.go @@ -21,6 +21,14 @@ type Node struct { ID string `json:"id,omitempty"` // Value holds the value of the "value" field. Value int `json:"value,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the NodeQuery when eager-loading is set. + Edges struct { + // Prev holds the value of the prev edge. + Prev *Node + // Next holds the value of the next edge. + Next *Node + } } // FromResponse scans the gremlin response data into Node. diff --git a/entc/integration/gremlin/ent/node_query.go b/entc/integration/gremlin/ent/node_query.go index f79150ba6..2e1e04696 100644 --- a/entc/integration/gremlin/ent/node_query.go +++ b/entc/integration/gremlin/ent/node_query.go @@ -27,6 +27,9 @@ type NodeQuery struct { order []Order unique []string predicates []predicate.Node + // eager-loading edges. + withPrev *NodeQuery + withNext *NodeQuery // intermediate query. gremlin *dsl.Traversal } @@ -240,6 +243,28 @@ func (nq *NodeQuery) Clone() *NodeQuery { } } +// WithPrev tells the query-builder to eager-loads the nodes that are connected to +// the "prev" edge. The optional arguments used to configure the query builder of the edge. +func (nq *NodeQuery) WithPrev(opts ...func(*NodeQuery)) *NodeQuery { + query := &NodeQuery{config: nq.config} + for _, opt := range opts { + opt(query) + } + nq.withPrev = query + return nq +} + +// WithNext tells the query-builder to eager-loads the nodes that are connected to +// the "next" edge. The optional arguments used to configure the query builder of the edge. +func (nq *NodeQuery) WithNext(opts ...func(*NodeQuery)) *NodeQuery { + query := &NodeQuery{config: nq.config} + for _, opt := range opts { + opt(query) + } + nq.withNext = query + return nq +} + // GroupBy used to group vertices by one or more fields/columns. // It is often used with aggregate functions, like: count, max, mean, min, sum. // diff --git a/entc/integration/gremlin/ent/pet.go b/entc/integration/gremlin/ent/pet.go index b23a5e287..6f540057a 100644 --- a/entc/integration/gremlin/ent/pet.go +++ b/entc/integration/gremlin/ent/pet.go @@ -21,6 +21,14 @@ type Pet struct { ID string `json:"id,omitempty"` // Name holds the value of the "name" field. Name string `json:"name,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the PetQuery when eager-loading is set. + Edges struct { + // Team holds the value of the team edge. + Team *User + // Owner holds the value of the owner edge. + Owner *User + } } // FromResponse scans the gremlin response data into Pet. diff --git a/entc/integration/gremlin/ent/pet_query.go b/entc/integration/gremlin/ent/pet_query.go index 3cbc4208f..d74c46f6e 100644 --- a/entc/integration/gremlin/ent/pet_query.go +++ b/entc/integration/gremlin/ent/pet_query.go @@ -28,6 +28,9 @@ type PetQuery struct { order []Order unique []string predicates []predicate.Pet + // eager-loading edges. + withTeam *UserQuery + withOwner *UserQuery // intermediate query. gremlin *dsl.Traversal } @@ -241,6 +244,28 @@ func (pq *PetQuery) Clone() *PetQuery { } } +// WithTeam tells the query-builder to eager-loads the nodes that are connected to +// the "team" edge. The optional arguments used to configure the query builder of the edge. +func (pq *PetQuery) WithTeam(opts ...func(*UserQuery)) *PetQuery { + query := &UserQuery{config: pq.config} + for _, opt := range opts { + opt(query) + } + pq.withTeam = query + return pq +} + +// WithOwner tells the query-builder to eager-loads the nodes that are connected to +// the "owner" edge. The optional arguments used to configure the query builder of the edge. +func (pq *PetQuery) WithOwner(opts ...func(*UserQuery)) *PetQuery { + query := &UserQuery{config: pq.config} + for _, opt := range opts { + opt(query) + } + pq.withOwner = query + return pq +} + // GroupBy used to group vertices by one or more fields/columns. // It is often used with aggregate functions, like: count, max, mean, min, sum. // diff --git a/entc/integration/gremlin/ent/user.go b/entc/integration/gremlin/ent/user.go index 5d902f984..6962e255d 100644 --- a/entc/integration/gremlin/ent/user.go +++ b/entc/integration/gremlin/ent/user.go @@ -36,6 +36,32 @@ type User struct { Password string `graphql:"-" json:"-"` // Role holds the value of the "role" field. Role user.Role `json:"role,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the UserQuery when eager-loading is set. + Edges struct { + // Card holds the value of the card edge. + Card *Card + // Pets holds the value of the pets edge. + Pets []*Pet + // Files holds the value of the files edge. + Files []*File + // Groups holds the value of the groups edge. + Groups []*Group + // Friends holds the value of the friends edge. + Friends []*User + // Followers holds the value of the followers edge. + Followers []*User + // Following holds the value of the following edge. + Following []*User + // Team holds the value of the team edge. + Team *Pet + // Spouse holds the value of the spouse edge. + Spouse *User + // Children holds the value of the children edge. + Children []*User + // Parent holds the value of the parent edge. + Parent *User + } } // FromResponse scans the gremlin response data into User. diff --git a/entc/integration/gremlin/ent/user_query.go b/entc/integration/gremlin/ent/user_query.go index 1607d22b1..583c49ef4 100644 --- a/entc/integration/gremlin/ent/user_query.go +++ b/entc/integration/gremlin/ent/user_query.go @@ -27,6 +27,18 @@ type UserQuery struct { order []Order unique []string predicates []predicate.User + // eager-loading edges. + withCard *CardQuery + withPets *PetQuery + withFiles *FileQuery + withGroups *GroupQuery + withFriends *UserQuery + withFollowers *UserQuery + withFollowing *UserQuery + withTeam *PetQuery + withSpouse *UserQuery + withChildren *UserQuery + withParent *UserQuery // intermediate query. gremlin *dsl.Traversal } @@ -312,6 +324,127 @@ func (uq *UserQuery) Clone() *UserQuery { } } +// WithCard tells the query-builder to eager-loads the nodes that are connected to +// the "card" edge. The optional arguments used to configure the query builder of the edge. +func (uq *UserQuery) WithCard(opts ...func(*CardQuery)) *UserQuery { + query := &CardQuery{config: uq.config} + for _, opt := range opts { + opt(query) + } + uq.withCard = query + return uq +} + +// WithPets tells the query-builder to eager-loads the nodes that are connected to +// the "pets" edge. The optional arguments used to configure the query builder of the edge. +func (uq *UserQuery) WithPets(opts ...func(*PetQuery)) *UserQuery { + query := &PetQuery{config: uq.config} + for _, opt := range opts { + opt(query) + } + uq.withPets = query + return uq +} + +// WithFiles tells the query-builder to eager-loads the nodes that are connected to +// the "files" edge. The optional arguments used to configure the query builder of the edge. +func (uq *UserQuery) WithFiles(opts ...func(*FileQuery)) *UserQuery { + query := &FileQuery{config: uq.config} + for _, opt := range opts { + opt(query) + } + uq.withFiles = query + return uq +} + +// WithGroups tells the query-builder to eager-loads the nodes that are connected to +// the "groups" edge. The optional arguments used to configure the query builder of the edge. +func (uq *UserQuery) WithGroups(opts ...func(*GroupQuery)) *UserQuery { + query := &GroupQuery{config: uq.config} + for _, opt := range opts { + opt(query) + } + uq.withGroups = query + return uq +} + +// WithFriends tells the query-builder to eager-loads the nodes that are connected to +// the "friends" edge. The optional arguments used to configure the query builder of the edge. +func (uq *UserQuery) WithFriends(opts ...func(*UserQuery)) *UserQuery { + query := &UserQuery{config: uq.config} + for _, opt := range opts { + opt(query) + } + uq.withFriends = query + return uq +} + +// WithFollowers tells the query-builder to eager-loads the nodes that are connected to +// the "followers" edge. The optional arguments used to configure the query builder of the edge. +func (uq *UserQuery) WithFollowers(opts ...func(*UserQuery)) *UserQuery { + query := &UserQuery{config: uq.config} + for _, opt := range opts { + opt(query) + } + uq.withFollowers = query + return uq +} + +// WithFollowing tells the query-builder to eager-loads the nodes that are connected to +// the "following" edge. The optional arguments used to configure the query builder of the edge. +func (uq *UserQuery) WithFollowing(opts ...func(*UserQuery)) *UserQuery { + query := &UserQuery{config: uq.config} + for _, opt := range opts { + opt(query) + } + uq.withFollowing = query + return uq +} + +// WithTeam tells the query-builder to eager-loads the nodes that are connected to +// the "team" edge. The optional arguments used to configure the query builder of the edge. +func (uq *UserQuery) WithTeam(opts ...func(*PetQuery)) *UserQuery { + query := &PetQuery{config: uq.config} + for _, opt := range opts { + opt(query) + } + uq.withTeam = query + return uq +} + +// WithSpouse tells the query-builder to eager-loads the nodes that are connected to +// the "spouse" edge. The optional arguments used to configure the query builder of the edge. +func (uq *UserQuery) WithSpouse(opts ...func(*UserQuery)) *UserQuery { + query := &UserQuery{config: uq.config} + for _, opt := range opts { + opt(query) + } + uq.withSpouse = query + return uq +} + +// WithChildren tells the query-builder to eager-loads the nodes that are connected to +// the "children" edge. The optional arguments used to configure the query builder of the edge. +func (uq *UserQuery) WithChildren(opts ...func(*UserQuery)) *UserQuery { + query := &UserQuery{config: uq.config} + for _, opt := range opts { + opt(query) + } + uq.withChildren = query + return uq +} + +// WithParent tells the query-builder to eager-loads the nodes that are connected to +// the "parent" edge. The optional arguments used to configure the query builder of the edge. +func (uq *UserQuery) WithParent(opts ...func(*UserQuery)) *UserQuery { + query := &UserQuery{config: uq.config} + for _, opt := range opts { + opt(query) + } + uq.withParent = query + return uq +} + // GroupBy used to group vertices by one or more fields/columns. // It is often used with aggregate functions, like: count, max, mean, min, sum. // diff --git a/entc/integration/idtype/ent/user.go b/entc/integration/idtype/ent/user.go index 04cc0e9ba..2116c004e 100644 --- a/entc/integration/idtype/ent/user.go +++ b/entc/integration/idtype/ent/user.go @@ -21,20 +21,38 @@ type User struct { ID uint64 `json:"id,omitempty"` // Name holds the value of the "name" field. Name string `json:"name,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the UserQuery when eager-loading is set. + Edges struct { + // Spouse holds the value of the spouse edge. + Spouse *User + // Followers holds the value of the followers edge. + Followers []*User + // Following holds the value of the following edge. + Following []*User + } + user_spouse_id *uint64 } // scanValues returns the types for scanning values from sql.Rows. func (*User) scanValues() []interface{} { return []interface{}{ - &sql.NullInt64{}, - &sql.NullString{}, + &sql.NullInt64{}, // id + &sql.NullString{}, // name + } +} + +// fkValues returns the types for scanning foreign-keys values from sql.Rows. +func (*User) fkValues() []interface{} { + return []interface{}{ + &sql.NullInt64{}, // user_spouse_id } } // assignValues assigns the values that were returned from sql.Rows (after scanning) // to the User fields. func (u *User) assignValues(values ...interface{}) error { - if m, n := len(values), len(user.Columns); m != n { + if m, n := len(values), len(user.Columns); m < n { return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) } value, ok := values[0].(*sql.NullInt64) @@ -48,6 +66,15 @@ func (u *User) assignValues(values ...interface{}) error { } else if value.Valid { u.Name = value.String } + values = values[1:] + if len(values) == len(user.ForeignKeys) { + if value, ok := values[0].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for edge-field user_spouse_id", value) + } else if value.Valid { + u.user_spouse_id = new(uint64) + *u.user_spouse_id = uint64(value.Int64) + } + } return nil } diff --git a/entc/integration/idtype/ent/user/user.go b/entc/integration/idtype/ent/user/user.go index 62ab85538..9ac692442 100644 --- a/entc/integration/idtype/ent/user/user.go +++ b/entc/integration/idtype/ent/user/user.go @@ -26,12 +26,17 @@ const ( FollowingTable = "user_following" ) -// Columns holds all SQL columns are user fields. +// Columns holds all SQL columns for user fields. var Columns = []string{ FieldID, FieldName, } +// ForeignKeys holds the SQL foreign-keys that are owned by the User type. +var ForeignKeys = []string{ + "user_spouse_id", +} + var ( // FollowersPrimaryKey and FollowersColumn2 are the table columns denoting the // primary key for the followers relation (M2M). diff --git a/entc/integration/idtype/ent/user_query.go b/entc/integration/idtype/ent/user_query.go index dd20a8b29..cadd11c93 100644 --- a/entc/integration/idtype/ent/user_query.go +++ b/entc/integration/idtype/ent/user_query.go @@ -8,6 +8,7 @@ package ent import ( "context" + "database/sql/driver" "errors" "fmt" "math" @@ -27,6 +28,11 @@ type UserQuery struct { order []Order unique []string predicates []predicate.User + // eager-loading edges. + withSpouse *UserQuery + withFollowers *UserQuery + withFollowing *UserQuery + withFKs bool // intermediate query. sql *sql.Selector } @@ -260,6 +266,39 @@ func (uq *UserQuery) Clone() *UserQuery { } } +// WithSpouse tells the query-builder to eager-loads the nodes that are connected to +// the "spouse" edge. The optional arguments used to configure the query builder of the edge. +func (uq *UserQuery) WithSpouse(opts ...func(*UserQuery)) *UserQuery { + query := &UserQuery{config: uq.config} + for _, opt := range opts { + opt(query) + } + uq.withSpouse = query + return uq +} + +// WithFollowers tells the query-builder to eager-loads the nodes that are connected to +// the "followers" edge. The optional arguments used to configure the query builder of the edge. +func (uq *UserQuery) WithFollowers(opts ...func(*UserQuery)) *UserQuery { + query := &UserQuery{config: uq.config} + for _, opt := range opts { + opt(query) + } + uq.withFollowers = query + return uq +} + +// WithFollowing tells the query-builder to eager-loads the nodes that are connected to +// the "following" edge. The optional arguments used to configure the query builder of the edge. +func (uq *UserQuery) WithFollowing(opts ...func(*UserQuery)) *UserQuery { + query := &UserQuery{config: uq.config} + for _, opt := range opts { + opt(query) + } + uq.withFollowing = query + return uq +} + // GroupBy used to group vertices by one or more fields/columns. // It is often used with aggregate functions, like: count, max, mean, min, sum. // @@ -303,13 +342,24 @@ func (uq *UserQuery) Select(field string, fields ...string) *UserSelect { func (uq *UserQuery) sqlAll(ctx context.Context) ([]*User, error) { var ( - nodes []*User - spec = uq.querySpec() + nodes []*User + withFKs = uq.withFKs + spec = uq.querySpec() ) + if uq.withSpouse != nil { + withFKs = true + } + if withFKs { + spec.Node.Columns = append(spec.Node.Columns, user.ForeignKeys...) + } spec.ScanValues = func() []interface{} { node := &User{config: uq.config} nodes = append(nodes, node) - return node.scanValues() + values := node.scanValues() + if withFKs { + values = append(values, node.fkValues()...) + } + return values } spec.Assign = func(values ...interface{}) error { if len(nodes) == 0 { @@ -321,6 +371,158 @@ func (uq *UserQuery) sqlAll(ctx context.Context) ([]*User, error) { if err := sqlgraph.QueryNodes(ctx, uq.driver, spec); err != nil { return nil, err } + + if query := uq.withSpouse; query != nil { + ids := make([]uint64, 0, len(nodes)) + nodeids := make(map[uint64][]*User) + for i := range nodes { + if fk := nodes[i].user_spouse_id; fk != nil { + ids = append(ids, *fk) + nodeids[*fk] = append(nodeids[*fk], nodes[i]) + } + } + query.Where(user.IDIn(ids...)) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + nodes, ok := nodeids[n.ID] + if !ok { + return nil, fmt.Errorf(`unexpected foreign-key "user_spouse_id" returned %v`, n.ID) + } + for i := range nodes { + nodes[i].Edges.Spouse = n + } + } + } + + if query := uq.withFollowers; query != nil { + fks := make([]driver.Value, 0, len(nodes)) + ids := make(map[uint64]*User, len(nodes)) + for _, node := range nodes { + ids[node.ID] = node + fks = append(fks, node.ID) + } + var ( + edgeids []uint64 + edges = make(map[uint64][]*User) + ) + spec := &sqlgraph.EdgeQuerySpec{ + Edge: &sqlgraph.EdgeSpec{ + Inverse: true, + Table: user.FollowersTable, + Columns: user.FollowersPrimaryKey, + }, + Predicate: func(s *sql.Selector) { + s.Where(sql.InValues(user.FollowersPrimaryKey[1], fks...)) + }, + + ScanValues: func() [2]interface{} { + return [2]interface{}{&sql.NullInt64{}, &sql.NullInt64{}} + }, + Assign: func(out, in interface{}) error { + eout, ok := out.(*sql.NullInt64) + if !ok || eout == nil { + return fmt.Errorf("unexpected id value for edge-out") + } + ein, ok := in.(*sql.NullInt64) + if !ok || ein == nil { + return fmt.Errorf("unexpected id value for edge-in") + } + outValue := uint64(eout.Int64) + inValue := uint64(eout.Int64) + node, ok := ids[outValue] + if !ok { + return fmt.Errorf("unexpected node id in edges: %v", outValue) + } + edgeids = append(edgeids, inValue) + edges[inValue] = append(edges[inValue], node) + return nil + }, + } + if err := sqlgraph.QueryEdges(ctx, uq.driver, spec); err != nil { + return nil, fmt.Errorf(`query edges "followers": %v`, err) + } + query.Where(user.IDIn(edgeids...)) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + nodes, ok := edges[n.ID] + if !ok { + return nil, fmt.Errorf(`unexpected "followers" node returned %v`, n.ID) + } + for i := range nodes { + nodes[i].Edges.Followers = append(nodes[i].Edges.Followers, n) + } + } + } + + if query := uq.withFollowing; query != nil { + fks := make([]driver.Value, 0, len(nodes)) + ids := make(map[uint64]*User, len(nodes)) + for _, node := range nodes { + ids[node.ID] = node + fks = append(fks, node.ID) + } + var ( + edgeids []uint64 + edges = make(map[uint64][]*User) + ) + spec := &sqlgraph.EdgeQuerySpec{ + Edge: &sqlgraph.EdgeSpec{ + Inverse: false, + Table: user.FollowingTable, + Columns: user.FollowingPrimaryKey, + }, + Predicate: func(s *sql.Selector) { + s.Where(sql.InValues(user.FollowingPrimaryKey[0], fks...)) + }, + + ScanValues: func() [2]interface{} { + return [2]interface{}{&sql.NullInt64{}, &sql.NullInt64{}} + }, + Assign: func(out, in interface{}) error { + eout, ok := out.(*sql.NullInt64) + if !ok || eout == nil { + return fmt.Errorf("unexpected id value for edge-out") + } + ein, ok := in.(*sql.NullInt64) + if !ok || ein == nil { + return fmt.Errorf("unexpected id value for edge-in") + } + outValue := uint64(eout.Int64) + inValue := uint64(eout.Int64) + node, ok := ids[outValue] + if !ok { + return fmt.Errorf("unexpected node id in edges: %v", outValue) + } + edgeids = append(edgeids, inValue) + edges[inValue] = append(edges[inValue], node) + return nil + }, + } + if err := sqlgraph.QueryEdges(ctx, uq.driver, spec); err != nil { + return nil, fmt.Errorf(`query edges "following": %v`, err) + } + query.Where(user.IDIn(edgeids...)) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + nodes, ok := edges[n.ID] + if !ok { + return nil, fmt.Errorf(`unexpected "following" node returned %v`, n.ID) + } + for i := range nodes { + nodes[i].Edges.Following = append(nodes[i].Edges.Following, n) + } + } + } + return nodes, nil } diff --git a/entc/integration/integration_test.go b/entc/integration/integration_test.go index 6adb5a08e..c04ac2ac8 100644 --- a/entc/integration/integration_test.go +++ b/entc/integration/integration_test.go @@ -115,6 +115,7 @@ var tests = []func(*testing.T, *ent.Client){ DefaultValue, ImmutableValue, Sensitive, + EagerLoading, } func Sanity(t *testing.T, client *ent.Client) { @@ -871,6 +872,130 @@ func Sensitive(t *testing.T, client *ent.Client) { require.NotContains(string(b), "secret-password") } +func EagerLoading(t *testing.T, client *ent.Client) { + ctx := context.Background() + require := require.New(t) + + a8m := client.User.Create().SetName("a8m").SetAge(30).SaveX(ctx) + nati := client.User.Create().SetName("nati").SetAge(28).SetSpouse(a8m).SaveX(ctx) + alex := client.User.Create().SetName("alexsn").SetAge(35).AddFriends(a8m).SaveX(ctx) + client.Pet.Create().SetName("xabi").SaveX(ctx) + client.Pet.Create().SetName("pedro").SetOwner(a8m).SetTeam(nati).SaveX(ctx) + client.Card.Create().SetNumber("102030").SetOwner(a8m).SaveX(ctx) + + inf := client.GroupInfo.Create().SetDesc("desc").SaveX(ctx) + files := ent.Files{ + client.File.Create().SetName("a").SetSize(10).SaveX(ctx), + client.File.Create().SetName("b").SetSize(10).SaveX(ctx), + client.File.Create().SetName("c").SetSize(10).SaveX(ctx), + } + typ := client.FileType.Create().SetName("type").AddFiles(files...).SaveX(ctx) + hub := client.Group.Create().SetName("GitHub").SetExpire(time.Now()).AddUsers(alex, a8m).SetInfo(inf).SaveX(ctx) + lab := client.Group.Create().SetName("GitLab").SetExpire(time.Now()).AddUsers(nati, a8m).SetInfo(inf).AddFiles(files...).SaveX(ctx) + + t.Run("O2O", func(t *testing.T) { + users := client.User. + Query(). + Where(user.HasSpouse()). + WithSpouse(). + WithCard(). + Order(ent.Asc(user.FieldName)). + AllX(ctx) + require.Len(users, 2) + require.NotNil(users[0].Edges.Spouse) + require.NotNil(users[1].Edges.Spouse) + require.NotNil(nati.Name, users[0].Edges.Spouse.Name) + require.NotNil(a8m.Name, users[1].Edges.Spouse.Name) + require.NotNil(users[0].Edges.Card) + require.Nil(users[1].Edges.Card) + }) + + t.Run("O2M", func(t *testing.T) { + pets := client.Pet.Query().AllX(ctx) + require.Nil(pets[0].Edges.Team) + require.Nil(pets[0].Edges.Owner) + require.Nil(pets[1].Edges.Team) + require.Nil(pets[1].Edges.Owner) + + pedro := client.Pet.Query().Where(pet.HasOwner()).WithOwner().OnlyX(ctx) + require.Nil(pedro.Edges.Team) + require.NotNil(pedro.Edges.Owner) + require.Equal(a8m.Name, pedro.Edges.Owner.Name) + + pedro = client.Pet.Query().Where(pet.HasOwner()).WithOwner().WithTeam().OnlyX(ctx) + require.NotNil(pedro.Edges.Team) + require.NotNil(pedro.Edges.Owner) + require.Equal(a8m.Name, pedro.Edges.Owner.Name) + require.Equal(nati.Name, pedro.Edges.Team.Name) + }) + + t.Run("M2O", func(t *testing.T) { + a8m = client.User.Query().Where(user.ID(a8m.ID)).OnlyX(ctx) + require.Empty(a8m.Edges.Pets) + + a8m = client.User. + Query(). + Where(user.ID(a8m.ID)). + WithPets(func(q *ent.PetQuery) { + q.WithTeam().Order(ent.Asc(pet.FieldName)) + }). + OnlyX(ctx) + require.Len(a8m.Edges.Pets, 1) + require.Equal("pedro", a8m.Edges.Pets[0].Name) + require.Equal(nati.Name, a8m.Edges.Pets[0].Edges.Team.Name) + }) + + t.Run("M2M", func(t *testing.T) { + users := client.User. + Query(). + WithFriends(). + WithGroups(func(q *ent.GroupQuery) { + q.Order(ent.Desc(group.FieldName)) + }). + Order(ent.Asc(user.FieldName)). + AllX(ctx) + require.Equal(a8m.Name, users[0].Name) + require.Len(users[0].Edges.Groups, 2) + require.Len(users[0].Edges.Friends, 1) + require.Equal(alex.Name, users[0].Edges.Friends[0].Name) + g1, g2 := users[0].Edges.Groups[0], users[0].Edges.Groups[1] + require.Equal(lab.Name, g1.Name) + require.Equal(hub.Name, g2.Name) + }) + + t.Run("Graph", func(t *testing.T) { + users := client.User. + Query(). + WithSpouse(). + WithFriends(). + WithGroups(func(q *ent.GroupQuery) { + q.WithInfo() + q.WithFiles(func(q *ent.FileQuery) { + q.WithType() + q.Order(ent.Asc(file.FieldName)) + }) + q.Order(ent.Desc(group.FieldName)) + }). + Order(ent.Asc(user.FieldName)). + AllX(ctx) + require.Equal(a8m.Name, users[0].Name) + require.NotNil(users[0].Edges.Spouse) + require.Equal(nati.Name, users[0].Edges.Spouse.Name) + require.Len(users[0].Edges.Groups, 2) + require.Len(users[0].Edges.Friends, 1) + require.Equal(alex.Name, users[0].Edges.Friends[0].Name) + g1, g2 := users[0].Edges.Groups[0], users[0].Edges.Groups[1] + require.Equal(lab.Name, g1.Name) + require.Equal(hub.Name, g2.Name) + require.Equal(inf.Desc, g1.Edges.Info.Desc) + require.Equal([]string{"a", "c"}, []string{g1.Edges.Files[0].Name, g1.Edges.Files[2].Name}) + for _, f := range g1.Edges.Files { + require.NotNil(f.Edges.Type) + require.Equal(typ.Name, f.Edges.Type.Name) + } + }) +} + func drop(t *testing.T, client *ent.Client) { t.Log("drop data from database") ctx := context.Background() diff --git a/entc/integration/json/ent/user.go b/entc/integration/json/ent/user.go index a5e7ffada..0f0ae537b 100644 --- a/entc/integration/json/ent/user.go +++ b/entc/integration/json/ent/user.go @@ -39,20 +39,20 @@ type User struct { // scanValues returns the types for scanning values from sql.Rows. func (*User) scanValues() []interface{} { return []interface{}{ - &sql.NullInt64{}, - &[]byte{}, - &[]byte{}, - &[]byte{}, - &[]byte{}, - &[]byte{}, - &[]byte{}, + &sql.NullInt64{}, // id + &[]byte{}, // url + &[]byte{}, // raw + &[]byte{}, // dirs + &[]byte{}, // ints + &[]byte{}, // floats + &[]byte{}, // strings } } // assignValues assigns the values that were returned from sql.Rows (after scanning) // to the User fields. func (u *User) assignValues(values ...interface{}) error { - if m, n := len(values), len(user.Columns); m != n { + if m, n := len(values), len(user.Columns); m < n { return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) } value, ok := values[0].(*sql.NullInt64) diff --git a/entc/integration/json/ent/user/user.go b/entc/integration/json/ent/user/user.go index e56d3a486..25b9b5b44 100644 --- a/entc/integration/json/ent/user/user.go +++ b/entc/integration/json/ent/user/user.go @@ -28,7 +28,7 @@ const ( Table = "users" ) -// Columns holds all SQL columns are user fields. +// Columns holds all SQL columns for user fields. var Columns = []string{ FieldID, FieldURL, diff --git a/entc/integration/json/ent/user_query.go b/entc/integration/json/ent/user_query.go index fe0b947d0..724c4085a 100644 --- a/entc/integration/json/ent/user_query.go +++ b/entc/integration/json/ent/user_query.go @@ -273,7 +273,8 @@ func (uq *UserQuery) sqlAll(ctx context.Context) ([]*User, error) { spec.ScanValues = func() []interface{} { node := &User{config: uq.config} nodes = append(nodes, node) - return node.scanValues() + values := node.scanValues() + return values } spec.Assign = func(values ...interface{}) error { if len(nodes) == 0 { diff --git a/entc/integration/migrate/entv1/car.go b/entc/integration/migrate/entv1/car.go index ebfebe2c6..33cf6688b 100644 --- a/entc/integration/migrate/entv1/car.go +++ b/entc/integration/migrate/entv1/car.go @@ -19,19 +19,33 @@ type Car struct { config // ID of the ent. ID int `json:"id,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the CarQuery when eager-loading is set. + Edges struct { + // Owner holds the value of the owner edge. + Owner *User + } + owner_id *int } // scanValues returns the types for scanning values from sql.Rows. func (*Car) scanValues() []interface{} { return []interface{}{ - &sql.NullInt64{}, + &sql.NullInt64{}, // id + } +} + +// fkValues returns the types for scanning foreign-keys values from sql.Rows. +func (*Car) fkValues() []interface{} { + return []interface{}{ + &sql.NullInt64{}, // owner_id } } // assignValues assigns the values that were returned from sql.Rows (after scanning) // to the Car fields. func (c *Car) assignValues(values ...interface{}) error { - if m, n := len(values), len(car.Columns); m != n { + if m, n := len(values), len(car.Columns); m < n { return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) } value, ok := values[0].(*sql.NullInt64) @@ -40,6 +54,15 @@ func (c *Car) assignValues(values ...interface{}) error { } c.ID = int(value.Int64) values = values[1:] + values = values[0:] + if len(values) == len(car.ForeignKeys) { + if value, ok := values[0].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for edge-field owner_id", value) + } else if value.Valid { + c.owner_id = new(int) + *c.owner_id = int(value.Int64) + } + } return nil } diff --git a/entc/integration/migrate/entv1/car/car.go b/entc/integration/migrate/entv1/car/car.go index 9738845d9..60449402e 100644 --- a/entc/integration/migrate/entv1/car/car.go +++ b/entc/integration/migrate/entv1/car/car.go @@ -23,7 +23,12 @@ const ( OwnerColumn = "owner_id" ) -// Columns holds all SQL columns are car fields. +// Columns holds all SQL columns for car fields. var Columns = []string{ FieldID, } + +// ForeignKeys holds the SQL foreign-keys that are owned by the Car type. +var ForeignKeys = []string{ + "owner_id", +} diff --git a/entc/integration/migrate/entv1/car_query.go b/entc/integration/migrate/entv1/car_query.go index 932a0a95c..69d93b068 100644 --- a/entc/integration/migrate/entv1/car_query.go +++ b/entc/integration/migrate/entv1/car_query.go @@ -28,6 +28,9 @@ type CarQuery struct { order []Order unique []string predicates []predicate.Car + // eager-loading edges. + withOwner *UserQuery + withFKs bool // intermediate query. sql *sql.Selector } @@ -237,6 +240,17 @@ func (cq *CarQuery) Clone() *CarQuery { } } +// WithOwner tells the query-builder to eager-loads the nodes that are connected to +// the "owner" edge. The optional arguments used to configure the query builder of the edge. +func (cq *CarQuery) WithOwner(opts ...func(*UserQuery)) *CarQuery { + query := &UserQuery{config: cq.config} + for _, opt := range opts { + opt(query) + } + cq.withOwner = query + return cq +} + // GroupBy used to group vertices by one or more fields/columns. // It is often used with aggregate functions, like: count, max, mean, min, sum. func (cq *CarQuery) GroupBy(field string, fields ...string) *CarGroupBy { @@ -256,13 +270,24 @@ func (cq *CarQuery) Select(field string, fields ...string) *CarSelect { func (cq *CarQuery) sqlAll(ctx context.Context) ([]*Car, error) { var ( - nodes []*Car - spec = cq.querySpec() + nodes []*Car + withFKs = cq.withFKs + spec = cq.querySpec() ) + if cq.withOwner != nil { + withFKs = true + } + if withFKs { + spec.Node.Columns = append(spec.Node.Columns, car.ForeignKeys...) + } spec.ScanValues = func() []interface{} { node := &Car{config: cq.config} nodes = append(nodes, node) - return node.scanValues() + values := node.scanValues() + if withFKs { + values = append(values, node.fkValues()...) + } + return values } spec.Assign = func(values ...interface{}) error { if len(nodes) == 0 { @@ -274,6 +299,32 @@ func (cq *CarQuery) sqlAll(ctx context.Context) ([]*Car, error) { if err := sqlgraph.QueryNodes(ctx, cq.driver, spec); err != nil { return nil, err } + + if query := cq.withOwner; query != nil { + ids := make([]int, 0, len(nodes)) + nodeids := make(map[int][]*Car) + for i := range nodes { + if fk := nodes[i].owner_id; fk != nil { + ids = append(ids, *fk) + nodeids[*fk] = append(nodeids[*fk], nodes[i]) + } + } + query.Where(user.IDIn(ids...)) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + nodes, ok := nodeids[n.ID] + if !ok { + return nil, fmt.Errorf(`unexpected foreign-key "owner_id" returned %v`, n.ID) + } + for i := range nodes { + nodes[i].Edges.Owner = n + } + } + } + return nodes, nil } diff --git a/entc/integration/migrate/entv1/user.go b/entc/integration/migrate/entv1/user.go index fb9676aae..276b23ee9 100644 --- a/entc/integration/migrate/entv1/user.go +++ b/entc/integration/migrate/entv1/user.go @@ -33,26 +33,48 @@ type User struct { Blob []byte `json:"blob,omitempty"` // State holds the value of the "state" field. State user.State `json:"state,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the UserQuery when eager-loading is set. + Edges struct { + // Parent holds the value of the parent edge. + Parent *User + // Children holds the value of the children edge. + Children []*User + // Spouse holds the value of the spouse edge. + Spouse *User + // Car holds the value of the car edge. + Car *Car + } + parent_id *int + user_spouse_id *int } // scanValues returns the types for scanning values from sql.Rows. func (*User) scanValues() []interface{} { return []interface{}{ - &sql.NullInt64{}, - &sql.NullInt64{}, - &sql.NullString{}, - &sql.NullString{}, - &sql.NullString{}, - &sql.NullString{}, - &[]byte{}, - &sql.NullString{}, + &sql.NullInt64{}, // id + &sql.NullInt64{}, // age + &sql.NullString{}, // name + &sql.NullString{}, // nickname + &sql.NullString{}, // address + &sql.NullString{}, // renamed + &[]byte{}, // blob + &sql.NullString{}, // state + } +} + +// fkValues returns the types for scanning foreign-keys values from sql.Rows. +func (*User) fkValues() []interface{} { + return []interface{}{ + &sql.NullInt64{}, // parent_id + &sql.NullInt64{}, // user_spouse_id } } // assignValues assigns the values that were returned from sql.Rows (after scanning) // to the User fields. func (u *User) assignValues(values ...interface{}) error { - if m, n := len(values), len(user.Columns); m != n { + if m, n := len(values), len(user.Columns); m < n { return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) } value, ok := values[0].(*sql.NullInt64) @@ -96,6 +118,21 @@ func (u *User) assignValues(values ...interface{}) error { } else if value.Valid { u.State = user.State(value.String) } + values = values[7:] + if len(values) == len(user.ForeignKeys) { + if value, ok := values[0].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for edge-field parent_id", value) + } else if value.Valid { + u.parent_id = new(int) + *u.parent_id = int(value.Int64) + } + if value, ok := values[1].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for edge-field user_spouse_id", value) + } else if value.Valid { + u.user_spouse_id = new(int) + *u.user_spouse_id = int(value.Int64) + } + } return nil } diff --git a/entc/integration/migrate/entv1/user/user.go b/entc/integration/migrate/entv1/user/user.go index 5d0fbfd4f..0c1a5a120 100644 --- a/entc/integration/migrate/entv1/user/user.go +++ b/entc/integration/migrate/entv1/user/user.go @@ -55,7 +55,7 @@ const ( CarColumn = "owner_id" ) -// Columns holds all SQL columns are user fields. +// Columns holds all SQL columns for user fields. var Columns = []string{ FieldID, FieldAge, @@ -67,6 +67,12 @@ var Columns = []string{ FieldState, } +// ForeignKeys holds the SQL foreign-keys that are owned by the User type. +var ForeignKeys = []string{ + "parent_id", + "user_spouse_id", +} + var ( fields = schema.User{}.Fields() diff --git a/entc/integration/migrate/entv1/user_query.go b/entc/integration/migrate/entv1/user_query.go index f75f278d8..54c6539a8 100644 --- a/entc/integration/migrate/entv1/user_query.go +++ b/entc/integration/migrate/entv1/user_query.go @@ -8,6 +8,7 @@ package entv1 import ( "context" + "database/sql/driver" "errors" "fmt" "math" @@ -28,6 +29,12 @@ type UserQuery struct { order []Order unique []string predicates []predicate.User + // eager-loading edges. + withParent *UserQuery + withChildren *UserQuery + withSpouse *UserQuery + withCar *CarQuery + withFKs bool // intermediate query. sql *sql.Selector } @@ -273,6 +280,50 @@ func (uq *UserQuery) Clone() *UserQuery { } } +// WithParent tells the query-builder to eager-loads the nodes that are connected to +// the "parent" edge. The optional arguments used to configure the query builder of the edge. +func (uq *UserQuery) WithParent(opts ...func(*UserQuery)) *UserQuery { + query := &UserQuery{config: uq.config} + for _, opt := range opts { + opt(query) + } + uq.withParent = query + return uq +} + +// WithChildren tells the query-builder to eager-loads the nodes that are connected to +// the "children" edge. The optional arguments used to configure the query builder of the edge. +func (uq *UserQuery) WithChildren(opts ...func(*UserQuery)) *UserQuery { + query := &UserQuery{config: uq.config} + for _, opt := range opts { + opt(query) + } + uq.withChildren = query + return uq +} + +// WithSpouse tells the query-builder to eager-loads the nodes that are connected to +// the "spouse" edge. The optional arguments used to configure the query builder of the edge. +func (uq *UserQuery) WithSpouse(opts ...func(*UserQuery)) *UserQuery { + query := &UserQuery{config: uq.config} + for _, opt := range opts { + opt(query) + } + uq.withSpouse = query + return uq +} + +// WithCar tells the query-builder to eager-loads the nodes that are connected to +// the "car" edge. The optional arguments used to configure the query builder of the edge. +func (uq *UserQuery) WithCar(opts ...func(*CarQuery)) *UserQuery { + query := &CarQuery{config: uq.config} + for _, opt := range opts { + opt(query) + } + uq.withCar = query + return uq +} + // GroupBy used to group vertices by one or more fields/columns. // It is often used with aggregate functions, like: count, max, mean, min, sum. // @@ -316,13 +367,24 @@ func (uq *UserQuery) Select(field string, fields ...string) *UserSelect { func (uq *UserQuery) sqlAll(ctx context.Context) ([]*User, error) { var ( - nodes []*User - spec = uq.querySpec() + nodes []*User + withFKs = uq.withFKs + spec = uq.querySpec() ) + if uq.withParent != nil || uq.withSpouse != nil { + withFKs = true + } + if withFKs { + spec.Node.Columns = append(spec.Node.Columns, user.ForeignKeys...) + } spec.ScanValues = func() []interface{} { node := &User{config: uq.config} nodes = append(nodes, node) - return node.scanValues() + values := node.scanValues() + if withFKs { + values = append(values, node.fkValues()...) + } + return values } spec.Assign = func(values ...interface{}) error { if len(nodes) == 0 { @@ -334,6 +396,113 @@ func (uq *UserQuery) sqlAll(ctx context.Context) ([]*User, error) { if err := sqlgraph.QueryNodes(ctx, uq.driver, spec); err != nil { return nil, err } + + if query := uq.withParent; query != nil { + ids := make([]int, 0, len(nodes)) + nodeids := make(map[int][]*User) + for i := range nodes { + if fk := nodes[i].parent_id; fk != nil { + ids = append(ids, *fk) + nodeids[*fk] = append(nodeids[*fk], nodes[i]) + } + } + query.Where(user.IDIn(ids...)) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + nodes, ok := nodeids[n.ID] + if !ok { + return nil, fmt.Errorf(`unexpected foreign-key "parent_id" returned %v`, n.ID) + } + for i := range nodes { + nodes[i].Edges.Parent = n + } + } + } + + if query := uq.withChildren; query != nil { + fks := make([]driver.Value, 0, len(nodes)) + nodeids := make(map[int]*User) + for i := range nodes { + fks = append(fks, nodes[i].ID) + nodeids[nodes[i].ID] = nodes[i] + } + query.withFKs = true + query.Where(predicate.User(func(s *sql.Selector) { + s.Where(sql.InValues(user.ChildrenColumn, fks...)) + })) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + fk := n.parent_id + if fk == nil { + return nil, fmt.Errorf(`foreign-key "parent_id" is nil for node %v`, n.ID) + } + node, ok := nodeids[*fk] + if !ok { + return nil, fmt.Errorf(`unexpected foreign-key "parent_id" returned %v for node %v`, *fk, n.ID) + } + node.Edges.Children = append(node.Edges.Children, n) + } + } + + if query := uq.withSpouse; query != nil { + ids := make([]int, 0, len(nodes)) + nodeids := make(map[int][]*User) + for i := range nodes { + if fk := nodes[i].user_spouse_id; fk != nil { + ids = append(ids, *fk) + nodeids[*fk] = append(nodeids[*fk], nodes[i]) + } + } + query.Where(user.IDIn(ids...)) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + nodes, ok := nodeids[n.ID] + if !ok { + return nil, fmt.Errorf(`unexpected foreign-key "user_spouse_id" returned %v`, n.ID) + } + for i := range nodes { + nodes[i].Edges.Spouse = n + } + } + } + + if query := uq.withCar; query != nil { + fks := make([]driver.Value, 0, len(nodes)) + nodeids := make(map[int]*User) + for i := range nodes { + fks = append(fks, nodes[i].ID) + nodeids[nodes[i].ID] = nodes[i] + } + query.withFKs = true + query.Where(predicate.Car(func(s *sql.Selector) { + s.Where(sql.InValues(user.CarColumn, fks...)) + })) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + fk := n.owner_id + if fk == nil { + return nil, fmt.Errorf(`foreign-key "owner_id" is nil for node %v`, n.ID) + } + node, ok := nodeids[*fk] + if !ok { + return nil, fmt.Errorf(`unexpected foreign-key "owner_id" returned %v for node %v`, *fk, n.ID) + } + node.Edges.Car = n + } + } + return nodes, nil } diff --git a/entc/integration/migrate/entv2/car.go b/entc/integration/migrate/entv2/car.go index 68036d840..64a4a7126 100644 --- a/entc/integration/migrate/entv2/car.go +++ b/entc/integration/migrate/entv2/car.go @@ -19,19 +19,33 @@ type Car struct { config // ID of the ent. ID int `json:"id,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the CarQuery when eager-loading is set. + Edges struct { + // Owner holds the value of the owner edge. + Owner *User + } + owner_id *int } // scanValues returns the types for scanning values from sql.Rows. func (*Car) scanValues() []interface{} { return []interface{}{ - &sql.NullInt64{}, + &sql.NullInt64{}, // id + } +} + +// fkValues returns the types for scanning foreign-keys values from sql.Rows. +func (*Car) fkValues() []interface{} { + return []interface{}{ + &sql.NullInt64{}, // owner_id } } // assignValues assigns the values that were returned from sql.Rows (after scanning) // to the Car fields. func (c *Car) assignValues(values ...interface{}) error { - if m, n := len(values), len(car.Columns); m != n { + if m, n := len(values), len(car.Columns); m < n { return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) } value, ok := values[0].(*sql.NullInt64) @@ -40,6 +54,15 @@ func (c *Car) assignValues(values ...interface{}) error { } c.ID = int(value.Int64) values = values[1:] + values = values[0:] + if len(values) == len(car.ForeignKeys) { + if value, ok := values[0].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for edge-field owner_id", value) + } else if value.Valid { + c.owner_id = new(int) + *c.owner_id = int(value.Int64) + } + } return nil } diff --git a/entc/integration/migrate/entv2/car/car.go b/entc/integration/migrate/entv2/car/car.go index 9738845d9..60449402e 100644 --- a/entc/integration/migrate/entv2/car/car.go +++ b/entc/integration/migrate/entv2/car/car.go @@ -23,7 +23,12 @@ const ( OwnerColumn = "owner_id" ) -// Columns holds all SQL columns are car fields. +// Columns holds all SQL columns for car fields. var Columns = []string{ FieldID, } + +// ForeignKeys holds the SQL foreign-keys that are owned by the Car type. +var ForeignKeys = []string{ + "owner_id", +} diff --git a/entc/integration/migrate/entv2/car_query.go b/entc/integration/migrate/entv2/car_query.go index 6f291b16d..4939f9559 100644 --- a/entc/integration/migrate/entv2/car_query.go +++ b/entc/integration/migrate/entv2/car_query.go @@ -28,6 +28,9 @@ type CarQuery struct { order []Order unique []string predicates []predicate.Car + // eager-loading edges. + withOwner *UserQuery + withFKs bool // intermediate query. sql *sql.Selector } @@ -237,6 +240,17 @@ func (cq *CarQuery) Clone() *CarQuery { } } +// WithOwner tells the query-builder to eager-loads the nodes that are connected to +// the "owner" edge. The optional arguments used to configure the query builder of the edge. +func (cq *CarQuery) WithOwner(opts ...func(*UserQuery)) *CarQuery { + query := &UserQuery{config: cq.config} + for _, opt := range opts { + opt(query) + } + cq.withOwner = query + return cq +} + // GroupBy used to group vertices by one or more fields/columns. // It is often used with aggregate functions, like: count, max, mean, min, sum. func (cq *CarQuery) GroupBy(field string, fields ...string) *CarGroupBy { @@ -256,13 +270,24 @@ func (cq *CarQuery) Select(field string, fields ...string) *CarSelect { func (cq *CarQuery) sqlAll(ctx context.Context) ([]*Car, error) { var ( - nodes []*Car - spec = cq.querySpec() + nodes []*Car + withFKs = cq.withFKs + spec = cq.querySpec() ) + if cq.withOwner != nil { + withFKs = true + } + if withFKs { + spec.Node.Columns = append(spec.Node.Columns, car.ForeignKeys...) + } spec.ScanValues = func() []interface{} { node := &Car{config: cq.config} nodes = append(nodes, node) - return node.scanValues() + values := node.scanValues() + if withFKs { + values = append(values, node.fkValues()...) + } + return values } spec.Assign = func(values ...interface{}) error { if len(nodes) == 0 { @@ -274,6 +299,32 @@ func (cq *CarQuery) sqlAll(ctx context.Context) ([]*Car, error) { if err := sqlgraph.QueryNodes(ctx, cq.driver, spec); err != nil { return nil, err } + + if query := cq.withOwner; query != nil { + ids := make([]int, 0, len(nodes)) + nodeids := make(map[int][]*Car) + for i := range nodes { + if fk := nodes[i].owner_id; fk != nil { + ids = append(ids, *fk) + nodeids[*fk] = append(nodeids[*fk], nodes[i]) + } + } + query.Where(user.IDIn(ids...)) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + nodes, ok := nodeids[n.ID] + if !ok { + return nil, fmt.Errorf(`unexpected foreign-key "owner_id" returned %v`, n.ID) + } + for i := range nodes { + nodes[i].Edges.Owner = n + } + } + } + return nodes, nil } diff --git a/entc/integration/migrate/entv2/group.go b/entc/integration/migrate/entv2/group.go index 45755408a..2e9164aa2 100644 --- a/entc/integration/migrate/entv2/group.go +++ b/entc/integration/migrate/entv2/group.go @@ -24,14 +24,14 @@ type Group struct { // scanValues returns the types for scanning values from sql.Rows. func (*Group) scanValues() []interface{} { return []interface{}{ - &sql.NullInt64{}, + &sql.NullInt64{}, // id } } // assignValues assigns the values that were returned from sql.Rows (after scanning) // to the Group fields. func (gr *Group) assignValues(values ...interface{}) error { - if m, n := len(values), len(group.Columns); m != n { + if m, n := len(values), len(group.Columns); m < n { return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) } value, ok := values[0].(*sql.NullInt64) diff --git a/entc/integration/migrate/entv2/group/group.go b/entc/integration/migrate/entv2/group/group.go index 9f4a1dea6..93762821b 100644 --- a/entc/integration/migrate/entv2/group/group.go +++ b/entc/integration/migrate/entv2/group/group.go @@ -16,7 +16,7 @@ const ( Table = "groups" ) -// Columns holds all SQL columns are group fields. +// Columns holds all SQL columns for group fields. var Columns = []string{ FieldID, } diff --git a/entc/integration/migrate/entv2/group_query.go b/entc/integration/migrate/entv2/group_query.go index 9357e0543..76b863450 100644 --- a/entc/integration/migrate/entv2/group_query.go +++ b/entc/integration/migrate/entv2/group_query.go @@ -249,7 +249,8 @@ func (gq *GroupQuery) sqlAll(ctx context.Context) ([]*Group, error) { spec.ScanValues = func() []interface{} { node := &Group{config: gq.config} nodes = append(nodes, node) - return node.scanValues() + values := node.scanValues() + return values } spec.Assign = func(values ...interface{}) error { if len(nodes) == 0 { diff --git a/entc/integration/migrate/entv2/pet.go b/entc/integration/migrate/entv2/pet.go index b1bb4eaa8..fb0aa0fd2 100644 --- a/entc/integration/migrate/entv2/pet.go +++ b/entc/integration/migrate/entv2/pet.go @@ -24,14 +24,14 @@ type Pet struct { // scanValues returns the types for scanning values from sql.Rows. func (*Pet) scanValues() []interface{} { return []interface{}{ - &sql.NullInt64{}, + &sql.NullInt64{}, // id } } // assignValues assigns the values that were returned from sql.Rows (after scanning) // to the Pet fields. func (pe *Pet) assignValues(values ...interface{}) error { - if m, n := len(values), len(pet.Columns); m != n { + if m, n := len(values), len(pet.Columns); m < n { return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) } value, ok := values[0].(*sql.NullInt64) diff --git a/entc/integration/migrate/entv2/pet/pet.go b/entc/integration/migrate/entv2/pet/pet.go index ae617912e..d63ea09ec 100644 --- a/entc/integration/migrate/entv2/pet/pet.go +++ b/entc/integration/migrate/entv2/pet/pet.go @@ -16,7 +16,7 @@ const ( Table = "pets" ) -// Columns holds all SQL columns are pet fields. +// Columns holds all SQL columns for pet fields. var Columns = []string{ FieldID, } diff --git a/entc/integration/migrate/entv2/pet_query.go b/entc/integration/migrate/entv2/pet_query.go index d64156962..2c585609d 100644 --- a/entc/integration/migrate/entv2/pet_query.go +++ b/entc/integration/migrate/entv2/pet_query.go @@ -249,7 +249,8 @@ func (pq *PetQuery) sqlAll(ctx context.Context) ([]*Pet, error) { spec.ScanValues = func() []interface{} { node := &Pet{config: pq.config} nodes = append(nodes, node) - return node.scanValues() + values := node.scanValues() + return values } spec.Assign = func(values ...interface{}) error { if len(nodes) == 0 { diff --git a/entc/integration/migrate/entv2/user.go b/entc/integration/migrate/entv2/user.go index 387d50b24..0ac7201b5 100644 --- a/entc/integration/migrate/entv2/user.go +++ b/entc/integration/migrate/entv2/user.go @@ -37,28 +37,34 @@ type User struct { Blob []byte `json:"blob,omitempty"` // State holds the value of the "state" field. State user.State `json:"state,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the UserQuery when eager-loading is set. + Edges struct { + // Car holds the value of the car edge. + Car []*Car + } } // scanValues returns the types for scanning values from sql.Rows. func (*User) scanValues() []interface{} { return []interface{}{ - &sql.NullInt64{}, - &sql.NullInt64{}, - &sql.NullString{}, - &sql.NullString{}, - &sql.NullString{}, - &[]byte{}, - &sql.NullString{}, - &sql.NullString{}, - &[]byte{}, - &sql.NullString{}, + &sql.NullInt64{}, // id + &sql.NullInt64{}, // age + &sql.NullString{}, // name + &sql.NullString{}, // nickname + &sql.NullString{}, // phone + &[]byte{}, // buffer + &sql.NullString{}, // title + &sql.NullString{}, // new_name + &[]byte{}, // blob + &sql.NullString{}, // state } } // assignValues assigns the values that were returned from sql.Rows (after scanning) // to the User fields. func (u *User) assignValues(values ...interface{}) error { - if m, n := len(values), len(user.Columns); m != n { + if m, n := len(values), len(user.Columns); m < n { return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) } value, ok := values[0].(*sql.NullInt64) diff --git a/entc/integration/migrate/entv2/user/user.go b/entc/integration/migrate/entv2/user/user.go index 29cc2c585..ec399a007 100644 --- a/entc/integration/migrate/entv2/user/user.go +++ b/entc/integration/migrate/entv2/user/user.go @@ -47,7 +47,7 @@ const ( CarColumn = "owner_id" ) -// Columns holds all SQL columns are user fields. +// Columns holds all SQL columns for user fields. var Columns = []string{ FieldID, FieldAge, diff --git a/entc/integration/migrate/entv2/user_query.go b/entc/integration/migrate/entv2/user_query.go index 886d1bfed..24e1a0ac9 100644 --- a/entc/integration/migrate/entv2/user_query.go +++ b/entc/integration/migrate/entv2/user_query.go @@ -8,6 +8,7 @@ package entv2 import ( "context" + "database/sql/driver" "errors" "fmt" "math" @@ -28,6 +29,8 @@ type UserQuery struct { order []Order unique []string predicates []predicate.User + // eager-loading edges. + withCar *CarQuery // intermediate query. sql *sql.Selector } @@ -237,6 +240,17 @@ func (uq *UserQuery) Clone() *UserQuery { } } +// WithCar tells the query-builder to eager-loads the nodes that are connected to +// the "car" edge. The optional arguments used to configure the query builder of the edge. +func (uq *UserQuery) WithCar(opts ...func(*CarQuery)) *UserQuery { + query := &CarQuery{config: uq.config} + for _, opt := range opts { + opt(query) + } + uq.withCar = query + return uq +} + // GroupBy used to group vertices by one or more fields/columns. // It is often used with aggregate functions, like: count, max, mean, min, sum. // @@ -286,7 +300,8 @@ func (uq *UserQuery) sqlAll(ctx context.Context) ([]*User, error) { spec.ScanValues = func() []interface{} { node := &User{config: uq.config} nodes = append(nodes, node) - return node.scanValues() + values := node.scanValues() + return values } spec.Assign = func(values ...interface{}) error { if len(nodes) == 0 { @@ -298,6 +313,35 @@ func (uq *UserQuery) sqlAll(ctx context.Context) ([]*User, error) { if err := sqlgraph.QueryNodes(ctx, uq.driver, spec); err != nil { return nil, err } + + if query := uq.withCar; query != nil { + fks := make([]driver.Value, 0, len(nodes)) + nodeids := make(map[int]*User) + for i := range nodes { + fks = append(fks, nodes[i].ID) + nodeids[nodes[i].ID] = nodes[i] + } + query.withFKs = true + query.Where(predicate.Car(func(s *sql.Selector) { + s.Where(sql.InValues(user.CarColumn, fks...)) + })) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + fk := n.owner_id + if fk == nil { + return nil, fmt.Errorf(`foreign-key "owner_id" is nil for node %v`, n.ID) + } + node, ok := nodeids[*fk] + if !ok { + return nil, fmt.Errorf(`unexpected foreign-key "owner_id" returned %v for node %v`, *fk, n.ID) + } + node.Edges.Car = append(node.Edges.Car, n) + } + } + return nodes, nil } diff --git a/entc/integration/template/ent/group.go b/entc/integration/template/ent/group.go index 3eca2a2a2..6f184d294 100644 --- a/entc/integration/template/ent/group.go +++ b/entc/integration/template/ent/group.go @@ -26,15 +26,15 @@ type Group struct { // scanValues returns the types for scanning values from sql.Rows. func (*Group) scanValues() []interface{} { return []interface{}{ - &sql.NullInt64{}, - &sql.NullInt64{}, + &sql.NullInt64{}, // id + &sql.NullInt64{}, // max_users } } // assignValues assigns the values that were returned from sql.Rows (after scanning) // to the Group fields. func (gr *Group) assignValues(values ...interface{}) error { - if m, n := len(values), len(group.Columns); m != n { + if m, n := len(values), len(group.Columns); m < n { return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) } value, ok := values[0].(*sql.NullInt64) diff --git a/entc/integration/template/ent/group/group.go b/entc/integration/template/ent/group/group.go index f592b416d..60b698a55 100644 --- a/entc/integration/template/ent/group/group.go +++ b/entc/integration/template/ent/group/group.go @@ -18,7 +18,7 @@ const ( Table = "groups" ) -// Columns holds all SQL columns are group fields. +// Columns holds all SQL columns for group fields. var Columns = []string{ FieldID, FieldMaxUsers, diff --git a/entc/integration/template/ent/group_query.go b/entc/integration/template/ent/group_query.go index ae8110699..479d2b9bc 100644 --- a/entc/integration/template/ent/group_query.go +++ b/entc/integration/template/ent/group_query.go @@ -273,7 +273,8 @@ func (gq *GroupQuery) sqlAll(ctx context.Context) ([]*Group, error) { spec.ScanValues = func() []interface{} { node := &Group{config: gq.config} nodes = append(nodes, node) - return node.scanValues() + values := node.scanValues() + return values } spec.Assign = func(values ...interface{}) error { if len(nodes) == 0 { diff --git a/entc/integration/template/ent/pet.go b/entc/integration/template/ent/pet.go index 87d123330..962812491 100644 --- a/entc/integration/template/ent/pet.go +++ b/entc/integration/template/ent/pet.go @@ -24,21 +24,35 @@ type Pet struct { Age int `json:"age,omitempty"` // LicensedAt holds the value of the "licensed_at" field. LicensedAt *time.Time `json:"licensed_at,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the PetQuery when eager-loading is set. + Edges struct { + // Owner holds the value of the owner edge. + Owner *User + } + owner_id *int } // scanValues returns the types for scanning values from sql.Rows. func (*Pet) scanValues() []interface{} { return []interface{}{ - &sql.NullInt64{}, - &sql.NullInt64{}, - &sql.NullTime{}, + &sql.NullInt64{}, // id + &sql.NullInt64{}, // age + &sql.NullTime{}, // licensed_at + } +} + +// fkValues returns the types for scanning foreign-keys values from sql.Rows. +func (*Pet) fkValues() []interface{} { + return []interface{}{ + &sql.NullInt64{}, // owner_id } } // assignValues assigns the values that were returned from sql.Rows (after scanning) // to the Pet fields. func (pe *Pet) assignValues(values ...interface{}) error { - if m, n := len(values), len(pet.Columns); m != n { + if m, n := len(values), len(pet.Columns); m < n { return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) } value, ok := values[0].(*sql.NullInt64) @@ -58,6 +72,15 @@ func (pe *Pet) assignValues(values ...interface{}) error { pe.LicensedAt = new(time.Time) *pe.LicensedAt = value.Time } + values = values[2:] + if len(values) == len(pet.ForeignKeys) { + if value, ok := values[0].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for edge-field owner_id", value) + } else if value.Valid { + pe.owner_id = new(int) + *pe.owner_id = int(value.Int64) + } + } return nil } diff --git a/entc/integration/template/ent/pet/pet.go b/entc/integration/template/ent/pet/pet.go index 1ed71793b..37d0eed8f 100644 --- a/entc/integration/template/ent/pet/pet.go +++ b/entc/integration/template/ent/pet/pet.go @@ -27,9 +27,14 @@ const ( OwnerColumn = "owner_id" ) -// Columns holds all SQL columns are pet fields. +// Columns holds all SQL columns for pet fields. var Columns = []string{ FieldID, FieldAge, FieldLicensedAt, } + +// ForeignKeys holds the SQL foreign-keys that are owned by the Pet type. +var ForeignKeys = []string{ + "owner_id", +} diff --git a/entc/integration/template/ent/pet_query.go b/entc/integration/template/ent/pet_query.go index 36fb67847..0f477d999 100644 --- a/entc/integration/template/ent/pet_query.go +++ b/entc/integration/template/ent/pet_query.go @@ -28,6 +28,9 @@ type PetQuery struct { order []Order unique []string predicates []predicate.Pet + // eager-loading edges. + withOwner *UserQuery + withFKs bool // intermediate query. sql *sql.Selector } @@ -237,6 +240,17 @@ func (pq *PetQuery) Clone() *PetQuery { } } +// WithOwner tells the query-builder to eager-loads the nodes that are connected to +// the "owner" edge. The optional arguments used to configure the query builder of the edge. +func (pq *PetQuery) WithOwner(opts ...func(*UserQuery)) *PetQuery { + query := &UserQuery{config: pq.config} + for _, opt := range opts { + opt(query) + } + pq.withOwner = query + return pq +} + // GroupBy used to group vertices by one or more fields/columns. // It is often used with aggregate functions, like: count, max, mean, min, sum. // @@ -280,13 +294,24 @@ func (pq *PetQuery) Select(field string, fields ...string) *PetSelect { func (pq *PetQuery) sqlAll(ctx context.Context) ([]*Pet, error) { var ( - nodes []*Pet - spec = pq.querySpec() + nodes []*Pet + withFKs = pq.withFKs + spec = pq.querySpec() ) + if pq.withOwner != nil { + withFKs = true + } + if withFKs { + spec.Node.Columns = append(spec.Node.Columns, pet.ForeignKeys...) + } spec.ScanValues = func() []interface{} { node := &Pet{config: pq.config} nodes = append(nodes, node) - return node.scanValues() + values := node.scanValues() + if withFKs { + values = append(values, node.fkValues()...) + } + return values } spec.Assign = func(values ...interface{}) error { if len(nodes) == 0 { @@ -298,6 +323,32 @@ func (pq *PetQuery) sqlAll(ctx context.Context) ([]*Pet, error) { if err := sqlgraph.QueryNodes(ctx, pq.driver, spec); err != nil { return nil, err } + + if query := pq.withOwner; query != nil { + ids := make([]int, 0, len(nodes)) + nodeids := make(map[int][]*Pet) + for i := range nodes { + if fk := nodes[i].owner_id; fk != nil { + ids = append(ids, *fk) + nodeids[*fk] = append(nodeids[*fk], nodes[i]) + } + } + query.Where(user.IDIn(ids...)) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + nodes, ok := nodeids[n.ID] + if !ok { + return nil, fmt.Errorf(`unexpected foreign-key "owner_id" returned %v`, n.ID) + } + for i := range nodes { + nodes[i].Edges.Owner = n + } + } + } + return nodes, nil } diff --git a/entc/integration/template/ent/user.go b/entc/integration/template/ent/user.go index ef4c20058..73d02d971 100644 --- a/entc/integration/template/ent/user.go +++ b/entc/integration/template/ent/user.go @@ -21,20 +21,28 @@ type User struct { ID int `json:"id,omitempty"` // Name holds the value of the "name" field. Name string `json:"name,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the UserQuery when eager-loading is set. + Edges struct { + // Pets holds the value of the pets edge. + Pets []*Pet + // Friends holds the value of the friends edge. + Friends []*User + } } // scanValues returns the types for scanning values from sql.Rows. func (*User) scanValues() []interface{} { return []interface{}{ - &sql.NullInt64{}, - &sql.NullString{}, + &sql.NullInt64{}, // id + &sql.NullString{}, // name } } // assignValues assigns the values that were returned from sql.Rows (after scanning) // to the User fields. func (u *User) assignValues(values ...interface{}) error { - if m, n := len(values), len(user.Columns); m != n { + if m, n := len(values), len(user.Columns); m < n { return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) } value, ok := values[0].(*sql.NullInt64) diff --git a/entc/integration/template/ent/user/user.go b/entc/integration/template/ent/user/user.go index 46d24e532..0eeebbca5 100644 --- a/entc/integration/template/ent/user/user.go +++ b/entc/integration/template/ent/user/user.go @@ -27,7 +27,7 @@ const ( FriendsTable = "user_friends" ) -// Columns holds all SQL columns are user fields. +// Columns holds all SQL columns for user fields. var Columns = []string{ FieldID, FieldName, diff --git a/entc/integration/template/ent/user_query.go b/entc/integration/template/ent/user_query.go index 552389d6a..e835f80f0 100644 --- a/entc/integration/template/ent/user_query.go +++ b/entc/integration/template/ent/user_query.go @@ -8,6 +8,7 @@ package ent import ( "context" + "database/sql/driver" "errors" "fmt" "math" @@ -28,6 +29,9 @@ type UserQuery struct { order []Order unique []string predicates []predicate.User + // eager-loading edges. + withPets *PetQuery + withFriends *UserQuery // intermediate query. sql *sql.Selector } @@ -249,6 +253,28 @@ func (uq *UserQuery) Clone() *UserQuery { } } +// WithPets tells the query-builder to eager-loads the nodes that are connected to +// the "pets" edge. The optional arguments used to configure the query builder of the edge. +func (uq *UserQuery) WithPets(opts ...func(*PetQuery)) *UserQuery { + query := &PetQuery{config: uq.config} + for _, opt := range opts { + opt(query) + } + uq.withPets = query + return uq +} + +// WithFriends tells the query-builder to eager-loads the nodes that are connected to +// the "friends" edge. The optional arguments used to configure the query builder of the edge. +func (uq *UserQuery) WithFriends(opts ...func(*UserQuery)) *UserQuery { + query := &UserQuery{config: uq.config} + for _, opt := range opts { + opt(query) + } + uq.withFriends = query + return uq +} + // GroupBy used to group vertices by one or more fields/columns. // It is often used with aggregate functions, like: count, max, mean, min, sum. // @@ -298,7 +324,8 @@ func (uq *UserQuery) sqlAll(ctx context.Context) ([]*User, error) { spec.ScanValues = func() []interface{} { node := &User{config: uq.config} nodes = append(nodes, node) - return node.scanValues() + values := node.scanValues() + return values } spec.Assign = func(values ...interface{}) error { if len(nodes) == 0 { @@ -310,6 +337,98 @@ func (uq *UserQuery) sqlAll(ctx context.Context) ([]*User, error) { if err := sqlgraph.QueryNodes(ctx, uq.driver, spec); err != nil { return nil, err } + + if query := uq.withPets; query != nil { + fks := make([]driver.Value, 0, len(nodes)) + nodeids := make(map[int]*User) + for i := range nodes { + fks = append(fks, nodes[i].ID) + nodeids[nodes[i].ID] = nodes[i] + } + query.withFKs = true + query.Where(predicate.Pet(func(s *sql.Selector) { + s.Where(sql.InValues(user.PetsColumn, fks...)) + })) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + fk := n.owner_id + if fk == nil { + return nil, fmt.Errorf(`foreign-key "owner_id" is nil for node %v`, n.ID) + } + node, ok := nodeids[*fk] + if !ok { + return nil, fmt.Errorf(`unexpected foreign-key "owner_id" returned %v for node %v`, *fk, n.ID) + } + node.Edges.Pets = append(node.Edges.Pets, n) + } + } + + if query := uq.withFriends; query != nil { + fks := make([]driver.Value, 0, len(nodes)) + ids := make(map[int]*User, len(nodes)) + for _, node := range nodes { + ids[node.ID] = node + fks = append(fks, node.ID) + } + var ( + edgeids []int + edges = make(map[int][]*User) + ) + spec := &sqlgraph.EdgeQuerySpec{ + Edge: &sqlgraph.EdgeSpec{ + Inverse: false, + Table: user.FriendsTable, + Columns: user.FriendsPrimaryKey, + }, + Predicate: func(s *sql.Selector) { + s.Where(sql.InValues(user.FriendsPrimaryKey[0], fks...)) + }, + + ScanValues: func() [2]interface{} { + return [2]interface{}{&sql.NullInt64{}, &sql.NullInt64{}} + }, + Assign: func(out, in interface{}) error { + eout, ok := out.(*sql.NullInt64) + if !ok || eout == nil { + return fmt.Errorf("unexpected id value for edge-out") + } + ein, ok := in.(*sql.NullInt64) + if !ok || ein == nil { + return fmt.Errorf("unexpected id value for edge-in") + } + outValue := int(eout.Int64) + inValue := int(eout.Int64) + node, ok := ids[outValue] + if !ok { + return fmt.Errorf("unexpected node id in edges: %v", outValue) + } + edgeids = append(edgeids, inValue) + edges[inValue] = append(edges[inValue], node) + return nil + }, + } + if err := sqlgraph.QueryEdges(ctx, uq.driver, spec); err != nil { + return nil, fmt.Errorf(`query edges "friends": %v`, err) + } + query.Where(user.IDIn(edgeids...)) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + nodes, ok := edges[n.ID] + if !ok { + return nil, fmt.Errorf(`unexpected "friends" node returned %v`, n.ID) + } + for i := range nodes { + nodes[i].Edges.Friends = append(nodes[i].Edges.Friends, n) + } + } + } + return nodes, nil } diff --git a/examples/edgeindex/ent/city.go b/examples/edgeindex/ent/city.go index 08802fdf7..6ae0c8907 100644 --- a/examples/edgeindex/ent/city.go +++ b/examples/edgeindex/ent/city.go @@ -21,20 +21,26 @@ type City struct { ID int `json:"id,omitempty"` // Name holds the value of the "name" field. Name string `json:"name,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the CityQuery when eager-loading is set. + Edges struct { + // Streets holds the value of the streets edge. + Streets []*Street + } } // scanValues returns the types for scanning values from sql.Rows. func (*City) scanValues() []interface{} { return []interface{}{ - &sql.NullInt64{}, - &sql.NullString{}, + &sql.NullInt64{}, // id + &sql.NullString{}, // name } } // assignValues assigns the values that were returned from sql.Rows (after scanning) // to the City fields. func (c *City) assignValues(values ...interface{}) error { - if m, n := len(values), len(city.Columns); m != n { + if m, n := len(values), len(city.Columns); m < n { return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) } value, ok := values[0].(*sql.NullInt64) diff --git a/examples/edgeindex/ent/city/city.go b/examples/edgeindex/ent/city/city.go index 62021378c..c9a248c13 100644 --- a/examples/edgeindex/ent/city/city.go +++ b/examples/edgeindex/ent/city/city.go @@ -25,7 +25,7 @@ const ( StreetsColumn = "city_id" ) -// Columns holds all SQL columns are city fields. +// Columns holds all SQL columns for city fields. var Columns = []string{ FieldID, FieldName, diff --git a/examples/edgeindex/ent/city_query.go b/examples/edgeindex/ent/city_query.go index d37cd7973..892671146 100644 --- a/examples/edgeindex/ent/city_query.go +++ b/examples/edgeindex/ent/city_query.go @@ -8,6 +8,7 @@ package ent import ( "context" + "database/sql/driver" "errors" "fmt" "math" @@ -28,6 +29,8 @@ type CityQuery struct { order []Order unique []string predicates []predicate.City + // eager-loading edges. + withStreets *StreetQuery // intermediate query. sql *sql.Selector } @@ -237,6 +240,17 @@ func (cq *CityQuery) Clone() *CityQuery { } } +// WithStreets tells the query-builder to eager-loads the nodes that are connected to +// the "streets" edge. The optional arguments used to configure the query builder of the edge. +func (cq *CityQuery) WithStreets(opts ...func(*StreetQuery)) *CityQuery { + query := &StreetQuery{config: cq.config} + for _, opt := range opts { + opt(query) + } + cq.withStreets = query + return cq +} + // GroupBy used to group vertices by one or more fields/columns. // It is often used with aggregate functions, like: count, max, mean, min, sum. // @@ -286,7 +300,8 @@ func (cq *CityQuery) sqlAll(ctx context.Context) ([]*City, error) { spec.ScanValues = func() []interface{} { node := &City{config: cq.config} nodes = append(nodes, node) - return node.scanValues() + values := node.scanValues() + return values } spec.Assign = func(values ...interface{}) error { if len(nodes) == 0 { @@ -298,6 +313,35 @@ func (cq *CityQuery) sqlAll(ctx context.Context) ([]*City, error) { if err := sqlgraph.QueryNodes(ctx, cq.driver, spec); err != nil { return nil, err } + + if query := cq.withStreets; query != nil { + fks := make([]driver.Value, 0, len(nodes)) + nodeids := make(map[int]*City) + for i := range nodes { + fks = append(fks, nodes[i].ID) + nodeids[nodes[i].ID] = nodes[i] + } + query.withFKs = true + query.Where(predicate.Street(func(s *sql.Selector) { + s.Where(sql.InValues(city.StreetsColumn, fks...)) + })) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + fk := n.city_id + if fk == nil { + return nil, fmt.Errorf(`foreign-key "city_id" is nil for node %v`, n.ID) + } + node, ok := nodeids[*fk] + if !ok { + return nil, fmt.Errorf(`unexpected foreign-key "city_id" returned %v for node %v`, *fk, n.ID) + } + node.Edges.Streets = append(node.Edges.Streets, n) + } + } + return nodes, nil } diff --git a/examples/edgeindex/ent/street.go b/examples/edgeindex/ent/street.go index 2221807be..c8e81bd45 100644 --- a/examples/edgeindex/ent/street.go +++ b/examples/edgeindex/ent/street.go @@ -21,20 +21,34 @@ type Street struct { ID int `json:"id,omitempty"` // Name holds the value of the "name" field. Name string `json:"name,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the StreetQuery when eager-loading is set. + Edges struct { + // City holds the value of the city edge. + City *City + } + city_id *int } // scanValues returns the types for scanning values from sql.Rows. func (*Street) scanValues() []interface{} { return []interface{}{ - &sql.NullInt64{}, - &sql.NullString{}, + &sql.NullInt64{}, // id + &sql.NullString{}, // name + } +} + +// fkValues returns the types for scanning foreign-keys values from sql.Rows. +func (*Street) fkValues() []interface{} { + return []interface{}{ + &sql.NullInt64{}, // city_id } } // assignValues assigns the values that were returned from sql.Rows (after scanning) // to the Street fields. func (s *Street) assignValues(values ...interface{}) error { - if m, n := len(values), len(street.Columns); m != n { + if m, n := len(values), len(street.Columns); m < n { return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) } value, ok := values[0].(*sql.NullInt64) @@ -48,6 +62,15 @@ func (s *Street) assignValues(values ...interface{}) error { } else if value.Valid { s.Name = value.String } + values = values[1:] + if len(values) == len(street.ForeignKeys) { + if value, ok := values[0].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for edge-field city_id", value) + } else if value.Valid { + s.city_id = new(int) + *s.city_id = int(value.Int64) + } + } return nil } diff --git a/examples/edgeindex/ent/street/street.go b/examples/edgeindex/ent/street/street.go index 748f03131..00b6d793d 100644 --- a/examples/edgeindex/ent/street/street.go +++ b/examples/edgeindex/ent/street/street.go @@ -25,8 +25,13 @@ const ( CityColumn = "city_id" ) -// Columns holds all SQL columns are street fields. +// Columns holds all SQL columns for street fields. var Columns = []string{ FieldID, FieldName, } + +// ForeignKeys holds the SQL foreign-keys that are owned by the Street type. +var ForeignKeys = []string{ + "city_id", +} diff --git a/examples/edgeindex/ent/street_query.go b/examples/edgeindex/ent/street_query.go index b9ce562f7..9af1ca1a9 100644 --- a/examples/edgeindex/ent/street_query.go +++ b/examples/edgeindex/ent/street_query.go @@ -28,6 +28,9 @@ type StreetQuery struct { order []Order unique []string predicates []predicate.Street + // eager-loading edges. + withCity *CityQuery + withFKs bool // intermediate query. sql *sql.Selector } @@ -237,6 +240,17 @@ func (sq *StreetQuery) Clone() *StreetQuery { } } +// WithCity tells the query-builder to eager-loads the nodes that are connected to +// the "city" edge. The optional arguments used to configure the query builder of the edge. +func (sq *StreetQuery) WithCity(opts ...func(*CityQuery)) *StreetQuery { + query := &CityQuery{config: sq.config} + for _, opt := range opts { + opt(query) + } + sq.withCity = query + return sq +} + // GroupBy used to group vertices by one or more fields/columns. // It is often used with aggregate functions, like: count, max, mean, min, sum. // @@ -280,13 +294,24 @@ func (sq *StreetQuery) Select(field string, fields ...string) *StreetSelect { func (sq *StreetQuery) sqlAll(ctx context.Context) ([]*Street, error) { var ( - nodes []*Street - spec = sq.querySpec() + nodes []*Street + withFKs = sq.withFKs + spec = sq.querySpec() ) + if sq.withCity != nil { + withFKs = true + } + if withFKs { + spec.Node.Columns = append(spec.Node.Columns, street.ForeignKeys...) + } spec.ScanValues = func() []interface{} { node := &Street{config: sq.config} nodes = append(nodes, node) - return node.scanValues() + values := node.scanValues() + if withFKs { + values = append(values, node.fkValues()...) + } + return values } spec.Assign = func(values ...interface{}) error { if len(nodes) == 0 { @@ -298,6 +323,32 @@ func (sq *StreetQuery) sqlAll(ctx context.Context) ([]*Street, error) { if err := sqlgraph.QueryNodes(ctx, sq.driver, spec); err != nil { return nil, err } + + if query := sq.withCity; query != nil { + ids := make([]int, 0, len(nodes)) + nodeids := make(map[int][]*Street) + for i := range nodes { + if fk := nodes[i].city_id; fk != nil { + ids = append(ids, *fk) + nodeids[*fk] = append(nodeids[*fk], nodes[i]) + } + } + query.Where(city.IDIn(ids...)) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + nodes, ok := nodeids[n.ID] + if !ok { + return nil, fmt.Errorf(`unexpected foreign-key "city_id" returned %v`, n.ID) + } + for i := range nodes { + nodes[i].Edges.City = n + } + } + } + return nodes, nil } diff --git a/examples/entcpkg/ent/user.go b/examples/entcpkg/ent/user.go index e5d87ba83..4cee633ae 100644 --- a/examples/entcpkg/ent/user.go +++ b/examples/entcpkg/ent/user.go @@ -24,14 +24,14 @@ type User struct { // scanValues returns the types for scanning values from sql.Rows. func (*User) scanValues() []interface{} { return []interface{}{ - &sql.NullInt64{}, + &sql.NullInt64{}, // id } } // assignValues assigns the values that were returned from sql.Rows (after scanning) // to the User fields. func (u *User) assignValues(values ...interface{}) error { - if m, n := len(values), len(user.Columns); m != n { + if m, n := len(values), len(user.Columns); m < n { return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) } value, ok := values[0].(*sql.NullInt64) diff --git a/examples/entcpkg/ent/user/user.go b/examples/entcpkg/ent/user/user.go index c310c6fc1..75009ebd9 100644 --- a/examples/entcpkg/ent/user/user.go +++ b/examples/entcpkg/ent/user/user.go @@ -16,7 +16,7 @@ const ( Table = "users" ) -// Columns holds all SQL columns are user fields. +// Columns holds all SQL columns for user fields. var Columns = []string{ FieldID, } diff --git a/examples/entcpkg/ent/user_query.go b/examples/entcpkg/ent/user_query.go index 2eee933ef..a1121600b 100644 --- a/examples/entcpkg/ent/user_query.go +++ b/examples/entcpkg/ent/user_query.go @@ -249,7 +249,8 @@ func (uq *UserQuery) sqlAll(ctx context.Context) ([]*User, error) { spec.ScanValues = func() []interface{} { node := &User{config: uq.config} nodes = append(nodes, node) - return node.scanValues() + values := node.scanValues() + return values } spec.Assign = func(values ...interface{}) error { if len(nodes) == 0 { diff --git a/examples/m2m2types/ent/group.go b/examples/m2m2types/ent/group.go index 00a01931d..7484a66fd 100644 --- a/examples/m2m2types/ent/group.go +++ b/examples/m2m2types/ent/group.go @@ -21,20 +21,26 @@ type Group struct { ID int `json:"id,omitempty"` // Name holds the value of the "name" field. Name string `json:"name,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the GroupQuery when eager-loading is set. + Edges struct { + // Users holds the value of the users edge. + Users []*User + } } // scanValues returns the types for scanning values from sql.Rows. func (*Group) scanValues() []interface{} { return []interface{}{ - &sql.NullInt64{}, - &sql.NullString{}, + &sql.NullInt64{}, // id + &sql.NullString{}, // name } } // assignValues assigns the values that were returned from sql.Rows (after scanning) // to the Group fields. func (gr *Group) assignValues(values ...interface{}) error { - if m, n := len(values), len(group.Columns); m != n { + if m, n := len(values), len(group.Columns); m < n { return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) } value, ok := values[0].(*sql.NullInt64) diff --git a/examples/m2m2types/ent/group/group.go b/examples/m2m2types/ent/group/group.go index fcb0cbee4..070d4f894 100644 --- a/examples/m2m2types/ent/group/group.go +++ b/examples/m2m2types/ent/group/group.go @@ -23,7 +23,7 @@ const ( UsersInverseTable = "users" ) -// Columns holds all SQL columns are group fields. +// Columns holds all SQL columns for group fields. var Columns = []string{ FieldID, FieldName, diff --git a/examples/m2m2types/ent/group_query.go b/examples/m2m2types/ent/group_query.go index 9f1550e55..4d3aa575f 100644 --- a/examples/m2m2types/ent/group_query.go +++ b/examples/m2m2types/ent/group_query.go @@ -8,6 +8,7 @@ package ent import ( "context" + "database/sql/driver" "errors" "fmt" "math" @@ -28,6 +29,8 @@ type GroupQuery struct { order []Order unique []string predicates []predicate.Group + // eager-loading edges. + withUsers *UserQuery // intermediate query. sql *sql.Selector } @@ -237,6 +240,17 @@ func (gq *GroupQuery) Clone() *GroupQuery { } } +// WithUsers tells the query-builder to eager-loads the nodes that are connected to +// the "users" edge. The optional arguments used to configure the query builder of the edge. +func (gq *GroupQuery) WithUsers(opts ...func(*UserQuery)) *GroupQuery { + query := &UserQuery{config: gq.config} + for _, opt := range opts { + opt(query) + } + gq.withUsers = query + return gq +} + // GroupBy used to group vertices by one or more fields/columns. // It is often used with aggregate functions, like: count, max, mean, min, sum. // @@ -286,7 +300,8 @@ func (gq *GroupQuery) sqlAll(ctx context.Context) ([]*Group, error) { spec.ScanValues = func() []interface{} { node := &Group{config: gq.config} nodes = append(nodes, node) - return node.scanValues() + values := node.scanValues() + return values } spec.Assign = func(values ...interface{}) error { if len(nodes) == 0 { @@ -298,6 +313,70 @@ func (gq *GroupQuery) sqlAll(ctx context.Context) ([]*Group, error) { if err := sqlgraph.QueryNodes(ctx, gq.driver, spec); err != nil { return nil, err } + + if query := gq.withUsers; query != nil { + fks := make([]driver.Value, 0, len(nodes)) + ids := make(map[int]*Group, len(nodes)) + for _, node := range nodes { + ids[node.ID] = node + fks = append(fks, node.ID) + } + var ( + edgeids []int + edges = make(map[int][]*Group) + ) + spec := &sqlgraph.EdgeQuerySpec{ + Edge: &sqlgraph.EdgeSpec{ + Inverse: false, + Table: group.UsersTable, + Columns: group.UsersPrimaryKey, + }, + Predicate: func(s *sql.Selector) { + s.Where(sql.InValues(group.UsersPrimaryKey[0], fks...)) + }, + + ScanValues: func() [2]interface{} { + return [2]interface{}{&sql.NullInt64{}, &sql.NullInt64{}} + }, + Assign: func(out, in interface{}) error { + eout, ok := out.(*sql.NullInt64) + if !ok || eout == nil { + return fmt.Errorf("unexpected id value for edge-out") + } + ein, ok := in.(*sql.NullInt64) + if !ok || ein == nil { + return fmt.Errorf("unexpected id value for edge-in") + } + outValue := int(eout.Int64) + inValue := int(eout.Int64) + node, ok := ids[outValue] + if !ok { + return fmt.Errorf("unexpected node id in edges: %v", outValue) + } + edgeids = append(edgeids, inValue) + edges[inValue] = append(edges[inValue], node) + return nil + }, + } + if err := sqlgraph.QueryEdges(ctx, gq.driver, spec); err != nil { + return nil, fmt.Errorf(`query edges "users": %v`, err) + } + query.Where(user.IDIn(edgeids...)) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + nodes, ok := edges[n.ID] + if !ok { + return nil, fmt.Errorf(`unexpected "users" node returned %v`, n.ID) + } + for i := range nodes { + nodes[i].Edges.Users = append(nodes[i].Edges.Users, n) + } + } + } + return nodes, nil } diff --git a/examples/m2m2types/ent/user.go b/examples/m2m2types/ent/user.go index df3b95b53..2d8301379 100644 --- a/examples/m2m2types/ent/user.go +++ b/examples/m2m2types/ent/user.go @@ -23,21 +23,27 @@ type User struct { Age int `json:"age,omitempty"` // Name holds the value of the "name" field. Name string `json:"name,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the UserQuery when eager-loading is set. + Edges struct { + // Groups holds the value of the groups edge. + Groups []*Group + } } // scanValues returns the types for scanning values from sql.Rows. func (*User) scanValues() []interface{} { return []interface{}{ - &sql.NullInt64{}, - &sql.NullInt64{}, - &sql.NullString{}, + &sql.NullInt64{}, // id + &sql.NullInt64{}, // age + &sql.NullString{}, // name } } // assignValues assigns the values that were returned from sql.Rows (after scanning) // to the User fields. func (u *User) assignValues(values ...interface{}) error { - if m, n := len(values), len(user.Columns); m != n { + if m, n := len(values), len(user.Columns); m < n { return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) } value, ok := values[0].(*sql.NullInt64) diff --git a/examples/m2m2types/ent/user/user.go b/examples/m2m2types/ent/user/user.go index 6306c2a08..d743d1702 100644 --- a/examples/m2m2types/ent/user/user.go +++ b/examples/m2m2types/ent/user/user.go @@ -25,7 +25,7 @@ const ( GroupsInverseTable = "groups" ) -// Columns holds all SQL columns are user fields. +// Columns holds all SQL columns for user fields. var Columns = []string{ FieldID, FieldAge, diff --git a/examples/m2m2types/ent/user_query.go b/examples/m2m2types/ent/user_query.go index 20d4aba49..dcd1b13f8 100644 --- a/examples/m2m2types/ent/user_query.go +++ b/examples/m2m2types/ent/user_query.go @@ -8,6 +8,7 @@ package ent import ( "context" + "database/sql/driver" "errors" "fmt" "math" @@ -28,6 +29,8 @@ type UserQuery struct { order []Order unique []string predicates []predicate.User + // eager-loading edges. + withGroups *GroupQuery // intermediate query. sql *sql.Selector } @@ -237,6 +240,17 @@ func (uq *UserQuery) Clone() *UserQuery { } } +// WithGroups tells the query-builder to eager-loads the nodes that are connected to +// the "groups" edge. The optional arguments used to configure the query builder of the edge. +func (uq *UserQuery) WithGroups(opts ...func(*GroupQuery)) *UserQuery { + query := &GroupQuery{config: uq.config} + for _, opt := range opts { + opt(query) + } + uq.withGroups = query + return uq +} + // GroupBy used to group vertices by one or more fields/columns. // It is often used with aggregate functions, like: count, max, mean, min, sum. // @@ -286,7 +300,8 @@ func (uq *UserQuery) sqlAll(ctx context.Context) ([]*User, error) { spec.ScanValues = func() []interface{} { node := &User{config: uq.config} nodes = append(nodes, node) - return node.scanValues() + values := node.scanValues() + return values } spec.Assign = func(values ...interface{}) error { if len(nodes) == 0 { @@ -298,6 +313,70 @@ func (uq *UserQuery) sqlAll(ctx context.Context) ([]*User, error) { if err := sqlgraph.QueryNodes(ctx, uq.driver, spec); err != nil { return nil, err } + + if query := uq.withGroups; query != nil { + fks := make([]driver.Value, 0, len(nodes)) + ids := make(map[int]*User, len(nodes)) + for _, node := range nodes { + ids[node.ID] = node + fks = append(fks, node.ID) + } + var ( + edgeids []int + edges = make(map[int][]*User) + ) + spec := &sqlgraph.EdgeQuerySpec{ + Edge: &sqlgraph.EdgeSpec{ + Inverse: true, + Table: user.GroupsTable, + Columns: user.GroupsPrimaryKey, + }, + Predicate: func(s *sql.Selector) { + s.Where(sql.InValues(user.GroupsPrimaryKey[1], fks...)) + }, + + ScanValues: func() [2]interface{} { + return [2]interface{}{&sql.NullInt64{}, &sql.NullInt64{}} + }, + Assign: func(out, in interface{}) error { + eout, ok := out.(*sql.NullInt64) + if !ok || eout == nil { + return fmt.Errorf("unexpected id value for edge-out") + } + ein, ok := in.(*sql.NullInt64) + if !ok || ein == nil { + return fmt.Errorf("unexpected id value for edge-in") + } + outValue := int(eout.Int64) + inValue := int(eout.Int64) + node, ok := ids[outValue] + if !ok { + return fmt.Errorf("unexpected node id in edges: %v", outValue) + } + edgeids = append(edgeids, inValue) + edges[inValue] = append(edges[inValue], node) + return nil + }, + } + if err := sqlgraph.QueryEdges(ctx, uq.driver, spec); err != nil { + return nil, fmt.Errorf(`query edges "groups": %v`, err) + } + query.Where(group.IDIn(edgeids...)) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + nodes, ok := edges[n.ID] + if !ok { + return nil, fmt.Errorf(`unexpected "groups" node returned %v`, n.ID) + } + for i := range nodes { + nodes[i].Edges.Groups = append(nodes[i].Edges.Groups, n) + } + } + } + return nodes, nil } diff --git a/examples/m2mbidi/ent/user.go b/examples/m2mbidi/ent/user.go index d7bd7f051..fc567fe9d 100644 --- a/examples/m2mbidi/ent/user.go +++ b/examples/m2mbidi/ent/user.go @@ -23,21 +23,27 @@ type User struct { Age int `json:"age,omitempty"` // Name holds the value of the "name" field. Name string `json:"name,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the UserQuery when eager-loading is set. + Edges struct { + // Friends holds the value of the friends edge. + Friends []*User + } } // scanValues returns the types for scanning values from sql.Rows. func (*User) scanValues() []interface{} { return []interface{}{ - &sql.NullInt64{}, - &sql.NullInt64{}, - &sql.NullString{}, + &sql.NullInt64{}, // id + &sql.NullInt64{}, // age + &sql.NullString{}, // name } } // assignValues assigns the values that were returned from sql.Rows (after scanning) // to the User fields. func (u *User) assignValues(values ...interface{}) error { - if m, n := len(values), len(user.Columns); m != n { + if m, n := len(values), len(user.Columns); m < n { return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) } value, ok := values[0].(*sql.NullInt64) diff --git a/examples/m2mbidi/ent/user/user.go b/examples/m2mbidi/ent/user/user.go index f1aae0081..59abaa60c 100644 --- a/examples/m2mbidi/ent/user/user.go +++ b/examples/m2mbidi/ent/user/user.go @@ -22,7 +22,7 @@ const ( FriendsTable = "user_friends" ) -// Columns holds all SQL columns are user fields. +// Columns holds all SQL columns for user fields. var Columns = []string{ FieldID, FieldAge, diff --git a/examples/m2mbidi/ent/user_query.go b/examples/m2mbidi/ent/user_query.go index 4351a0df6..a540f05ec 100644 --- a/examples/m2mbidi/ent/user_query.go +++ b/examples/m2mbidi/ent/user_query.go @@ -8,6 +8,7 @@ package ent import ( "context" + "database/sql/driver" "errors" "fmt" "math" @@ -27,6 +28,8 @@ type UserQuery struct { order []Order unique []string predicates []predicate.User + // eager-loading edges. + withFriends *UserQuery // intermediate query. sql *sql.Selector } @@ -236,6 +239,17 @@ func (uq *UserQuery) Clone() *UserQuery { } } +// WithFriends tells the query-builder to eager-loads the nodes that are connected to +// the "friends" edge. The optional arguments used to configure the query builder of the edge. +func (uq *UserQuery) WithFriends(opts ...func(*UserQuery)) *UserQuery { + query := &UserQuery{config: uq.config} + for _, opt := range opts { + opt(query) + } + uq.withFriends = query + return uq +} + // GroupBy used to group vertices by one or more fields/columns. // It is often used with aggregate functions, like: count, max, mean, min, sum. // @@ -285,7 +299,8 @@ func (uq *UserQuery) sqlAll(ctx context.Context) ([]*User, error) { spec.ScanValues = func() []interface{} { node := &User{config: uq.config} nodes = append(nodes, node) - return node.scanValues() + values := node.scanValues() + return values } spec.Assign = func(values ...interface{}) error { if len(nodes) == 0 { @@ -297,6 +312,70 @@ func (uq *UserQuery) sqlAll(ctx context.Context) ([]*User, error) { if err := sqlgraph.QueryNodes(ctx, uq.driver, spec); err != nil { return nil, err } + + if query := uq.withFriends; query != nil { + fks := make([]driver.Value, 0, len(nodes)) + ids := make(map[int]*User, len(nodes)) + for _, node := range nodes { + ids[node.ID] = node + fks = append(fks, node.ID) + } + var ( + edgeids []int + edges = make(map[int][]*User) + ) + spec := &sqlgraph.EdgeQuerySpec{ + Edge: &sqlgraph.EdgeSpec{ + Inverse: false, + Table: user.FriendsTable, + Columns: user.FriendsPrimaryKey, + }, + Predicate: func(s *sql.Selector) { + s.Where(sql.InValues(user.FriendsPrimaryKey[0], fks...)) + }, + + ScanValues: func() [2]interface{} { + return [2]interface{}{&sql.NullInt64{}, &sql.NullInt64{}} + }, + Assign: func(out, in interface{}) error { + eout, ok := out.(*sql.NullInt64) + if !ok || eout == nil { + return fmt.Errorf("unexpected id value for edge-out") + } + ein, ok := in.(*sql.NullInt64) + if !ok || ein == nil { + return fmt.Errorf("unexpected id value for edge-in") + } + outValue := int(eout.Int64) + inValue := int(eout.Int64) + node, ok := ids[outValue] + if !ok { + return fmt.Errorf("unexpected node id in edges: %v", outValue) + } + edgeids = append(edgeids, inValue) + edges[inValue] = append(edges[inValue], node) + return nil + }, + } + if err := sqlgraph.QueryEdges(ctx, uq.driver, spec); err != nil { + return nil, fmt.Errorf(`query edges "friends": %v`, err) + } + query.Where(user.IDIn(edgeids...)) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + nodes, ok := edges[n.ID] + if !ok { + return nil, fmt.Errorf(`unexpected "friends" node returned %v`, n.ID) + } + for i := range nodes { + nodes[i].Edges.Friends = append(nodes[i].Edges.Friends, n) + } + } + } + return nodes, nil } diff --git a/examples/m2mrecur/ent/user.go b/examples/m2mrecur/ent/user.go index 0af01a579..4ab7adbc1 100644 --- a/examples/m2mrecur/ent/user.go +++ b/examples/m2mrecur/ent/user.go @@ -23,21 +23,29 @@ type User struct { Age int `json:"age,omitempty"` // Name holds the value of the "name" field. Name string `json:"name,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the UserQuery when eager-loading is set. + Edges struct { + // Followers holds the value of the followers edge. + Followers []*User + // Following holds the value of the following edge. + Following []*User + } } // scanValues returns the types for scanning values from sql.Rows. func (*User) scanValues() []interface{} { return []interface{}{ - &sql.NullInt64{}, - &sql.NullInt64{}, - &sql.NullString{}, + &sql.NullInt64{}, // id + &sql.NullInt64{}, // age + &sql.NullString{}, // name } } // assignValues assigns the values that were returned from sql.Rows (after scanning) // to the User fields. func (u *User) assignValues(values ...interface{}) error { - if m, n := len(values), len(user.Columns); m != n { + if m, n := len(values), len(user.Columns); m < n { return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) } value, ok := values[0].(*sql.NullInt64) diff --git a/examples/m2mrecur/ent/user/user.go b/examples/m2mrecur/ent/user/user.go index b4259a2a0..5cff95ba0 100644 --- a/examples/m2mrecur/ent/user/user.go +++ b/examples/m2mrecur/ent/user/user.go @@ -24,7 +24,7 @@ const ( FollowingTable = "user_following" ) -// Columns holds all SQL columns are user fields. +// Columns holds all SQL columns for user fields. var Columns = []string{ FieldID, FieldAge, diff --git a/examples/m2mrecur/ent/user_query.go b/examples/m2mrecur/ent/user_query.go index cafe6c5cf..654c7d17b 100644 --- a/examples/m2mrecur/ent/user_query.go +++ b/examples/m2mrecur/ent/user_query.go @@ -8,6 +8,7 @@ package ent import ( "context" + "database/sql/driver" "errors" "fmt" "math" @@ -27,6 +28,9 @@ type UserQuery struct { order []Order unique []string predicates []predicate.User + // eager-loading edges. + withFollowers *UserQuery + withFollowing *UserQuery // intermediate query. sql *sql.Selector } @@ -248,6 +252,28 @@ func (uq *UserQuery) Clone() *UserQuery { } } +// WithFollowers tells the query-builder to eager-loads the nodes that are connected to +// the "followers" edge. The optional arguments used to configure the query builder of the edge. +func (uq *UserQuery) WithFollowers(opts ...func(*UserQuery)) *UserQuery { + query := &UserQuery{config: uq.config} + for _, opt := range opts { + opt(query) + } + uq.withFollowers = query + return uq +} + +// WithFollowing tells the query-builder to eager-loads the nodes that are connected to +// the "following" edge. The optional arguments used to configure the query builder of the edge. +func (uq *UserQuery) WithFollowing(opts ...func(*UserQuery)) *UserQuery { + query := &UserQuery{config: uq.config} + for _, opt := range opts { + opt(query) + } + uq.withFollowing = query + return uq +} + // GroupBy used to group vertices by one or more fields/columns. // It is often used with aggregate functions, like: count, max, mean, min, sum. // @@ -297,7 +323,8 @@ func (uq *UserQuery) sqlAll(ctx context.Context) ([]*User, error) { spec.ScanValues = func() []interface{} { node := &User{config: uq.config} nodes = append(nodes, node) - return node.scanValues() + values := node.scanValues() + return values } spec.Assign = func(values ...interface{}) error { if len(nodes) == 0 { @@ -309,6 +336,133 @@ func (uq *UserQuery) sqlAll(ctx context.Context) ([]*User, error) { if err := sqlgraph.QueryNodes(ctx, uq.driver, spec); err != nil { return nil, err } + + if query := uq.withFollowers; query != nil { + fks := make([]driver.Value, 0, len(nodes)) + ids := make(map[int]*User, len(nodes)) + for _, node := range nodes { + ids[node.ID] = node + fks = append(fks, node.ID) + } + var ( + edgeids []int + edges = make(map[int][]*User) + ) + spec := &sqlgraph.EdgeQuerySpec{ + Edge: &sqlgraph.EdgeSpec{ + Inverse: true, + Table: user.FollowersTable, + Columns: user.FollowersPrimaryKey, + }, + Predicate: func(s *sql.Selector) { + s.Where(sql.InValues(user.FollowersPrimaryKey[1], fks...)) + }, + + ScanValues: func() [2]interface{} { + return [2]interface{}{&sql.NullInt64{}, &sql.NullInt64{}} + }, + Assign: func(out, in interface{}) error { + eout, ok := out.(*sql.NullInt64) + if !ok || eout == nil { + return fmt.Errorf("unexpected id value for edge-out") + } + ein, ok := in.(*sql.NullInt64) + if !ok || ein == nil { + return fmt.Errorf("unexpected id value for edge-in") + } + outValue := int(eout.Int64) + inValue := int(eout.Int64) + node, ok := ids[outValue] + if !ok { + return fmt.Errorf("unexpected node id in edges: %v", outValue) + } + edgeids = append(edgeids, inValue) + edges[inValue] = append(edges[inValue], node) + return nil + }, + } + if err := sqlgraph.QueryEdges(ctx, uq.driver, spec); err != nil { + return nil, fmt.Errorf(`query edges "followers": %v`, err) + } + query.Where(user.IDIn(edgeids...)) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + nodes, ok := edges[n.ID] + if !ok { + return nil, fmt.Errorf(`unexpected "followers" node returned %v`, n.ID) + } + for i := range nodes { + nodes[i].Edges.Followers = append(nodes[i].Edges.Followers, n) + } + } + } + + if query := uq.withFollowing; query != nil { + fks := make([]driver.Value, 0, len(nodes)) + ids := make(map[int]*User, len(nodes)) + for _, node := range nodes { + ids[node.ID] = node + fks = append(fks, node.ID) + } + var ( + edgeids []int + edges = make(map[int][]*User) + ) + spec := &sqlgraph.EdgeQuerySpec{ + Edge: &sqlgraph.EdgeSpec{ + Inverse: false, + Table: user.FollowingTable, + Columns: user.FollowingPrimaryKey, + }, + Predicate: func(s *sql.Selector) { + s.Where(sql.InValues(user.FollowingPrimaryKey[0], fks...)) + }, + + ScanValues: func() [2]interface{} { + return [2]interface{}{&sql.NullInt64{}, &sql.NullInt64{}} + }, + Assign: func(out, in interface{}) error { + eout, ok := out.(*sql.NullInt64) + if !ok || eout == nil { + return fmt.Errorf("unexpected id value for edge-out") + } + ein, ok := in.(*sql.NullInt64) + if !ok || ein == nil { + return fmt.Errorf("unexpected id value for edge-in") + } + outValue := int(eout.Int64) + inValue := int(eout.Int64) + node, ok := ids[outValue] + if !ok { + return fmt.Errorf("unexpected node id in edges: %v", outValue) + } + edgeids = append(edgeids, inValue) + edges[inValue] = append(edges[inValue], node) + return nil + }, + } + if err := sqlgraph.QueryEdges(ctx, uq.driver, spec); err != nil { + return nil, fmt.Errorf(`query edges "following": %v`, err) + } + query.Where(user.IDIn(edgeids...)) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + nodes, ok := edges[n.ID] + if !ok { + return nil, fmt.Errorf(`unexpected "following" node returned %v`, n.ID) + } + for i := range nodes { + nodes[i].Edges.Following = append(nodes[i].Edges.Following, n) + } + } + } + return nodes, nil } diff --git a/examples/o2m2types/ent/pet.go b/examples/o2m2types/ent/pet.go index b07ed2f31..440404531 100644 --- a/examples/o2m2types/ent/pet.go +++ b/examples/o2m2types/ent/pet.go @@ -21,20 +21,34 @@ type Pet struct { ID int `json:"id,omitempty"` // Name holds the value of the "name" field. Name string `json:"name,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the PetQuery when eager-loading is set. + Edges struct { + // Owner holds the value of the owner edge. + Owner *User + } + owner_id *int } // scanValues returns the types for scanning values from sql.Rows. func (*Pet) scanValues() []interface{} { return []interface{}{ - &sql.NullInt64{}, - &sql.NullString{}, + &sql.NullInt64{}, // id + &sql.NullString{}, // name + } +} + +// fkValues returns the types for scanning foreign-keys values from sql.Rows. +func (*Pet) fkValues() []interface{} { + return []interface{}{ + &sql.NullInt64{}, // owner_id } } // assignValues assigns the values that were returned from sql.Rows (after scanning) // to the Pet fields. func (pe *Pet) assignValues(values ...interface{}) error { - if m, n := len(values), len(pet.Columns); m != n { + if m, n := len(values), len(pet.Columns); m < n { return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) } value, ok := values[0].(*sql.NullInt64) @@ -48,6 +62,15 @@ func (pe *Pet) assignValues(values ...interface{}) error { } else if value.Valid { pe.Name = value.String } + values = values[1:] + if len(values) == len(pet.ForeignKeys) { + if value, ok := values[0].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for edge-field owner_id", value) + } else if value.Valid { + pe.owner_id = new(int) + *pe.owner_id = int(value.Int64) + } + } return nil } diff --git a/examples/o2m2types/ent/pet/pet.go b/examples/o2m2types/ent/pet/pet.go index b3cd705c1..c1d15c069 100644 --- a/examples/o2m2types/ent/pet/pet.go +++ b/examples/o2m2types/ent/pet/pet.go @@ -25,8 +25,13 @@ const ( OwnerColumn = "owner_id" ) -// Columns holds all SQL columns are pet fields. +// Columns holds all SQL columns for pet fields. var Columns = []string{ FieldID, FieldName, } + +// ForeignKeys holds the SQL foreign-keys that are owned by the Pet type. +var ForeignKeys = []string{ + "owner_id", +} diff --git a/examples/o2m2types/ent/pet_query.go b/examples/o2m2types/ent/pet_query.go index 72a0648ec..d8ebd6034 100644 --- a/examples/o2m2types/ent/pet_query.go +++ b/examples/o2m2types/ent/pet_query.go @@ -28,6 +28,9 @@ type PetQuery struct { order []Order unique []string predicates []predicate.Pet + // eager-loading edges. + withOwner *UserQuery + withFKs bool // intermediate query. sql *sql.Selector } @@ -237,6 +240,17 @@ func (pq *PetQuery) Clone() *PetQuery { } } +// WithOwner tells the query-builder to eager-loads the nodes that are connected to +// the "owner" edge. The optional arguments used to configure the query builder of the edge. +func (pq *PetQuery) WithOwner(opts ...func(*UserQuery)) *PetQuery { + query := &UserQuery{config: pq.config} + for _, opt := range opts { + opt(query) + } + pq.withOwner = query + return pq +} + // GroupBy used to group vertices by one or more fields/columns. // It is often used with aggregate functions, like: count, max, mean, min, sum. // @@ -280,13 +294,24 @@ func (pq *PetQuery) Select(field string, fields ...string) *PetSelect { func (pq *PetQuery) sqlAll(ctx context.Context) ([]*Pet, error) { var ( - nodes []*Pet - spec = pq.querySpec() + nodes []*Pet + withFKs = pq.withFKs + spec = pq.querySpec() ) + if pq.withOwner != nil { + withFKs = true + } + if withFKs { + spec.Node.Columns = append(spec.Node.Columns, pet.ForeignKeys...) + } spec.ScanValues = func() []interface{} { node := &Pet{config: pq.config} nodes = append(nodes, node) - return node.scanValues() + values := node.scanValues() + if withFKs { + values = append(values, node.fkValues()...) + } + return values } spec.Assign = func(values ...interface{}) error { if len(nodes) == 0 { @@ -298,6 +323,32 @@ func (pq *PetQuery) sqlAll(ctx context.Context) ([]*Pet, error) { if err := sqlgraph.QueryNodes(ctx, pq.driver, spec); err != nil { return nil, err } + + if query := pq.withOwner; query != nil { + ids := make([]int, 0, len(nodes)) + nodeids := make(map[int][]*Pet) + for i := range nodes { + if fk := nodes[i].owner_id; fk != nil { + ids = append(ids, *fk) + nodeids[*fk] = append(nodeids[*fk], nodes[i]) + } + } + query.Where(user.IDIn(ids...)) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + nodes, ok := nodeids[n.ID] + if !ok { + return nil, fmt.Errorf(`unexpected foreign-key "owner_id" returned %v`, n.ID) + } + for i := range nodes { + nodes[i].Edges.Owner = n + } + } + } + return nodes, nil } diff --git a/examples/o2m2types/ent/user.go b/examples/o2m2types/ent/user.go index 701b37812..bb95ce618 100644 --- a/examples/o2m2types/ent/user.go +++ b/examples/o2m2types/ent/user.go @@ -23,21 +23,27 @@ type User struct { Age int `json:"age,omitempty"` // Name holds the value of the "name" field. Name string `json:"name,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the UserQuery when eager-loading is set. + Edges struct { + // Pets holds the value of the pets edge. + Pets []*Pet + } } // scanValues returns the types for scanning values from sql.Rows. func (*User) scanValues() []interface{} { return []interface{}{ - &sql.NullInt64{}, - &sql.NullInt64{}, - &sql.NullString{}, + &sql.NullInt64{}, // id + &sql.NullInt64{}, // age + &sql.NullString{}, // name } } // assignValues assigns the values that were returned from sql.Rows (after scanning) // to the User fields. func (u *User) assignValues(values ...interface{}) error { - if m, n := len(values), len(user.Columns); m != n { + if m, n := len(values), len(user.Columns); m < n { return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) } value, ok := values[0].(*sql.NullInt64) diff --git a/examples/o2m2types/ent/user/user.go b/examples/o2m2types/ent/user/user.go index 69b6e5fee..212972ab2 100644 --- a/examples/o2m2types/ent/user/user.go +++ b/examples/o2m2types/ent/user/user.go @@ -27,7 +27,7 @@ const ( PetsColumn = "owner_id" ) -// Columns holds all SQL columns are user fields. +// Columns holds all SQL columns for user fields. var Columns = []string{ FieldID, FieldAge, diff --git a/examples/o2m2types/ent/user_query.go b/examples/o2m2types/ent/user_query.go index 6834013be..24d0c3dd3 100644 --- a/examples/o2m2types/ent/user_query.go +++ b/examples/o2m2types/ent/user_query.go @@ -8,6 +8,7 @@ package ent import ( "context" + "database/sql/driver" "errors" "fmt" "math" @@ -28,6 +29,8 @@ type UserQuery struct { order []Order unique []string predicates []predicate.User + // eager-loading edges. + withPets *PetQuery // intermediate query. sql *sql.Selector } @@ -237,6 +240,17 @@ func (uq *UserQuery) Clone() *UserQuery { } } +// WithPets tells the query-builder to eager-loads the nodes that are connected to +// the "pets" edge. The optional arguments used to configure the query builder of the edge. +func (uq *UserQuery) WithPets(opts ...func(*PetQuery)) *UserQuery { + query := &PetQuery{config: uq.config} + for _, opt := range opts { + opt(query) + } + uq.withPets = query + return uq +} + // GroupBy used to group vertices by one or more fields/columns. // It is often used with aggregate functions, like: count, max, mean, min, sum. // @@ -286,7 +300,8 @@ func (uq *UserQuery) sqlAll(ctx context.Context) ([]*User, error) { spec.ScanValues = func() []interface{} { node := &User{config: uq.config} nodes = append(nodes, node) - return node.scanValues() + values := node.scanValues() + return values } spec.Assign = func(values ...interface{}) error { if len(nodes) == 0 { @@ -298,6 +313,35 @@ func (uq *UserQuery) sqlAll(ctx context.Context) ([]*User, error) { if err := sqlgraph.QueryNodes(ctx, uq.driver, spec); err != nil { return nil, err } + + if query := uq.withPets; query != nil { + fks := make([]driver.Value, 0, len(nodes)) + nodeids := make(map[int]*User) + for i := range nodes { + fks = append(fks, nodes[i].ID) + nodeids[nodes[i].ID] = nodes[i] + } + query.withFKs = true + query.Where(predicate.Pet(func(s *sql.Selector) { + s.Where(sql.InValues(user.PetsColumn, fks...)) + })) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + fk := n.owner_id + if fk == nil { + return nil, fmt.Errorf(`foreign-key "owner_id" is nil for node %v`, n.ID) + } + node, ok := nodeids[*fk] + if !ok { + return nil, fmt.Errorf(`unexpected foreign-key "owner_id" returned %v for node %v`, *fk, n.ID) + } + node.Edges.Pets = append(node.Edges.Pets, n) + } + } + return nodes, nil } diff --git a/examples/o2mrecur/ent/node.go b/examples/o2mrecur/ent/node.go index 57258adb0..b0d48e0fc 100644 --- a/examples/o2mrecur/ent/node.go +++ b/examples/o2mrecur/ent/node.go @@ -21,20 +21,36 @@ type Node struct { ID int `json:"id,omitempty"` // Value holds the value of the "value" field. Value int `json:"value,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the NodeQuery when eager-loading is set. + Edges struct { + // Parent holds the value of the parent edge. + Parent *Node + // Children holds the value of the children edge. + Children []*Node + } + parent_id *int } // scanValues returns the types for scanning values from sql.Rows. func (*Node) scanValues() []interface{} { return []interface{}{ - &sql.NullInt64{}, - &sql.NullInt64{}, + &sql.NullInt64{}, // id + &sql.NullInt64{}, // value + } +} + +// fkValues returns the types for scanning foreign-keys values from sql.Rows. +func (*Node) fkValues() []interface{} { + return []interface{}{ + &sql.NullInt64{}, // parent_id } } // assignValues assigns the values that were returned from sql.Rows (after scanning) // to the Node fields. func (n *Node) assignValues(values ...interface{}) error { - if m, n := len(values), len(node.Columns); m != n { + if m, n := len(values), len(node.Columns); m < n { return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) } value, ok := values[0].(*sql.NullInt64) @@ -48,6 +64,15 @@ func (n *Node) assignValues(values ...interface{}) error { } else if value.Valid { n.Value = int(value.Int64) } + values = values[1:] + if len(values) == len(node.ForeignKeys) { + if value, ok := values[0].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for edge-field parent_id", value) + } else if value.Valid { + n.parent_id = new(int) + *n.parent_id = int(value.Int64) + } + } return nil } diff --git a/examples/o2mrecur/ent/node/node.go b/examples/o2mrecur/ent/node/node.go index fff2a2713..85eec7cd1 100644 --- a/examples/o2mrecur/ent/node/node.go +++ b/examples/o2mrecur/ent/node/node.go @@ -26,8 +26,13 @@ const ( ChildrenColumn = "parent_id" ) -// Columns holds all SQL columns are node fields. +// Columns holds all SQL columns for node fields. var Columns = []string{ FieldID, FieldValue, } + +// ForeignKeys holds the SQL foreign-keys that are owned by the Node type. +var ForeignKeys = []string{ + "parent_id", +} diff --git a/examples/o2mrecur/ent/node_query.go b/examples/o2mrecur/ent/node_query.go index a8db7c359..22004b7cf 100644 --- a/examples/o2mrecur/ent/node_query.go +++ b/examples/o2mrecur/ent/node_query.go @@ -8,6 +8,7 @@ package ent import ( "context" + "database/sql/driver" "errors" "fmt" "math" @@ -27,6 +28,10 @@ type NodeQuery struct { order []Order unique []string predicates []predicate.Node + // eager-loading edges. + withParent *NodeQuery + withChildren *NodeQuery + withFKs bool // intermediate query. sql *sql.Selector } @@ -248,6 +253,28 @@ func (nq *NodeQuery) Clone() *NodeQuery { } } +// WithParent tells the query-builder to eager-loads the nodes that are connected to +// the "parent" edge. The optional arguments used to configure the query builder of the edge. +func (nq *NodeQuery) WithParent(opts ...func(*NodeQuery)) *NodeQuery { + query := &NodeQuery{config: nq.config} + for _, opt := range opts { + opt(query) + } + nq.withParent = query + return nq +} + +// WithChildren tells the query-builder to eager-loads the nodes that are connected to +// the "children" edge. The optional arguments used to configure the query builder of the edge. +func (nq *NodeQuery) WithChildren(opts ...func(*NodeQuery)) *NodeQuery { + query := &NodeQuery{config: nq.config} + for _, opt := range opts { + opt(query) + } + nq.withChildren = query + return nq +} + // GroupBy used to group vertices by one or more fields/columns. // It is often used with aggregate functions, like: count, max, mean, min, sum. // @@ -291,13 +318,24 @@ func (nq *NodeQuery) Select(field string, fields ...string) *NodeSelect { func (nq *NodeQuery) sqlAll(ctx context.Context) ([]*Node, error) { var ( - nodes []*Node - spec = nq.querySpec() + nodes []*Node + withFKs = nq.withFKs + spec = nq.querySpec() ) + if nq.withParent != nil { + withFKs = true + } + if withFKs { + spec.Node.Columns = append(spec.Node.Columns, node.ForeignKeys...) + } spec.ScanValues = func() []interface{} { node := &Node{config: nq.config} nodes = append(nodes, node) - return node.scanValues() + values := node.scanValues() + if withFKs { + values = append(values, node.fkValues()...) + } + return values } spec.Assign = func(values ...interface{}) error { if len(nodes) == 0 { @@ -309,6 +347,60 @@ func (nq *NodeQuery) sqlAll(ctx context.Context) ([]*Node, error) { if err := sqlgraph.QueryNodes(ctx, nq.driver, spec); err != nil { return nil, err } + + if query := nq.withParent; query != nil { + ids := make([]int, 0, len(nodes)) + nodeids := make(map[int][]*Node) + for i := range nodes { + if fk := nodes[i].parent_id; fk != nil { + ids = append(ids, *fk) + nodeids[*fk] = append(nodeids[*fk], nodes[i]) + } + } + query.Where(node.IDIn(ids...)) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + nodes, ok := nodeids[n.ID] + if !ok { + return nil, fmt.Errorf(`unexpected foreign-key "parent_id" returned %v`, n.ID) + } + for i := range nodes { + nodes[i].Edges.Parent = n + } + } + } + + if query := nq.withChildren; query != nil { + fks := make([]driver.Value, 0, len(nodes)) + nodeids := make(map[int]*Node) + for i := range nodes { + fks = append(fks, nodes[i].ID) + nodeids[nodes[i].ID] = nodes[i] + } + query.withFKs = true + query.Where(predicate.Node(func(s *sql.Selector) { + s.Where(sql.InValues(node.ChildrenColumn, fks...)) + })) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + fk := n.parent_id + if fk == nil { + return nil, fmt.Errorf(`foreign-key "parent_id" is nil for node %v`, n.ID) + } + node, ok := nodeids[*fk] + if !ok { + return nil, fmt.Errorf(`unexpected foreign-key "parent_id" returned %v for node %v`, *fk, n.ID) + } + node.Edges.Children = append(node.Edges.Children, n) + } + } + return nodes, nil } diff --git a/examples/o2o2types/ent/card.go b/examples/o2o2types/ent/card.go index 12097d610..956dda187 100644 --- a/examples/o2o2types/ent/card.go +++ b/examples/o2o2types/ent/card.go @@ -24,21 +24,35 @@ type Card struct { Expired time.Time `json:"expired,omitempty"` // Number holds the value of the "number" field. Number string `json:"number,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the CardQuery when eager-loading is set. + Edges struct { + // Owner holds the value of the owner edge. + Owner *User + } + owner_id *int } // scanValues returns the types for scanning values from sql.Rows. func (*Card) scanValues() []interface{} { return []interface{}{ - &sql.NullInt64{}, - &sql.NullTime{}, - &sql.NullString{}, + &sql.NullInt64{}, // id + &sql.NullTime{}, // expired + &sql.NullString{}, // number + } +} + +// fkValues returns the types for scanning foreign-keys values from sql.Rows. +func (*Card) fkValues() []interface{} { + return []interface{}{ + &sql.NullInt64{}, // owner_id } } // assignValues assigns the values that were returned from sql.Rows (after scanning) // to the Card fields. func (c *Card) assignValues(values ...interface{}) error { - if m, n := len(values), len(card.Columns); m != n { + if m, n := len(values), len(card.Columns); m < n { return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) } value, ok := values[0].(*sql.NullInt64) @@ -57,6 +71,15 @@ func (c *Card) assignValues(values ...interface{}) error { } else if value.Valid { c.Number = value.String } + values = values[2:] + if len(values) == len(card.ForeignKeys) { + if value, ok := values[0].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for edge-field owner_id", value) + } else if value.Valid { + c.owner_id = new(int) + *c.owner_id = int(value.Int64) + } + } return nil } diff --git a/examples/o2o2types/ent/card/card.go b/examples/o2o2types/ent/card/card.go index 2999cab36..9ac13dfc0 100644 --- a/examples/o2o2types/ent/card/card.go +++ b/examples/o2o2types/ent/card/card.go @@ -27,9 +27,14 @@ const ( OwnerColumn = "owner_id" ) -// Columns holds all SQL columns are card fields. +// Columns holds all SQL columns for card fields. var Columns = []string{ FieldID, FieldExpired, FieldNumber, } + +// ForeignKeys holds the SQL foreign-keys that are owned by the Card type. +var ForeignKeys = []string{ + "owner_id", +} diff --git a/examples/o2o2types/ent/card_query.go b/examples/o2o2types/ent/card_query.go index 8cbcaaafc..702a7cece 100644 --- a/examples/o2o2types/ent/card_query.go +++ b/examples/o2o2types/ent/card_query.go @@ -28,6 +28,9 @@ type CardQuery struct { order []Order unique []string predicates []predicate.Card + // eager-loading edges. + withOwner *UserQuery + withFKs bool // intermediate query. sql *sql.Selector } @@ -237,6 +240,17 @@ func (cq *CardQuery) Clone() *CardQuery { } } +// WithOwner tells the query-builder to eager-loads the nodes that are connected to +// the "owner" edge. The optional arguments used to configure the query builder of the edge. +func (cq *CardQuery) WithOwner(opts ...func(*UserQuery)) *CardQuery { + query := &UserQuery{config: cq.config} + for _, opt := range opts { + opt(query) + } + cq.withOwner = query + return cq +} + // GroupBy used to group vertices by one or more fields/columns. // It is often used with aggregate functions, like: count, max, mean, min, sum. // @@ -280,13 +294,24 @@ func (cq *CardQuery) Select(field string, fields ...string) *CardSelect { func (cq *CardQuery) sqlAll(ctx context.Context) ([]*Card, error) { var ( - nodes []*Card - spec = cq.querySpec() + nodes []*Card + withFKs = cq.withFKs + spec = cq.querySpec() ) + if cq.withOwner != nil { + withFKs = true + } + if withFKs { + spec.Node.Columns = append(spec.Node.Columns, card.ForeignKeys...) + } spec.ScanValues = func() []interface{} { node := &Card{config: cq.config} nodes = append(nodes, node) - return node.scanValues() + values := node.scanValues() + if withFKs { + values = append(values, node.fkValues()...) + } + return values } spec.Assign = func(values ...interface{}) error { if len(nodes) == 0 { @@ -298,6 +323,32 @@ func (cq *CardQuery) sqlAll(ctx context.Context) ([]*Card, error) { if err := sqlgraph.QueryNodes(ctx, cq.driver, spec); err != nil { return nil, err } + + if query := cq.withOwner; query != nil { + ids := make([]int, 0, len(nodes)) + nodeids := make(map[int][]*Card) + for i := range nodes { + if fk := nodes[i].owner_id; fk != nil { + ids = append(ids, *fk) + nodeids[*fk] = append(nodeids[*fk], nodes[i]) + } + } + query.Where(user.IDIn(ids...)) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + nodes, ok := nodeids[n.ID] + if !ok { + return nil, fmt.Errorf(`unexpected foreign-key "owner_id" returned %v`, n.ID) + } + for i := range nodes { + nodes[i].Edges.Owner = n + } + } + } + return nodes, nil } diff --git a/examples/o2o2types/ent/user.go b/examples/o2o2types/ent/user.go index 33ea8ddd4..bca01c406 100644 --- a/examples/o2o2types/ent/user.go +++ b/examples/o2o2types/ent/user.go @@ -23,21 +23,27 @@ type User struct { Age int `json:"age,omitempty"` // Name holds the value of the "name" field. Name string `json:"name,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the UserQuery when eager-loading is set. + Edges struct { + // Card holds the value of the card edge. + Card *Card + } } // scanValues returns the types for scanning values from sql.Rows. func (*User) scanValues() []interface{} { return []interface{}{ - &sql.NullInt64{}, - &sql.NullInt64{}, - &sql.NullString{}, + &sql.NullInt64{}, // id + &sql.NullInt64{}, // age + &sql.NullString{}, // name } } // assignValues assigns the values that were returned from sql.Rows (after scanning) // to the User fields. func (u *User) assignValues(values ...interface{}) error { - if m, n := len(values), len(user.Columns); m != n { + if m, n := len(values), len(user.Columns); m < n { return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) } value, ok := values[0].(*sql.NullInt64) diff --git a/examples/o2o2types/ent/user/user.go b/examples/o2o2types/ent/user/user.go index 485e94e86..4a738de00 100644 --- a/examples/o2o2types/ent/user/user.go +++ b/examples/o2o2types/ent/user/user.go @@ -27,7 +27,7 @@ const ( CardColumn = "owner_id" ) -// Columns holds all SQL columns are user fields. +// Columns holds all SQL columns for user fields. var Columns = []string{ FieldID, FieldAge, diff --git a/examples/o2o2types/ent/user_query.go b/examples/o2o2types/ent/user_query.go index 4fe507174..2d3b34d91 100644 --- a/examples/o2o2types/ent/user_query.go +++ b/examples/o2o2types/ent/user_query.go @@ -8,6 +8,7 @@ package ent import ( "context" + "database/sql/driver" "errors" "fmt" "math" @@ -28,6 +29,8 @@ type UserQuery struct { order []Order unique []string predicates []predicate.User + // eager-loading edges. + withCard *CardQuery // intermediate query. sql *sql.Selector } @@ -237,6 +240,17 @@ func (uq *UserQuery) Clone() *UserQuery { } } +// WithCard tells the query-builder to eager-loads the nodes that are connected to +// the "card" edge. The optional arguments used to configure the query builder of the edge. +func (uq *UserQuery) WithCard(opts ...func(*CardQuery)) *UserQuery { + query := &CardQuery{config: uq.config} + for _, opt := range opts { + opt(query) + } + uq.withCard = query + return uq +} + // GroupBy used to group vertices by one or more fields/columns. // It is often used with aggregate functions, like: count, max, mean, min, sum. // @@ -286,7 +300,8 @@ func (uq *UserQuery) sqlAll(ctx context.Context) ([]*User, error) { spec.ScanValues = func() []interface{} { node := &User{config: uq.config} nodes = append(nodes, node) - return node.scanValues() + values := node.scanValues() + return values } spec.Assign = func(values ...interface{}) error { if len(nodes) == 0 { @@ -298,6 +313,35 @@ func (uq *UserQuery) sqlAll(ctx context.Context) ([]*User, error) { if err := sqlgraph.QueryNodes(ctx, uq.driver, spec); err != nil { return nil, err } + + if query := uq.withCard; query != nil { + fks := make([]driver.Value, 0, len(nodes)) + nodeids := make(map[int]*User) + for i := range nodes { + fks = append(fks, nodes[i].ID) + nodeids[nodes[i].ID] = nodes[i] + } + query.withFKs = true + query.Where(predicate.Card(func(s *sql.Selector) { + s.Where(sql.InValues(user.CardColumn, fks...)) + })) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + fk := n.owner_id + if fk == nil { + return nil, fmt.Errorf(`foreign-key "owner_id" is nil for node %v`, n.ID) + } + node, ok := nodeids[*fk] + if !ok { + return nil, fmt.Errorf(`unexpected foreign-key "owner_id" returned %v for node %v`, *fk, n.ID) + } + node.Edges.Card = n + } + } + return nodes, nil } diff --git a/examples/o2obidi/ent/user.go b/examples/o2obidi/ent/user.go index 45747e74f..cd917bf17 100644 --- a/examples/o2obidi/ent/user.go +++ b/examples/o2obidi/ent/user.go @@ -23,21 +23,35 @@ type User struct { Age int `json:"age,omitempty"` // Name holds the value of the "name" field. Name string `json:"name,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the UserQuery when eager-loading is set. + Edges struct { + // Spouse holds the value of the spouse edge. + Spouse *User + } + user_spouse_id *int } // scanValues returns the types for scanning values from sql.Rows. func (*User) scanValues() []interface{} { return []interface{}{ - &sql.NullInt64{}, - &sql.NullInt64{}, - &sql.NullString{}, + &sql.NullInt64{}, // id + &sql.NullInt64{}, // age + &sql.NullString{}, // name + } +} + +// fkValues returns the types for scanning foreign-keys values from sql.Rows. +func (*User) fkValues() []interface{} { + return []interface{}{ + &sql.NullInt64{}, // user_spouse_id } } // assignValues assigns the values that were returned from sql.Rows (after scanning) // to the User fields. func (u *User) assignValues(values ...interface{}) error { - if m, n := len(values), len(user.Columns); m != n { + if m, n := len(values), len(user.Columns); m < n { return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) } value, ok := values[0].(*sql.NullInt64) @@ -56,6 +70,15 @@ func (u *User) assignValues(values ...interface{}) error { } else if value.Valid { u.Name = value.String } + values = values[2:] + if len(values) == len(user.ForeignKeys) { + if value, ok := values[0].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for edge-field user_spouse_id", value) + } else if value.Valid { + u.user_spouse_id = new(int) + *u.user_spouse_id = int(value.Int64) + } + } return nil } diff --git a/examples/o2obidi/ent/user/user.go b/examples/o2obidi/ent/user/user.go index 48b6d17e9..4a184870c 100644 --- a/examples/o2obidi/ent/user/user.go +++ b/examples/o2obidi/ent/user/user.go @@ -24,9 +24,14 @@ const ( SpouseColumn = "user_spouse_id" ) -// Columns holds all SQL columns are user fields. +// Columns holds all SQL columns for user fields. var Columns = []string{ FieldID, FieldAge, FieldName, } + +// ForeignKeys holds the SQL foreign-keys that are owned by the User type. +var ForeignKeys = []string{ + "user_spouse_id", +} diff --git a/examples/o2obidi/ent/user_query.go b/examples/o2obidi/ent/user_query.go index d4c01ee48..2696a7c1d 100644 --- a/examples/o2obidi/ent/user_query.go +++ b/examples/o2obidi/ent/user_query.go @@ -27,6 +27,9 @@ type UserQuery struct { order []Order unique []string predicates []predicate.User + // eager-loading edges. + withSpouse *UserQuery + withFKs bool // intermediate query. sql *sql.Selector } @@ -236,6 +239,17 @@ func (uq *UserQuery) Clone() *UserQuery { } } +// WithSpouse tells the query-builder to eager-loads the nodes that are connected to +// the "spouse" edge. The optional arguments used to configure the query builder of the edge. +func (uq *UserQuery) WithSpouse(opts ...func(*UserQuery)) *UserQuery { + query := &UserQuery{config: uq.config} + for _, opt := range opts { + opt(query) + } + uq.withSpouse = query + return uq +} + // GroupBy used to group vertices by one or more fields/columns. // It is often used with aggregate functions, like: count, max, mean, min, sum. // @@ -279,13 +293,24 @@ func (uq *UserQuery) Select(field string, fields ...string) *UserSelect { func (uq *UserQuery) sqlAll(ctx context.Context) ([]*User, error) { var ( - nodes []*User - spec = uq.querySpec() + nodes []*User + withFKs = uq.withFKs + spec = uq.querySpec() ) + if uq.withSpouse != nil { + withFKs = true + } + if withFKs { + spec.Node.Columns = append(spec.Node.Columns, user.ForeignKeys...) + } spec.ScanValues = func() []interface{} { node := &User{config: uq.config} nodes = append(nodes, node) - return node.scanValues() + values := node.scanValues() + if withFKs { + values = append(values, node.fkValues()...) + } + return values } spec.Assign = func(values ...interface{}) error { if len(nodes) == 0 { @@ -297,6 +322,32 @@ func (uq *UserQuery) sqlAll(ctx context.Context) ([]*User, error) { if err := sqlgraph.QueryNodes(ctx, uq.driver, spec); err != nil { return nil, err } + + if query := uq.withSpouse; query != nil { + ids := make([]int, 0, len(nodes)) + nodeids := make(map[int][]*User) + for i := range nodes { + if fk := nodes[i].user_spouse_id; fk != nil { + ids = append(ids, *fk) + nodeids[*fk] = append(nodeids[*fk], nodes[i]) + } + } + query.Where(user.IDIn(ids...)) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + nodes, ok := nodeids[n.ID] + if !ok { + return nil, fmt.Errorf(`unexpected foreign-key "user_spouse_id" returned %v`, n.ID) + } + for i := range nodes { + nodes[i].Edges.Spouse = n + } + } + } + return nodes, nil } diff --git a/examples/o2orecur/ent/node.go b/examples/o2orecur/ent/node.go index fdd708fba..2337c3c89 100644 --- a/examples/o2orecur/ent/node.go +++ b/examples/o2orecur/ent/node.go @@ -21,20 +21,36 @@ type Node struct { ID int `json:"id,omitempty"` // Value holds the value of the "value" field. Value int `json:"value,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the NodeQuery when eager-loading is set. + Edges struct { + // Prev holds the value of the prev edge. + Prev *Node + // Next holds the value of the next edge. + Next *Node + } + prev_id *int } // scanValues returns the types for scanning values from sql.Rows. func (*Node) scanValues() []interface{} { return []interface{}{ - &sql.NullInt64{}, - &sql.NullInt64{}, + &sql.NullInt64{}, // id + &sql.NullInt64{}, // value + } +} + +// fkValues returns the types for scanning foreign-keys values from sql.Rows. +func (*Node) fkValues() []interface{} { + return []interface{}{ + &sql.NullInt64{}, // prev_id } } // assignValues assigns the values that were returned from sql.Rows (after scanning) // to the Node fields. func (n *Node) assignValues(values ...interface{}) error { - if m, n := len(values), len(node.Columns); m != n { + if m, n := len(values), len(node.Columns); m < n { return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) } value, ok := values[0].(*sql.NullInt64) @@ -48,6 +64,15 @@ func (n *Node) assignValues(values ...interface{}) error { } else if value.Valid { n.Value = int(value.Int64) } + values = values[1:] + if len(values) == len(node.ForeignKeys) { + if value, ok := values[0].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for edge-field prev_id", value) + } else if value.Valid { + n.prev_id = new(int) + *n.prev_id = int(value.Int64) + } + } return nil } diff --git a/examples/o2orecur/ent/node/node.go b/examples/o2orecur/ent/node/node.go index 64bbdf029..dfc9ae0d2 100644 --- a/examples/o2orecur/ent/node/node.go +++ b/examples/o2orecur/ent/node/node.go @@ -26,8 +26,13 @@ const ( NextColumn = "prev_id" ) -// Columns holds all SQL columns are node fields. +// Columns holds all SQL columns for node fields. var Columns = []string{ FieldID, FieldValue, } + +// ForeignKeys holds the SQL foreign-keys that are owned by the Node type. +var ForeignKeys = []string{ + "prev_id", +} diff --git a/examples/o2orecur/ent/node_query.go b/examples/o2orecur/ent/node_query.go index 79053f401..ca1acbd99 100644 --- a/examples/o2orecur/ent/node_query.go +++ b/examples/o2orecur/ent/node_query.go @@ -8,6 +8,7 @@ package ent import ( "context" + "database/sql/driver" "errors" "fmt" "math" @@ -27,6 +28,10 @@ type NodeQuery struct { order []Order unique []string predicates []predicate.Node + // eager-loading edges. + withPrev *NodeQuery + withNext *NodeQuery + withFKs bool // intermediate query. sql *sql.Selector } @@ -248,6 +253,28 @@ func (nq *NodeQuery) Clone() *NodeQuery { } } +// WithPrev tells the query-builder to eager-loads the nodes that are connected to +// the "prev" edge. The optional arguments used to configure the query builder of the edge. +func (nq *NodeQuery) WithPrev(opts ...func(*NodeQuery)) *NodeQuery { + query := &NodeQuery{config: nq.config} + for _, opt := range opts { + opt(query) + } + nq.withPrev = query + return nq +} + +// WithNext tells the query-builder to eager-loads the nodes that are connected to +// the "next" edge. The optional arguments used to configure the query builder of the edge. +func (nq *NodeQuery) WithNext(opts ...func(*NodeQuery)) *NodeQuery { + query := &NodeQuery{config: nq.config} + for _, opt := range opts { + opt(query) + } + nq.withNext = query + return nq +} + // GroupBy used to group vertices by one or more fields/columns. // It is often used with aggregate functions, like: count, max, mean, min, sum. // @@ -291,13 +318,24 @@ func (nq *NodeQuery) Select(field string, fields ...string) *NodeSelect { func (nq *NodeQuery) sqlAll(ctx context.Context) ([]*Node, error) { var ( - nodes []*Node - spec = nq.querySpec() + nodes []*Node + withFKs = nq.withFKs + spec = nq.querySpec() ) + if nq.withPrev != nil { + withFKs = true + } + if withFKs { + spec.Node.Columns = append(spec.Node.Columns, node.ForeignKeys...) + } spec.ScanValues = func() []interface{} { node := &Node{config: nq.config} nodes = append(nodes, node) - return node.scanValues() + values := node.scanValues() + if withFKs { + values = append(values, node.fkValues()...) + } + return values } spec.Assign = func(values ...interface{}) error { if len(nodes) == 0 { @@ -309,6 +347,60 @@ func (nq *NodeQuery) sqlAll(ctx context.Context) ([]*Node, error) { if err := sqlgraph.QueryNodes(ctx, nq.driver, spec); err != nil { return nil, err } + + if query := nq.withPrev; query != nil { + ids := make([]int, 0, len(nodes)) + nodeids := make(map[int][]*Node) + for i := range nodes { + if fk := nodes[i].prev_id; fk != nil { + ids = append(ids, *fk) + nodeids[*fk] = append(nodeids[*fk], nodes[i]) + } + } + query.Where(node.IDIn(ids...)) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + nodes, ok := nodeids[n.ID] + if !ok { + return nil, fmt.Errorf(`unexpected foreign-key "prev_id" returned %v`, n.ID) + } + for i := range nodes { + nodes[i].Edges.Prev = n + } + } + } + + if query := nq.withNext; query != nil { + fks := make([]driver.Value, 0, len(nodes)) + nodeids := make(map[int]*Node) + for i := range nodes { + fks = append(fks, nodes[i].ID) + nodeids[nodes[i].ID] = nodes[i] + } + query.withFKs = true + query.Where(predicate.Node(func(s *sql.Selector) { + s.Where(sql.InValues(node.NextColumn, fks...)) + })) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + fk := n.prev_id + if fk == nil { + return nil, fmt.Errorf(`foreign-key "prev_id" is nil for node %v`, n.ID) + } + node, ok := nodeids[*fk] + if !ok { + return nil, fmt.Errorf(`unexpected foreign-key "prev_id" returned %v for node %v`, *fk, n.ID) + } + node.Edges.Next = n + } + } + return nodes, nil } diff --git a/examples/start/ent/car.go b/examples/start/ent/car.go index c9281d2cf..be9f808e3 100644 --- a/examples/start/ent/car.go +++ b/examples/start/ent/car.go @@ -24,21 +24,35 @@ type Car struct { Model string `json:"model,omitempty"` // RegisteredAt holds the value of the "registered_at" field. RegisteredAt time.Time `json:"registered_at,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the CarQuery when eager-loading is set. + Edges struct { + // Owner holds the value of the owner edge. + Owner *User + } + owner_id *int } // scanValues returns the types for scanning values from sql.Rows. func (*Car) scanValues() []interface{} { return []interface{}{ - &sql.NullInt64{}, - &sql.NullString{}, - &sql.NullTime{}, + &sql.NullInt64{}, // id + &sql.NullString{}, // model + &sql.NullTime{}, // registered_at + } +} + +// fkValues returns the types for scanning foreign-keys values from sql.Rows. +func (*Car) fkValues() []interface{} { + return []interface{}{ + &sql.NullInt64{}, // owner_id } } // assignValues assigns the values that were returned from sql.Rows (after scanning) // to the Car fields. func (c *Car) assignValues(values ...interface{}) error { - if m, n := len(values), len(car.Columns); m != n { + if m, n := len(values), len(car.Columns); m < n { return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) } value, ok := values[0].(*sql.NullInt64) @@ -57,6 +71,15 @@ func (c *Car) assignValues(values ...interface{}) error { } else if value.Valid { c.RegisteredAt = value.Time } + values = values[2:] + if len(values) == len(car.ForeignKeys) { + if value, ok := values[0].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for edge-field owner_id", value) + } else if value.Valid { + c.owner_id = new(int) + *c.owner_id = int(value.Int64) + } + } return nil } diff --git a/examples/start/ent/car/car.go b/examples/start/ent/car/car.go index f53125a16..c7a4199b0 100644 --- a/examples/start/ent/car/car.go +++ b/examples/start/ent/car/car.go @@ -27,9 +27,14 @@ const ( OwnerColumn = "owner_id" ) -// Columns holds all SQL columns are car fields. +// Columns holds all SQL columns for car fields. var Columns = []string{ FieldID, FieldModel, FieldRegisteredAt, } + +// ForeignKeys holds the SQL foreign-keys that are owned by the Car type. +var ForeignKeys = []string{ + "owner_id", +} diff --git a/examples/start/ent/car_query.go b/examples/start/ent/car_query.go index 86a507232..30706eff8 100644 --- a/examples/start/ent/car_query.go +++ b/examples/start/ent/car_query.go @@ -28,6 +28,9 @@ type CarQuery struct { order []Order unique []string predicates []predicate.Car + // eager-loading edges. + withOwner *UserQuery + withFKs bool // intermediate query. sql *sql.Selector } @@ -237,6 +240,17 @@ func (cq *CarQuery) Clone() *CarQuery { } } +// WithOwner tells the query-builder to eager-loads the nodes that are connected to +// the "owner" edge. The optional arguments used to configure the query builder of the edge. +func (cq *CarQuery) WithOwner(opts ...func(*UserQuery)) *CarQuery { + query := &UserQuery{config: cq.config} + for _, opt := range opts { + opt(query) + } + cq.withOwner = query + return cq +} + // GroupBy used to group vertices by one or more fields/columns. // It is often used with aggregate functions, like: count, max, mean, min, sum. // @@ -280,13 +294,24 @@ func (cq *CarQuery) Select(field string, fields ...string) *CarSelect { func (cq *CarQuery) sqlAll(ctx context.Context) ([]*Car, error) { var ( - nodes []*Car - spec = cq.querySpec() + nodes []*Car + withFKs = cq.withFKs + spec = cq.querySpec() ) + if cq.withOwner != nil { + withFKs = true + } + if withFKs { + spec.Node.Columns = append(spec.Node.Columns, car.ForeignKeys...) + } spec.ScanValues = func() []interface{} { node := &Car{config: cq.config} nodes = append(nodes, node) - return node.scanValues() + values := node.scanValues() + if withFKs { + values = append(values, node.fkValues()...) + } + return values } spec.Assign = func(values ...interface{}) error { if len(nodes) == 0 { @@ -298,6 +323,32 @@ func (cq *CarQuery) sqlAll(ctx context.Context) ([]*Car, error) { if err := sqlgraph.QueryNodes(ctx, cq.driver, spec); err != nil { return nil, err } + + if query := cq.withOwner; query != nil { + ids := make([]int, 0, len(nodes)) + nodeids := make(map[int][]*Car) + for i := range nodes { + if fk := nodes[i].owner_id; fk != nil { + ids = append(ids, *fk) + nodeids[*fk] = append(nodeids[*fk], nodes[i]) + } + } + query.Where(user.IDIn(ids...)) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + nodes, ok := nodeids[n.ID] + if !ok { + return nil, fmt.Errorf(`unexpected foreign-key "owner_id" returned %v`, n.ID) + } + for i := range nodes { + nodes[i].Edges.Owner = n + } + } + } + return nodes, nil } diff --git a/examples/start/ent/group.go b/examples/start/ent/group.go index 15b92a849..be63494bd 100644 --- a/examples/start/ent/group.go +++ b/examples/start/ent/group.go @@ -21,20 +21,26 @@ type Group struct { ID int `json:"id,omitempty"` // Name holds the value of the "name" field. Name string `json:"name,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the GroupQuery when eager-loading is set. + Edges struct { + // Users holds the value of the users edge. + Users []*User + } } // scanValues returns the types for scanning values from sql.Rows. func (*Group) scanValues() []interface{} { return []interface{}{ - &sql.NullInt64{}, - &sql.NullString{}, + &sql.NullInt64{}, // id + &sql.NullString{}, // name } } // assignValues assigns the values that were returned from sql.Rows (after scanning) // to the Group fields. func (gr *Group) assignValues(values ...interface{}) error { - if m, n := len(values), len(group.Columns); m != n { + if m, n := len(values), len(group.Columns); m < n { return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) } value, ok := values[0].(*sql.NullInt64) diff --git a/examples/start/ent/group/group.go b/examples/start/ent/group/group.go index 6f76405e0..f20971a63 100644 --- a/examples/start/ent/group/group.go +++ b/examples/start/ent/group/group.go @@ -27,7 +27,7 @@ const ( UsersInverseTable = "users" ) -// Columns holds all SQL columns are group fields. +// Columns holds all SQL columns for group fields. var Columns = []string{ FieldID, FieldName, diff --git a/examples/start/ent/group_query.go b/examples/start/ent/group_query.go index fc166621a..e2a08c5c8 100644 --- a/examples/start/ent/group_query.go +++ b/examples/start/ent/group_query.go @@ -8,6 +8,7 @@ package ent import ( "context" + "database/sql/driver" "errors" "fmt" "math" @@ -28,6 +29,8 @@ type GroupQuery struct { order []Order unique []string predicates []predicate.Group + // eager-loading edges. + withUsers *UserQuery // intermediate query. sql *sql.Selector } @@ -237,6 +240,17 @@ func (gq *GroupQuery) Clone() *GroupQuery { } } +// WithUsers tells the query-builder to eager-loads the nodes that are connected to +// the "users" edge. The optional arguments used to configure the query builder of the edge. +func (gq *GroupQuery) WithUsers(opts ...func(*UserQuery)) *GroupQuery { + query := &UserQuery{config: gq.config} + for _, opt := range opts { + opt(query) + } + gq.withUsers = query + return gq +} + // GroupBy used to group vertices by one or more fields/columns. // It is often used with aggregate functions, like: count, max, mean, min, sum. // @@ -286,7 +300,8 @@ func (gq *GroupQuery) sqlAll(ctx context.Context) ([]*Group, error) { spec.ScanValues = func() []interface{} { node := &Group{config: gq.config} nodes = append(nodes, node) - return node.scanValues() + values := node.scanValues() + return values } spec.Assign = func(values ...interface{}) error { if len(nodes) == 0 { @@ -298,6 +313,70 @@ func (gq *GroupQuery) sqlAll(ctx context.Context) ([]*Group, error) { if err := sqlgraph.QueryNodes(ctx, gq.driver, spec); err != nil { return nil, err } + + if query := gq.withUsers; query != nil { + fks := make([]driver.Value, 0, len(nodes)) + ids := make(map[int]*Group, len(nodes)) + for _, node := range nodes { + ids[node.ID] = node + fks = append(fks, node.ID) + } + var ( + edgeids []int + edges = make(map[int][]*Group) + ) + spec := &sqlgraph.EdgeQuerySpec{ + Edge: &sqlgraph.EdgeSpec{ + Inverse: false, + Table: group.UsersTable, + Columns: group.UsersPrimaryKey, + }, + Predicate: func(s *sql.Selector) { + s.Where(sql.InValues(group.UsersPrimaryKey[0], fks...)) + }, + + ScanValues: func() [2]interface{} { + return [2]interface{}{&sql.NullInt64{}, &sql.NullInt64{}} + }, + Assign: func(out, in interface{}) error { + eout, ok := out.(*sql.NullInt64) + if !ok || eout == nil { + return fmt.Errorf("unexpected id value for edge-out") + } + ein, ok := in.(*sql.NullInt64) + if !ok || ein == nil { + return fmt.Errorf("unexpected id value for edge-in") + } + outValue := int(eout.Int64) + inValue := int(eout.Int64) + node, ok := ids[outValue] + if !ok { + return fmt.Errorf("unexpected node id in edges: %v", outValue) + } + edgeids = append(edgeids, inValue) + edges[inValue] = append(edges[inValue], node) + return nil + }, + } + if err := sqlgraph.QueryEdges(ctx, gq.driver, spec); err != nil { + return nil, fmt.Errorf(`query edges "users": %v`, err) + } + query.Where(user.IDIn(edgeids...)) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + nodes, ok := edges[n.ID] + if !ok { + return nil, fmt.Errorf(`unexpected "users" node returned %v`, n.ID) + } + for i := range nodes { + nodes[i].Edges.Users = append(nodes[i].Edges.Users, n) + } + } + } + return nodes, nil } diff --git a/examples/start/ent/user.go b/examples/start/ent/user.go index 1fbd500e8..4090630b2 100644 --- a/examples/start/ent/user.go +++ b/examples/start/ent/user.go @@ -23,21 +23,29 @@ type User struct { Age int `json:"age,omitempty"` // Name holds the value of the "name" field. Name string `json:"name,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the UserQuery when eager-loading is set. + Edges struct { + // Cars holds the value of the cars edge. + Cars []*Car + // Groups holds the value of the groups edge. + Groups []*Group + } } // scanValues returns the types for scanning values from sql.Rows. func (*User) scanValues() []interface{} { return []interface{}{ - &sql.NullInt64{}, - &sql.NullInt64{}, - &sql.NullString{}, + &sql.NullInt64{}, // id + &sql.NullInt64{}, // age + &sql.NullString{}, // name } } // assignValues assigns the values that were returned from sql.Rows (after scanning) // to the User fields. func (u *User) assignValues(values ...interface{}) error { - if m, n := len(values), len(user.Columns); m != n { + if m, n := len(values), len(user.Columns); m < n { return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) } value, ok := values[0].(*sql.NullInt64) diff --git a/examples/start/ent/user/user.go b/examples/start/ent/user/user.go index eebc02d7b..92046e20c 100644 --- a/examples/start/ent/user/user.go +++ b/examples/start/ent/user/user.go @@ -36,7 +36,7 @@ const ( GroupsInverseTable = "groups" ) -// Columns holds all SQL columns are user fields. +// Columns holds all SQL columns for user fields. var Columns = []string{ FieldID, FieldAge, diff --git a/examples/start/ent/user_query.go b/examples/start/ent/user_query.go index c706064c5..18bb1e7b3 100644 --- a/examples/start/ent/user_query.go +++ b/examples/start/ent/user_query.go @@ -8,6 +8,7 @@ package ent import ( "context" + "database/sql/driver" "errors" "fmt" "math" @@ -29,6 +30,9 @@ type UserQuery struct { order []Order unique []string predicates []predicate.User + // eager-loading edges. + withCars *CarQuery + withGroups *GroupQuery // intermediate query. sql *sql.Selector } @@ -250,6 +254,28 @@ func (uq *UserQuery) Clone() *UserQuery { } } +// WithCars tells the query-builder to eager-loads the nodes that are connected to +// the "cars" edge. The optional arguments used to configure the query builder of the edge. +func (uq *UserQuery) WithCars(opts ...func(*CarQuery)) *UserQuery { + query := &CarQuery{config: uq.config} + for _, opt := range opts { + opt(query) + } + uq.withCars = query + return uq +} + +// WithGroups tells the query-builder to eager-loads the nodes that are connected to +// the "groups" edge. The optional arguments used to configure the query builder of the edge. +func (uq *UserQuery) WithGroups(opts ...func(*GroupQuery)) *UserQuery { + query := &GroupQuery{config: uq.config} + for _, opt := range opts { + opt(query) + } + uq.withGroups = query + return uq +} + // GroupBy used to group vertices by one or more fields/columns. // It is often used with aggregate functions, like: count, max, mean, min, sum. // @@ -299,7 +325,8 @@ func (uq *UserQuery) sqlAll(ctx context.Context) ([]*User, error) { spec.ScanValues = func() []interface{} { node := &User{config: uq.config} nodes = append(nodes, node) - return node.scanValues() + values := node.scanValues() + return values } spec.Assign = func(values ...interface{}) error { if len(nodes) == 0 { @@ -311,6 +338,98 @@ func (uq *UserQuery) sqlAll(ctx context.Context) ([]*User, error) { if err := sqlgraph.QueryNodes(ctx, uq.driver, spec); err != nil { return nil, err } + + if query := uq.withCars; query != nil { + fks := make([]driver.Value, 0, len(nodes)) + nodeids := make(map[int]*User) + for i := range nodes { + fks = append(fks, nodes[i].ID) + nodeids[nodes[i].ID] = nodes[i] + } + query.withFKs = true + query.Where(predicate.Car(func(s *sql.Selector) { + s.Where(sql.InValues(user.CarsColumn, fks...)) + })) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + fk := n.owner_id + if fk == nil { + return nil, fmt.Errorf(`foreign-key "owner_id" is nil for node %v`, n.ID) + } + node, ok := nodeids[*fk] + if !ok { + return nil, fmt.Errorf(`unexpected foreign-key "owner_id" returned %v for node %v`, *fk, n.ID) + } + node.Edges.Cars = append(node.Edges.Cars, n) + } + } + + if query := uq.withGroups; query != nil { + fks := make([]driver.Value, 0, len(nodes)) + ids := make(map[int]*User, len(nodes)) + for _, node := range nodes { + ids[node.ID] = node + fks = append(fks, node.ID) + } + var ( + edgeids []int + edges = make(map[int][]*User) + ) + spec := &sqlgraph.EdgeQuerySpec{ + Edge: &sqlgraph.EdgeSpec{ + Inverse: true, + Table: user.GroupsTable, + Columns: user.GroupsPrimaryKey, + }, + Predicate: func(s *sql.Selector) { + s.Where(sql.InValues(user.GroupsPrimaryKey[1], fks...)) + }, + + ScanValues: func() [2]interface{} { + return [2]interface{}{&sql.NullInt64{}, &sql.NullInt64{}} + }, + Assign: func(out, in interface{}) error { + eout, ok := out.(*sql.NullInt64) + if !ok || eout == nil { + return fmt.Errorf("unexpected id value for edge-out") + } + ein, ok := in.(*sql.NullInt64) + if !ok || ein == nil { + return fmt.Errorf("unexpected id value for edge-in") + } + outValue := int(eout.Int64) + inValue := int(eout.Int64) + node, ok := ids[outValue] + if !ok { + return fmt.Errorf("unexpected node id in edges: %v", outValue) + } + edgeids = append(edgeids, inValue) + edges[inValue] = append(edges[inValue], node) + return nil + }, + } + if err := sqlgraph.QueryEdges(ctx, uq.driver, spec); err != nil { + return nil, fmt.Errorf(`query edges "groups": %v`, err) + } + query.Where(group.IDIn(edgeids...)) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + nodes, ok := edges[n.ID] + if !ok { + return nil, fmt.Errorf(`unexpected "groups" node returned %v`, n.ID) + } + for i := range nodes { + nodes[i].Edges.Groups = append(nodes[i].Edges.Groups, n) + } + } + } + return nodes, nil } diff --git a/examples/traversal/ent/group.go b/examples/traversal/ent/group.go index 9f65c54c4..2ad651619 100644 --- a/examples/traversal/ent/group.go +++ b/examples/traversal/ent/group.go @@ -21,20 +21,36 @@ type Group struct { ID int `json:"id,omitempty"` // Name holds the value of the "name" field. Name string `json:"name,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the GroupQuery when eager-loading is set. + Edges struct { + // Users holds the value of the users edge. + Users []*User + // Admin holds the value of the admin edge. + Admin *User + } + admin_id *int } // scanValues returns the types for scanning values from sql.Rows. func (*Group) scanValues() []interface{} { return []interface{}{ - &sql.NullInt64{}, - &sql.NullString{}, + &sql.NullInt64{}, // id + &sql.NullString{}, // name + } +} + +// fkValues returns the types for scanning foreign-keys values from sql.Rows. +func (*Group) fkValues() []interface{} { + return []interface{}{ + &sql.NullInt64{}, // admin_id } } // assignValues assigns the values that were returned from sql.Rows (after scanning) // to the Group fields. func (gr *Group) assignValues(values ...interface{}) error { - if m, n := len(values), len(group.Columns); m != n { + if m, n := len(values), len(group.Columns); m < n { return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) } value, ok := values[0].(*sql.NullInt64) @@ -48,6 +64,15 @@ func (gr *Group) assignValues(values ...interface{}) error { } else if value.Valid { gr.Name = value.String } + values = values[1:] + if len(values) == len(group.ForeignKeys) { + if value, ok := values[0].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for edge-field admin_id", value) + } else if value.Valid { + gr.admin_id = new(int) + *gr.admin_id = int(value.Int64) + } + } return nil } diff --git a/examples/traversal/ent/group/group.go b/examples/traversal/ent/group/group.go index a0e487cb6..32bb01cf0 100644 --- a/examples/traversal/ent/group/group.go +++ b/examples/traversal/ent/group/group.go @@ -30,12 +30,17 @@ const ( AdminColumn = "admin_id" ) -// Columns holds all SQL columns are group fields. +// Columns holds all SQL columns for group fields. var Columns = []string{ FieldID, FieldName, } +// ForeignKeys holds the SQL foreign-keys that are owned by the Group type. +var ForeignKeys = []string{ + "admin_id", +} + var ( // UsersPrimaryKey and UsersColumn2 are the table columns denoting the // primary key for the users relation (M2M). diff --git a/examples/traversal/ent/group_query.go b/examples/traversal/ent/group_query.go index 00bb879ae..641d1e52f 100644 --- a/examples/traversal/ent/group_query.go +++ b/examples/traversal/ent/group_query.go @@ -8,6 +8,7 @@ package ent import ( "context" + "database/sql/driver" "errors" "fmt" "math" @@ -28,6 +29,10 @@ type GroupQuery struct { order []Order unique []string predicates []predicate.Group + // eager-loading edges. + withUsers *UserQuery + withAdmin *UserQuery + withFKs bool // intermediate query. sql *sql.Selector } @@ -249,6 +254,28 @@ func (gq *GroupQuery) Clone() *GroupQuery { } } +// WithUsers tells the query-builder to eager-loads the nodes that are connected to +// the "users" edge. The optional arguments used to configure the query builder of the edge. +func (gq *GroupQuery) WithUsers(opts ...func(*UserQuery)) *GroupQuery { + query := &UserQuery{config: gq.config} + for _, opt := range opts { + opt(query) + } + gq.withUsers = query + return gq +} + +// WithAdmin tells the query-builder to eager-loads the nodes that are connected to +// the "admin" edge. The optional arguments used to configure the query builder of the edge. +func (gq *GroupQuery) WithAdmin(opts ...func(*UserQuery)) *GroupQuery { + query := &UserQuery{config: gq.config} + for _, opt := range opts { + opt(query) + } + gq.withAdmin = query + return gq +} + // GroupBy used to group vertices by one or more fields/columns. // It is often used with aggregate functions, like: count, max, mean, min, sum. // @@ -292,13 +319,24 @@ func (gq *GroupQuery) Select(field string, fields ...string) *GroupSelect { func (gq *GroupQuery) sqlAll(ctx context.Context) ([]*Group, error) { var ( - nodes []*Group - spec = gq.querySpec() + nodes []*Group + withFKs = gq.withFKs + spec = gq.querySpec() ) + if gq.withAdmin != nil { + withFKs = true + } + if withFKs { + spec.Node.Columns = append(spec.Node.Columns, group.ForeignKeys...) + } spec.ScanValues = func() []interface{} { node := &Group{config: gq.config} nodes = append(nodes, node) - return node.scanValues() + values := node.scanValues() + if withFKs { + values = append(values, node.fkValues()...) + } + return values } spec.Assign = func(values ...interface{}) error { if len(nodes) == 0 { @@ -310,6 +348,95 @@ func (gq *GroupQuery) sqlAll(ctx context.Context) ([]*Group, error) { if err := sqlgraph.QueryNodes(ctx, gq.driver, spec); err != nil { return nil, err } + + if query := gq.withUsers; query != nil { + fks := make([]driver.Value, 0, len(nodes)) + ids := make(map[int]*Group, len(nodes)) + for _, node := range nodes { + ids[node.ID] = node + fks = append(fks, node.ID) + } + var ( + edgeids []int + edges = make(map[int][]*Group) + ) + spec := &sqlgraph.EdgeQuerySpec{ + Edge: &sqlgraph.EdgeSpec{ + Inverse: false, + Table: group.UsersTable, + Columns: group.UsersPrimaryKey, + }, + Predicate: func(s *sql.Selector) { + s.Where(sql.InValues(group.UsersPrimaryKey[0], fks...)) + }, + + ScanValues: func() [2]interface{} { + return [2]interface{}{&sql.NullInt64{}, &sql.NullInt64{}} + }, + Assign: func(out, in interface{}) error { + eout, ok := out.(*sql.NullInt64) + if !ok || eout == nil { + return fmt.Errorf("unexpected id value for edge-out") + } + ein, ok := in.(*sql.NullInt64) + if !ok || ein == nil { + return fmt.Errorf("unexpected id value for edge-in") + } + outValue := int(eout.Int64) + inValue := int(eout.Int64) + node, ok := ids[outValue] + if !ok { + return fmt.Errorf("unexpected node id in edges: %v", outValue) + } + edgeids = append(edgeids, inValue) + edges[inValue] = append(edges[inValue], node) + return nil + }, + } + if err := sqlgraph.QueryEdges(ctx, gq.driver, spec); err != nil { + return nil, fmt.Errorf(`query edges "users": %v`, err) + } + query.Where(user.IDIn(edgeids...)) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + nodes, ok := edges[n.ID] + if !ok { + return nil, fmt.Errorf(`unexpected "users" node returned %v`, n.ID) + } + for i := range nodes { + nodes[i].Edges.Users = append(nodes[i].Edges.Users, n) + } + } + } + + if query := gq.withAdmin; query != nil { + ids := make([]int, 0, len(nodes)) + nodeids := make(map[int][]*Group) + for i := range nodes { + if fk := nodes[i].admin_id; fk != nil { + ids = append(ids, *fk) + nodeids[*fk] = append(nodeids[*fk], nodes[i]) + } + } + query.Where(user.IDIn(ids...)) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + nodes, ok := nodeids[n.ID] + if !ok { + return nil, fmt.Errorf(`unexpected foreign-key "admin_id" returned %v`, n.ID) + } + for i := range nodes { + nodes[i].Edges.Admin = n + } + } + } + return nodes, nil } diff --git a/examples/traversal/ent/pet.go b/examples/traversal/ent/pet.go index dd624cbe2..b6e21b2dc 100644 --- a/examples/traversal/ent/pet.go +++ b/examples/traversal/ent/pet.go @@ -21,20 +21,36 @@ type Pet struct { ID int `json:"id,omitempty"` // Name holds the value of the "name" field. Name string `json:"name,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the PetQuery when eager-loading is set. + Edges struct { + // Friends holds the value of the friends edge. + Friends []*Pet + // Owner holds the value of the owner edge. + Owner *User + } + owner_id *int } // scanValues returns the types for scanning values from sql.Rows. func (*Pet) scanValues() []interface{} { return []interface{}{ - &sql.NullInt64{}, - &sql.NullString{}, + &sql.NullInt64{}, // id + &sql.NullString{}, // name + } +} + +// fkValues returns the types for scanning foreign-keys values from sql.Rows. +func (*Pet) fkValues() []interface{} { + return []interface{}{ + &sql.NullInt64{}, // owner_id } } // assignValues assigns the values that were returned from sql.Rows (after scanning) // to the Pet fields. func (pe *Pet) assignValues(values ...interface{}) error { - if m, n := len(values), len(pet.Columns); m != n { + if m, n := len(values), len(pet.Columns); m < n { return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) } value, ok := values[0].(*sql.NullInt64) @@ -48,6 +64,15 @@ func (pe *Pet) assignValues(values ...interface{}) error { } else if value.Valid { pe.Name = value.String } + values = values[1:] + if len(values) == len(pet.ForeignKeys) { + if value, ok := values[0].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for edge-field owner_id", value) + } else if value.Valid { + pe.owner_id = new(int) + *pe.owner_id = int(value.Int64) + } + } return nil } diff --git a/examples/traversal/ent/pet/pet.go b/examples/traversal/ent/pet/pet.go index d0d35939a..223bd0545 100644 --- a/examples/traversal/ent/pet/pet.go +++ b/examples/traversal/ent/pet/pet.go @@ -27,12 +27,17 @@ const ( OwnerColumn = "owner_id" ) -// Columns holds all SQL columns are pet fields. +// Columns holds all SQL columns for pet fields. var Columns = []string{ FieldID, FieldName, } +// ForeignKeys holds the SQL foreign-keys that are owned by the Pet type. +var ForeignKeys = []string{ + "owner_id", +} + var ( // FriendsPrimaryKey and FriendsColumn2 are the table columns denoting the // primary key for the friends relation (M2M). diff --git a/examples/traversal/ent/pet_query.go b/examples/traversal/ent/pet_query.go index a7e19ff97..8ee2c18af 100644 --- a/examples/traversal/ent/pet_query.go +++ b/examples/traversal/ent/pet_query.go @@ -8,6 +8,7 @@ package ent import ( "context" + "database/sql/driver" "errors" "fmt" "math" @@ -28,6 +29,10 @@ type PetQuery struct { order []Order unique []string predicates []predicate.Pet + // eager-loading edges. + withFriends *PetQuery + withOwner *UserQuery + withFKs bool // intermediate query. sql *sql.Selector } @@ -249,6 +254,28 @@ func (pq *PetQuery) Clone() *PetQuery { } } +// WithFriends tells the query-builder to eager-loads the nodes that are connected to +// the "friends" edge. The optional arguments used to configure the query builder of the edge. +func (pq *PetQuery) WithFriends(opts ...func(*PetQuery)) *PetQuery { + query := &PetQuery{config: pq.config} + for _, opt := range opts { + opt(query) + } + pq.withFriends = query + return pq +} + +// WithOwner tells the query-builder to eager-loads the nodes that are connected to +// the "owner" edge. The optional arguments used to configure the query builder of the edge. +func (pq *PetQuery) WithOwner(opts ...func(*UserQuery)) *PetQuery { + query := &UserQuery{config: pq.config} + for _, opt := range opts { + opt(query) + } + pq.withOwner = query + return pq +} + // GroupBy used to group vertices by one or more fields/columns. // It is often used with aggregate functions, like: count, max, mean, min, sum. // @@ -292,13 +319,24 @@ func (pq *PetQuery) Select(field string, fields ...string) *PetSelect { func (pq *PetQuery) sqlAll(ctx context.Context) ([]*Pet, error) { var ( - nodes []*Pet - spec = pq.querySpec() + nodes []*Pet + withFKs = pq.withFKs + spec = pq.querySpec() ) + if pq.withOwner != nil { + withFKs = true + } + if withFKs { + spec.Node.Columns = append(spec.Node.Columns, pet.ForeignKeys...) + } spec.ScanValues = func() []interface{} { node := &Pet{config: pq.config} nodes = append(nodes, node) - return node.scanValues() + values := node.scanValues() + if withFKs { + values = append(values, node.fkValues()...) + } + return values } spec.Assign = func(values ...interface{}) error { if len(nodes) == 0 { @@ -310,6 +348,95 @@ func (pq *PetQuery) sqlAll(ctx context.Context) ([]*Pet, error) { if err := sqlgraph.QueryNodes(ctx, pq.driver, spec); err != nil { return nil, err } + + if query := pq.withFriends; query != nil { + fks := make([]driver.Value, 0, len(nodes)) + ids := make(map[int]*Pet, len(nodes)) + for _, node := range nodes { + ids[node.ID] = node + fks = append(fks, node.ID) + } + var ( + edgeids []int + edges = make(map[int][]*Pet) + ) + spec := &sqlgraph.EdgeQuerySpec{ + Edge: &sqlgraph.EdgeSpec{ + Inverse: false, + Table: pet.FriendsTable, + Columns: pet.FriendsPrimaryKey, + }, + Predicate: func(s *sql.Selector) { + s.Where(sql.InValues(pet.FriendsPrimaryKey[0], fks...)) + }, + + ScanValues: func() [2]interface{} { + return [2]interface{}{&sql.NullInt64{}, &sql.NullInt64{}} + }, + Assign: func(out, in interface{}) error { + eout, ok := out.(*sql.NullInt64) + if !ok || eout == nil { + return fmt.Errorf("unexpected id value for edge-out") + } + ein, ok := in.(*sql.NullInt64) + if !ok || ein == nil { + return fmt.Errorf("unexpected id value for edge-in") + } + outValue := int(eout.Int64) + inValue := int(eout.Int64) + node, ok := ids[outValue] + if !ok { + return fmt.Errorf("unexpected node id in edges: %v", outValue) + } + edgeids = append(edgeids, inValue) + edges[inValue] = append(edges[inValue], node) + return nil + }, + } + if err := sqlgraph.QueryEdges(ctx, pq.driver, spec); err != nil { + return nil, fmt.Errorf(`query edges "friends": %v`, err) + } + query.Where(pet.IDIn(edgeids...)) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + nodes, ok := edges[n.ID] + if !ok { + return nil, fmt.Errorf(`unexpected "friends" node returned %v`, n.ID) + } + for i := range nodes { + nodes[i].Edges.Friends = append(nodes[i].Edges.Friends, n) + } + } + } + + if query := pq.withOwner; query != nil { + ids := make([]int, 0, len(nodes)) + nodeids := make(map[int][]*Pet) + for i := range nodes { + if fk := nodes[i].owner_id; fk != nil { + ids = append(ids, *fk) + nodeids[*fk] = append(nodeids[*fk], nodes[i]) + } + } + query.Where(user.IDIn(ids...)) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + nodes, ok := nodeids[n.ID] + if !ok { + return nil, fmt.Errorf(`unexpected foreign-key "owner_id" returned %v`, n.ID) + } + for i := range nodes { + nodes[i].Edges.Owner = n + } + } + } + return nodes, nil } diff --git a/examples/traversal/ent/user.go b/examples/traversal/ent/user.go index a44248c2f..30fe50b51 100644 --- a/examples/traversal/ent/user.go +++ b/examples/traversal/ent/user.go @@ -23,21 +23,33 @@ type User struct { Age int `json:"age,omitempty"` // Name holds the value of the "name" field. Name string `json:"name,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the UserQuery when eager-loading is set. + Edges struct { + // Pets holds the value of the pets edge. + Pets []*Pet + // Friends holds the value of the friends edge. + Friends []*User + // Groups holds the value of the groups edge. + Groups []*Group + // Manage holds the value of the manage edge. + Manage []*Group + } } // scanValues returns the types for scanning values from sql.Rows. func (*User) scanValues() []interface{} { return []interface{}{ - &sql.NullInt64{}, - &sql.NullInt64{}, - &sql.NullString{}, + &sql.NullInt64{}, // id + &sql.NullInt64{}, // age + &sql.NullString{}, // name } } // assignValues assigns the values that were returned from sql.Rows (after scanning) // to the User fields. func (u *User) assignValues(values ...interface{}) error { - if m, n := len(values), len(user.Columns); m != n { + if m, n := len(values), len(user.Columns); m < n { return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) } value, ok := values[0].(*sql.NullInt64) diff --git a/examples/traversal/ent/user/user.go b/examples/traversal/ent/user/user.go index e3b5c50cc..5cc0f571b 100644 --- a/examples/traversal/ent/user/user.go +++ b/examples/traversal/ent/user/user.go @@ -41,7 +41,7 @@ const ( ManageColumn = "admin_id" ) -// Columns holds all SQL columns are user fields. +// Columns holds all SQL columns for user fields. var Columns = []string{ FieldID, FieldAge, diff --git a/examples/traversal/ent/user_query.go b/examples/traversal/ent/user_query.go index 0448edcb0..0aad1957f 100644 --- a/examples/traversal/ent/user_query.go +++ b/examples/traversal/ent/user_query.go @@ -8,6 +8,7 @@ package ent import ( "context" + "database/sql/driver" "errors" "fmt" "math" @@ -29,6 +30,11 @@ type UserQuery struct { order []Order unique []string predicates []predicate.User + // eager-loading edges. + withPets *PetQuery + withFriends *UserQuery + withGroups *GroupQuery + withManage *GroupQuery // intermediate query. sql *sql.Selector } @@ -274,6 +280,50 @@ func (uq *UserQuery) Clone() *UserQuery { } } +// WithPets tells the query-builder to eager-loads the nodes that are connected to +// the "pets" edge. The optional arguments used to configure the query builder of the edge. +func (uq *UserQuery) WithPets(opts ...func(*PetQuery)) *UserQuery { + query := &PetQuery{config: uq.config} + for _, opt := range opts { + opt(query) + } + uq.withPets = query + return uq +} + +// WithFriends tells the query-builder to eager-loads the nodes that are connected to +// the "friends" edge. The optional arguments used to configure the query builder of the edge. +func (uq *UserQuery) WithFriends(opts ...func(*UserQuery)) *UserQuery { + query := &UserQuery{config: uq.config} + for _, opt := range opts { + opt(query) + } + uq.withFriends = query + return uq +} + +// WithGroups tells the query-builder to eager-loads the nodes that are connected to +// the "groups" edge. The optional arguments used to configure the query builder of the edge. +func (uq *UserQuery) WithGroups(opts ...func(*GroupQuery)) *UserQuery { + query := &GroupQuery{config: uq.config} + for _, opt := range opts { + opt(query) + } + uq.withGroups = query + return uq +} + +// WithManage tells the query-builder to eager-loads the nodes that are connected to +// the "manage" edge. The optional arguments used to configure the query builder of the edge. +func (uq *UserQuery) WithManage(opts ...func(*GroupQuery)) *UserQuery { + query := &GroupQuery{config: uq.config} + for _, opt := range opts { + opt(query) + } + uq.withManage = query + return uq +} + // GroupBy used to group vertices by one or more fields/columns. // It is often used with aggregate functions, like: count, max, mean, min, sum. // @@ -323,7 +373,8 @@ func (uq *UserQuery) sqlAll(ctx context.Context) ([]*User, error) { spec.ScanValues = func() []interface{} { node := &User{config: uq.config} nodes = append(nodes, node) - return node.scanValues() + values := node.scanValues() + return values } spec.Assign = func(values ...interface{}) error { if len(nodes) == 0 { @@ -335,6 +386,189 @@ func (uq *UserQuery) sqlAll(ctx context.Context) ([]*User, error) { if err := sqlgraph.QueryNodes(ctx, uq.driver, spec); err != nil { return nil, err } + + if query := uq.withPets; query != nil { + fks := make([]driver.Value, 0, len(nodes)) + nodeids := make(map[int]*User) + for i := range nodes { + fks = append(fks, nodes[i].ID) + nodeids[nodes[i].ID] = nodes[i] + } + query.withFKs = true + query.Where(predicate.Pet(func(s *sql.Selector) { + s.Where(sql.InValues(user.PetsColumn, fks...)) + })) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + fk := n.owner_id + if fk == nil { + return nil, fmt.Errorf(`foreign-key "owner_id" is nil for node %v`, n.ID) + } + node, ok := nodeids[*fk] + if !ok { + return nil, fmt.Errorf(`unexpected foreign-key "owner_id" returned %v for node %v`, *fk, n.ID) + } + node.Edges.Pets = append(node.Edges.Pets, n) + } + } + + if query := uq.withFriends; query != nil { + fks := make([]driver.Value, 0, len(nodes)) + ids := make(map[int]*User, len(nodes)) + for _, node := range nodes { + ids[node.ID] = node + fks = append(fks, node.ID) + } + var ( + edgeids []int + edges = make(map[int][]*User) + ) + spec := &sqlgraph.EdgeQuerySpec{ + Edge: &sqlgraph.EdgeSpec{ + Inverse: false, + Table: user.FriendsTable, + Columns: user.FriendsPrimaryKey, + }, + Predicate: func(s *sql.Selector) { + s.Where(sql.InValues(user.FriendsPrimaryKey[0], fks...)) + }, + + ScanValues: func() [2]interface{} { + return [2]interface{}{&sql.NullInt64{}, &sql.NullInt64{}} + }, + Assign: func(out, in interface{}) error { + eout, ok := out.(*sql.NullInt64) + if !ok || eout == nil { + return fmt.Errorf("unexpected id value for edge-out") + } + ein, ok := in.(*sql.NullInt64) + if !ok || ein == nil { + return fmt.Errorf("unexpected id value for edge-in") + } + outValue := int(eout.Int64) + inValue := int(eout.Int64) + node, ok := ids[outValue] + if !ok { + return fmt.Errorf("unexpected node id in edges: %v", outValue) + } + edgeids = append(edgeids, inValue) + edges[inValue] = append(edges[inValue], node) + return nil + }, + } + if err := sqlgraph.QueryEdges(ctx, uq.driver, spec); err != nil { + return nil, fmt.Errorf(`query edges "friends": %v`, err) + } + query.Where(user.IDIn(edgeids...)) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + nodes, ok := edges[n.ID] + if !ok { + return nil, fmt.Errorf(`unexpected "friends" node returned %v`, n.ID) + } + for i := range nodes { + nodes[i].Edges.Friends = append(nodes[i].Edges.Friends, n) + } + } + } + + if query := uq.withGroups; query != nil { + fks := make([]driver.Value, 0, len(nodes)) + ids := make(map[int]*User, len(nodes)) + for _, node := range nodes { + ids[node.ID] = node + fks = append(fks, node.ID) + } + var ( + edgeids []int + edges = make(map[int][]*User) + ) + spec := &sqlgraph.EdgeQuerySpec{ + Edge: &sqlgraph.EdgeSpec{ + Inverse: true, + Table: user.GroupsTable, + Columns: user.GroupsPrimaryKey, + }, + Predicate: func(s *sql.Selector) { + s.Where(sql.InValues(user.GroupsPrimaryKey[1], fks...)) + }, + + ScanValues: func() [2]interface{} { + return [2]interface{}{&sql.NullInt64{}, &sql.NullInt64{}} + }, + Assign: func(out, in interface{}) error { + eout, ok := out.(*sql.NullInt64) + if !ok || eout == nil { + return fmt.Errorf("unexpected id value for edge-out") + } + ein, ok := in.(*sql.NullInt64) + if !ok || ein == nil { + return fmt.Errorf("unexpected id value for edge-in") + } + outValue := int(eout.Int64) + inValue := int(eout.Int64) + node, ok := ids[outValue] + if !ok { + return fmt.Errorf("unexpected node id in edges: %v", outValue) + } + edgeids = append(edgeids, inValue) + edges[inValue] = append(edges[inValue], node) + return nil + }, + } + if err := sqlgraph.QueryEdges(ctx, uq.driver, spec); err != nil { + return nil, fmt.Errorf(`query edges "groups": %v`, err) + } + query.Where(group.IDIn(edgeids...)) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + nodes, ok := edges[n.ID] + if !ok { + return nil, fmt.Errorf(`unexpected "groups" node returned %v`, n.ID) + } + for i := range nodes { + nodes[i].Edges.Groups = append(nodes[i].Edges.Groups, n) + } + } + } + + if query := uq.withManage; query != nil { + fks := make([]driver.Value, 0, len(nodes)) + nodeids := make(map[int]*User) + for i := range nodes { + fks = append(fks, nodes[i].ID) + nodeids[nodes[i].ID] = nodes[i] + } + query.withFKs = true + query.Where(predicate.Group(func(s *sql.Selector) { + s.Where(sql.InValues(user.ManageColumn, fks...)) + })) + neighbors, err := query.All(ctx) + if err != nil { + return nil, err + } + for _, n := range neighbors { + fk := n.admin_id + if fk == nil { + return nil, fmt.Errorf(`foreign-key "admin_id" is nil for node %v`, n.ID) + } + node, ok := nodeids[*fk] + if !ok { + return nil, fmt.Errorf(`unexpected foreign-key "admin_id" returned %v for node %v`, *fk, n.ID) + } + node.Edges.Manage = append(node.Edges.Manage, n) + } + } + return nodes, nil }