├── examples ├── ent │ ├── generate.go │ ├── predicate │ │ └── predicate.go │ ├── runtime.go │ ├── schema │ │ └── user.go │ ├── runtime │ │ └── runtime.go │ ├── migrate │ │ ├── schema.go │ │ └── migrate.go │ ├── user │ │ ├── user.go │ │ └── where.go │ ├── context.go │ ├── config.go │ ├── enttest │ │ └── enttest.go │ ├── user_delete.go │ ├── user.go │ ├── hook │ │ └── hook.go │ ├── user_create.go │ ├── client.go │ ├── tx.go │ ├── ent.go │ ├── user_update.go │ ├── mutation.go │ └── user_query.go ├── oc │ └── example_test.go └── otel │ └── example_test.go ├── .github └── workflows │ └── go.yml ├── tags_test.go ├── oc.go ├── context_test.go ├── config.go ├── taggers.go ├── otel.go ├── tags.go ├── README.md ├── go.mod ├── context.go ├── driver.go ├── LICENSE └── go.sum /examples/ent/generate.go: -------------------------------------------------------------------------------- 1 | package ent 2 | 3 | //go:generate go run -mod=mod entgo.io/ent/cmd/ent generate ./schema 4 | -------------------------------------------------------------------------------- /examples/ent/predicate/predicate.go: -------------------------------------------------------------------------------- 1 | // Code generated by entc, DO NOT EDIT. 2 | 3 | package predicate 4 | 5 | import ( 6 | "entgo.io/ent/dialect/sql" 7 | ) 8 | 9 | // User is the predicate function for user builders. 10 | type User func(*sql.Selector) 11 | -------------------------------------------------------------------------------- /examples/ent/runtime.go: -------------------------------------------------------------------------------- 1 | // Code generated by entc, DO NOT EDIT. 2 | 3 | package ent 4 | 5 | // The init function reads all schema descriptors with runtime code 6 | // (default values, validators, hooks and policies) and stitches it 7 | // to their package variables. 8 | func init() { 9 | } 10 | -------------------------------------------------------------------------------- /examples/ent/schema/user.go: -------------------------------------------------------------------------------- 1 | package schema 2 | 3 | import ( 4 | "entgo.io/ent" 5 | "entgo.io/ent/schema/field" 6 | ) 7 | 8 | // User holds the schema definition for the User entity. 9 | type User struct { 10 | ent.Schema 11 | } 12 | 13 | // Fields of the User. 14 | func (User) Fields() []ent.Field { 15 | return []ent.Field{ 16 | field.String("name"), 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /examples/ent/runtime/runtime.go: -------------------------------------------------------------------------------- 1 | // Code generated by entc, DO NOT EDIT. 2 | 3 | package runtime 4 | 5 | // The schema-stitching logic is generated in ariga.io/sqlcomment/examples/ent/runtime.go 6 | 7 | const ( 8 | Version = "v0.9.2-0.20210821141344-368a8f7a2e9a" // Version of ent codegen. 9 | Sum = "h1:ONZ5BfDxYM5/YMv6cceUXLAkxtCPNSNBUXyVBg6YucU=" // Sum of ent codegen. 10 | ) 11 | -------------------------------------------------------------------------------- /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Go 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v4 15 | 16 | - name: Set up Go 17 | uses: actions/setup-go@v5 18 | with: 19 | go-version-file: './go.mod' 20 | 21 | - name: Build 22 | run: go build -v ./... 23 | 24 | - name: Test 25 | run: | 26 | go test -v ./... 27 | -------------------------------------------------------------------------------- /tags_test.go: -------------------------------------------------------------------------------- 1 | package sqlcomment 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestCommentEscapse(t *testing.T) { 10 | tags := Tags{ 11 | "route": `/param first`, 12 | "num": `1234`, 13 | "query": `DROP TABLE FOO'`, 14 | "injection": `/route/*/;DROP TABLE USERS`, 15 | } 16 | 17 | assert.Equal(t, `injection='%2Froute%2F%2A%2F%3BDROP%20TABLE%20USERS',num='1234',query='DROP%20TABLE%20FOO%27',route='%2Fparam%20first'`, tags.Marshal()) 18 | } 19 | -------------------------------------------------------------------------------- /examples/ent/migrate/schema.go: -------------------------------------------------------------------------------- 1 | // Code generated by entc, DO NOT EDIT. 2 | 3 | package migrate 4 | 5 | import ( 6 | "entgo.io/ent/dialect/sql/schema" 7 | "entgo.io/ent/schema/field" 8 | ) 9 | 10 | var ( 11 | // UsersColumns holds the columns for the "users" table. 12 | UsersColumns = []*schema.Column{ 13 | {Name: "id", Type: field.TypeInt, Increment: true}, 14 | {Name: "name", Type: field.TypeString}, 15 | } 16 | // UsersTable holds the schema information for the "users" table. 17 | UsersTable = &schema.Table{ 18 | Name: "users", 19 | Columns: UsersColumns, 20 | PrimaryKey: []*schema.Column{UsersColumns[0]}, 21 | } 22 | // Tables holds all the tables in the schema. 23 | Tables = []*schema.Table{ 24 | UsersTable, 25 | } 26 | ) 27 | 28 | func init() { 29 | } 30 | -------------------------------------------------------------------------------- /examples/ent/user/user.go: -------------------------------------------------------------------------------- 1 | // Code generated by entc, DO NOT EDIT. 2 | 3 | package user 4 | 5 | const ( 6 | // Label holds the string label denoting the user type in the database. 7 | Label = "user" 8 | // FieldID holds the string denoting the id field in the database. 9 | FieldID = "id" 10 | // FieldName holds the string denoting the name field in the database. 11 | FieldName = "name" 12 | // Table holds the table name of the user in the database. 13 | Table = "users" 14 | ) 15 | 16 | // Columns holds all SQL columns for user fields. 17 | var Columns = []string{ 18 | FieldID, 19 | FieldName, 20 | } 21 | 22 | // ValidColumn reports if the column name is valid (part of the table columns). 23 | func ValidColumn(column string) bool { 24 | for i := range Columns { 25 | if column == Columns[i] { 26 | return true 27 | } 28 | } 29 | return false 30 | } 31 | -------------------------------------------------------------------------------- /oc.go: -------------------------------------------------------------------------------- 1 | package sqlcomment 2 | 3 | import ( 4 | "context" 5 | 6 | "go.opencensus.io/plugin/ochttp/propagation/tracecontext" 7 | "go.opencensus.io/trace" 8 | ) 9 | 10 | const ( 11 | traceparentHeader = "traceparent" 12 | tracestateHeader = "tracestate" 13 | ) 14 | 15 | // OCTagger is a Tagger that adds `traceparent` and `tracestate` tags to the SQL comment. 16 | type OCTagger struct { 17 | format *tracecontext.HTTPFormat 18 | } 19 | 20 | // NewOCTagger adds OC trace information as SQL tags. 21 | func NewOCTagger() OCTagger { 22 | return OCTagger{&tracecontext.HTTPFormat{}} 23 | } 24 | 25 | // Tag finds trace information on the given context and returns SQL tags with trace information. 26 | func (ot OCTagger) Tag(ctx context.Context) Tags { 27 | spanCtx := trace.FromContext(ctx).SpanContext() 28 | tp, ts := ot.format.SpanContextToHeaders(spanCtx) 29 | tags := Tags{ 30 | traceparentHeader: tp, 31 | } 32 | if ts != "" { 33 | tags[tracestateHeader] = ts 34 | } 35 | return tags 36 | } 37 | -------------------------------------------------------------------------------- /context_test.go: -------------------------------------------------------------------------------- 1 | package sqlcomment 2 | 3 | import ( 4 | "context" 5 | "github.com/stretchr/testify/assert" 6 | "sync" 7 | "testing" 8 | ) 9 | 10 | func TestWithTagThreadSafety(t *testing.T) { 11 | ctx := context.Background() 12 | var wg sync.WaitGroup 13 | const numGoroutines = 100 14 | results := make(map[int]Tags) 15 | mu := sync.Mutex{} 16 | for i := 0; i < numGoroutines; i++ { 17 | wg.Add(1) 18 | go func(i int) { 19 | defer wg.Done() 20 | // Add a unique tag for each goroutine 21 | newCtx := WithTag(ctx, "goroutine", string(rune('A'+i))) 22 | tags := FromContext(newCtx) 23 | // Store the result in the map 24 | mu.Lock() 25 | results[i] = tags 26 | mu.Unlock() 27 | }(i) 28 | } 29 | 30 | wg.Wait() 31 | // Verify that each goroutine has its own independent tags 32 | assert.Equal(t, numGoroutines, len(results)) 33 | for i := 0; i < numGoroutines; i++ { 34 | assert.Contains(t, results[i], "goroutine") 35 | assert.Equal(t, string(rune('A'+i)), results[i]["goroutine"]) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /examples/ent/context.go: -------------------------------------------------------------------------------- 1 | // Code generated by entc, DO NOT EDIT. 2 | 3 | package ent 4 | 5 | import ( 6 | "context" 7 | ) 8 | 9 | type clientCtxKey struct{} 10 | 11 | // FromContext returns a Client stored inside a context, or nil if there isn't one. 12 | func FromContext(ctx context.Context) *Client { 13 | c, _ := ctx.Value(clientCtxKey{}).(*Client) 14 | return c 15 | } 16 | 17 | // NewContext returns a new context with the given Client attached. 18 | func NewContext(parent context.Context, c *Client) context.Context { 19 | return context.WithValue(parent, clientCtxKey{}, c) 20 | } 21 | 22 | type txCtxKey struct{} 23 | 24 | // TxFromContext returns a Tx stored inside a context, or nil if there isn't one. 25 | func TxFromContext(ctx context.Context) *Tx { 26 | tx, _ := ctx.Value(txCtxKey{}).(*Tx) 27 | return tx 28 | } 29 | 30 | // NewTxContext returns a new context with the given Tx attached. 31 | func NewTxContext(parent context.Context, tx *Tx) context.Context { 32 | return context.WithValue(parent, txCtxKey{}, tx) 33 | } 34 | -------------------------------------------------------------------------------- /config.go: -------------------------------------------------------------------------------- 1 | package sqlcomment 2 | 3 | import ( 4 | "context" 5 | ) 6 | 7 | // A Tagger is used by the driver to add tags to SQL queries. 8 | type Tagger interface { 9 | Tag(context.Context) Tags 10 | } 11 | 12 | type ( 13 | Option func(*options) 14 | options struct { 15 | taggers []Tagger 16 | } 17 | ) 18 | 19 | // WithTagger sets the taggers to be used to populate the SQL comment. 20 | func WithTagger(taggers ...Tagger) Option { 21 | return func(opts *options) { 22 | opts.taggers = append(opts.taggers, taggers...) 23 | } 24 | } 25 | 26 | // WithTags appends the given tags to every SQL query. 27 | func WithTags(tags Tags) Option { 28 | return func(opts *options) { 29 | opts.taggers = append(opts.taggers, NewStaticTagger(tags)) 30 | } 31 | } 32 | 33 | // WithDriverVerTag adds `db_driver` tag with the current version of ent. 34 | func WithDriverVerTag() Option { 35 | return func(opts *options) { 36 | opts.taggers = append(opts.taggers, NewDriverVersionTagger()) 37 | } 38 | } 39 | 40 | func buildOptions(opts []Option) options { 41 | var o options 42 | for _, opt := range opts { 43 | opt(&o) 44 | } 45 | return o 46 | } 47 | -------------------------------------------------------------------------------- /taggers.go: -------------------------------------------------------------------------------- 1 | package sqlcomment 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "runtime/debug" 7 | ) 8 | 9 | // driverVersionTagger adds `db_driver` tag with "ent:" 10 | type driverVersionTagger struct { 11 | version string 12 | } 13 | 14 | func NewDriverVersionTagger() driverVersionTagger { 15 | info, ok := debug.ReadBuildInfo() 16 | if !ok { 17 | return driverVersionTagger{"ent"} 18 | } 19 | for _, d := range info.Deps { 20 | if d.Path == "entgo.io/ent" { 21 | return driverVersionTagger{fmt.Sprintf("ent:%s", d.Version)} 22 | } 23 | } 24 | return driverVersionTagger{"ent"} 25 | } 26 | 27 | func (dv driverVersionTagger) Tag(ctx context.Context) Tags { 28 | return Tags{ 29 | KeyDBDriver: dv.version, 30 | } 31 | } 32 | 33 | type contextTagger struct{} 34 | 35 | func (ct contextTagger) Tag(ctx context.Context) Tags { 36 | return FromContext(ctx) 37 | } 38 | 39 | type staticTagger struct { 40 | tags Tags 41 | } 42 | 43 | // NewStaticTagger returns an Tagger which adds tags to every SQL comment. 44 | func NewStaticTagger(tags Tags) staticTagger { 45 | return staticTagger{tags} 46 | } 47 | 48 | func (st staticTagger) Tag(ctx context.Context) Tags { 49 | return st.tags 50 | } 51 | -------------------------------------------------------------------------------- /otel.go: -------------------------------------------------------------------------------- 1 | package sqlcomment 2 | 3 | import ( 4 | "context" 5 | 6 | "go.opentelemetry.io/otel" 7 | ) 8 | 9 | type ( 10 | // OTELTagger is a Tagger that adds `traceparent` and `tracestate` tags to the SQL comment. 11 | OTELTagger struct{} 12 | // CommentCarrier implements propagation.TextMapCarrier in order to retrieve trace information from OTEL. 13 | CommentCarrier Tags 14 | ) 15 | 16 | // NewOTELTagger adds OTEL trace information as SQL tags. 17 | func NewOTELTagger() OTELTagger { 18 | return OTELTagger{} 19 | } 20 | 21 | // Tag finds trace information on the given context and returns SQL tags with trace information. 22 | func (ot OTELTagger) Tag(ctx context.Context) Tags { 23 | c := NewCommentCarrier() 24 | otel.GetTextMapPropagator().Inject(ctx, c) 25 | return Tags(c) 26 | } 27 | 28 | func NewCommentCarrier() CommentCarrier { 29 | return make(CommentCarrier) 30 | } 31 | 32 | // Get returns the value associated with the passed key. 33 | func (c CommentCarrier) Get(key string) string { 34 | return string(c[key]) 35 | } 36 | 37 | // Set stores the key-value pair. 38 | func (c CommentCarrier) Set(key string, value string) { 39 | c[key] = value 40 | } 41 | 42 | // Keys lists the keys stored in this carrier. 43 | func (c CommentCarrier) Keys() []string { 44 | keys := make([]string, 0, len(c)) 45 | for k := range c { 46 | keys = append(keys, string(k)) 47 | } 48 | return keys 49 | } 50 | -------------------------------------------------------------------------------- /examples/ent/config.go: -------------------------------------------------------------------------------- 1 | // Code generated by entc, DO NOT EDIT. 2 | 3 | package ent 4 | 5 | import ( 6 | "entgo.io/ent" 7 | "entgo.io/ent/dialect" 8 | ) 9 | 10 | // Option function to configure the client. 11 | type Option func(*config) 12 | 13 | // Config is the configuration for the client and its builder. 14 | type config struct { 15 | // driver used for executing database requests. 16 | driver dialect.Driver 17 | // debug enable a debug logging. 18 | debug bool 19 | // log used for logging on debug mode. 20 | log func(...interface{}) 21 | // hooks to execute on mutations. 22 | hooks *hooks 23 | } 24 | 25 | // hooks per client, for fast access. 26 | type hooks struct { 27 | User []ent.Hook 28 | } 29 | 30 | // Options applies the options on the config object. 31 | func (c *config) options(opts ...Option) { 32 | for _, opt := range opts { 33 | opt(c) 34 | } 35 | if c.debug { 36 | c.driver = dialect.Debug(c.driver, c.log) 37 | } 38 | } 39 | 40 | // Debug enables debug logging on the ent.Driver. 41 | func Debug() Option { 42 | return func(c *config) { 43 | c.debug = true 44 | } 45 | } 46 | 47 | // Log sets the logging function for debug mode. 48 | func Log(fn func(...interface{})) Option { 49 | return func(c *config) { 50 | c.log = fn 51 | } 52 | } 53 | 54 | // Driver configures the client driver. 55 | func Driver(driver dialect.Driver) Option { 56 | return func(c *config) { 57 | c.driver = driver 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /tags.go: -------------------------------------------------------------------------------- 1 | package sqlcomment 2 | 3 | import ( 4 | "fmt" 5 | "net/url" 6 | "sort" 7 | "strings" 8 | ) 9 | 10 | const ( 11 | KeyDBDriver = "db_driver" 12 | KeyFramework = "framework" 13 | KeyApplication = "application" 14 | KeyRoute = "route" 15 | KeyController = "controller" 16 | KeyAction = "action" 17 | ) 18 | 19 | // Tags represents key value pairs which can be serialized into SQL comment. 20 | // see https://google.github.io/sqlcommenter/spec/ 21 | type Tags map[string]string 22 | 23 | func encodeValue(v string) string { 24 | urlEscape := strings.ReplaceAll(url.PathEscape(string(v)), "+", "%20") 25 | return fmt.Sprintf("'%s'", urlEscape) 26 | } 27 | 28 | func encodeKey(k string) string { 29 | return url.QueryEscape(string(k)) 30 | } 31 | 32 | // Marshal returns the sqlcomment encoding of t following the spec (see https://google.github.io/sqlcommenter/). 33 | func (t Tags) Marshal() string { 34 | kv := make([]struct{ k, v string }, 0, len(t)) 35 | for k := range t { 36 | kv = append(kv, struct{ k, v string }{encodeKey(k), encodeValue(t[k])}) 37 | } 38 | sort.Slice(kv, func(i, j int) bool { 39 | return kv[i].k < kv[j].k 40 | }) 41 | var b strings.Builder 42 | for i, p := range kv { 43 | if i > 0 { 44 | b.WriteByte(',') 45 | } 46 | fmt.Fprintf(&b, "%s=%s", p.k, p.v) 47 | } 48 | return b.String() 49 | } 50 | 51 | // Merge copies given tags into sc. 52 | func (t Tags) Merge(tags ...Tags) Tags { 53 | for _, c := range tags { 54 | for k, v := range c { 55 | t[k] = v 56 | } 57 | } 58 | return t 59 | } 60 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sqlcomm**ent** 2 | sqlcomm**ent** is an [ent](https://entgo.io) driver that adds SQL comments following [sqlcommenter specification](https://google.github.io/sqlcommenter/spec/). 3 | sqlcomment includes support for OpenTelemetry and OpenCensus (see [examples](examples/)). 4 | 5 | # Installing 6 | ```bash 7 | go install ariga.io/sqlcomment 8 | ``` 9 | 10 | # Basic Usage 11 | ```go 12 | // Create db driver. 13 | // make sure to import "entgo.io/ent/dialect/sql" instead of "database/sql" 14 | db, err := sql.Open("sqlite3", "file:ent?mode=memory&cache=shared&_fk=1") 15 | if err != nil { 16 | log.Fatalf("Failed to connect to database: %v", err) 17 | } 18 | // Create sqlcomment driver which wraps sqlite driver. 19 | drv := sqlcomment.NewDriver(db, 20 | sqlcomment.WithDriverVerTag(), 21 | sqlcomment.WithTags(sqlcomment.Tags{ 22 | sqlcomment.KeyApplication: "my-app", 23 | sqlcomment.KeyFramework: "net/http", 24 | }), 25 | ) 26 | // Create and configure ent client 27 | client := ent.NewClient(ent.Driver(drv)) 28 | ``` 29 | 30 | # Adding context level tags 31 | Suppose you have a REST API and you want to add a tag with the request URL (typically `route` tag). You can achieve that by using `sqlcomment.WithTag(ctx, key, val)` which adds the given tag to the context to later be serialized by sqlcomment driver. (see full example [here](examples/otel/example_test.go)) 32 | ```go 33 | // Add a middleware to your HTTP server which puts the `route` tag on the context for every request. 34 | middleware := func(next http.Handler) http.Handler { 35 | fn := func(w http.ResponseWriter, r *http.Request) { 36 | ctx := sqlcomment.WithTag(r.Context(), "route", r.URL.Path) 37 | next.ServeHTTP(w, r.WithContext(ctx)) 38 | } 39 | return http.HandlerFunc(fn) 40 | } 41 | ``` 42 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module ariga.io/sqlcomment 2 | 3 | go 1.23.0 4 | 5 | toolchain go1.24.3 6 | 7 | require ( 8 | entgo.io/ent v0.14.4 9 | github.com/mattn/go-sqlite3 v1.14.28 10 | github.com/stretchr/testify v1.10.0 11 | go.opencensus.io v0.24.0 12 | go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0 13 | go.opentelemetry.io/otel v1.37.0 14 | go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.37.0 15 | go.opentelemetry.io/otel/sdk v1.37.0 16 | ) 17 | 18 | require ( 19 | ariga.io/atlas v0.31.1-0.20250212144724-069be8033e83 // indirect 20 | github.com/agext/levenshtein v1.2.1 // indirect 21 | github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect 22 | github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect 23 | github.com/bmatcuk/doublestar v1.3.4 // indirect 24 | github.com/davecgh/go-spew v1.1.1 // indirect 25 | github.com/felixge/httpsnoop v1.0.4 // indirect 26 | github.com/go-logr/logr v1.4.3 // indirect 27 | github.com/go-logr/stdr v1.2.2 // indirect 28 | github.com/go-openapi/inflect v0.19.0 // indirect 29 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect 30 | github.com/google/go-cmp v0.7.0 // indirect 31 | github.com/google/uuid v1.6.0 // indirect 32 | github.com/hashicorp/hcl/v2 v2.13.0 // indirect 33 | github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 // indirect 34 | github.com/pmezard/go-difflib v1.0.0 // indirect 35 | github.com/zclconf/go-cty v1.14.4 // indirect 36 | github.com/zclconf/go-cty-yaml v1.1.0 // indirect 37 | go.opentelemetry.io/auto/sdk v1.1.0 // indirect 38 | go.opentelemetry.io/otel/metric v1.37.0 // indirect 39 | go.opentelemetry.io/otel/trace v1.37.0 // indirect 40 | golang.org/x/mod v0.23.0 // indirect 41 | golang.org/x/sys v0.34.0 // indirect 42 | golang.org/x/text v0.21.0 // indirect 43 | gopkg.in/yaml.v3 v3.0.1 // indirect 44 | ) 45 | -------------------------------------------------------------------------------- /context.go: -------------------------------------------------------------------------------- 1 | package sqlcomment 2 | 3 | import ( 4 | "context" 5 | ) 6 | 7 | type ( 8 | // ctxOptions allows injecting runtime options. 9 | ctxOptions struct { 10 | skip bool // i.e. skip entry. 11 | tags Tags 12 | } 13 | ctxKeyType struct{} 14 | ) 15 | 16 | var ctxOptionsKey ctxKeyType 17 | 18 | // Skip returns a new Context that tells the Driver 19 | // to skip the commenting on Query. 20 | // 21 | // client.T.Query().All(sqlcomment.Skip(ctx)) 22 | func Skip(ctx context.Context) context.Context { 23 | c, ok := ctx.Value(ctxOptionsKey).(*ctxOptions) 24 | if !ok { 25 | return context.WithValue(ctx, ctxOptionsKey, &ctxOptions{skip: true}) 26 | } 27 | c.skip = true 28 | return ctx 29 | } 30 | 31 | // WithTag stores the key and val pair on the context. 32 | // for example, if you want to add `route` tag to your SQL comment, put the url path on request context: 33 | // 34 | // middleware := func(next http.Handler) http.Handler { 35 | // fn := func(w http.ResponseWriter, r *http.Request) { 36 | // ctx := sqlcomment.WithTag(r.Context(), "route", r.URL.Path) 37 | // next.ServeHTTP(w, r.WithContext(ctx)) 38 | // } 39 | // return http.HandlerFunc(fn) 40 | // } 41 | func WithTag(ctx context.Context, key, val string) context.Context { 42 | t, ok := ctx.Value(ctxOptionsKey).(*ctxOptions) 43 | if !ok { 44 | return context.WithValue(ctx, ctxOptionsKey, &ctxOptions{tags: Tags{key: val}}) 45 | } 46 | // Create a copy of the existing ctxOptions to avoid modifying the original 47 | newTags := make(Tags) 48 | for k, v := range t.tags { 49 | newTags[k] = v 50 | } 51 | newTags[key] = val 52 | return context.WithValue(ctx, ctxOptionsKey, &ctxOptions{tags: newTags}) 53 | } 54 | 55 | // FromContext returns the tags stored in ctx, if any. 56 | func FromContext(ctx context.Context) Tags { 57 | t, ok := ctx.Value(ctxOptionsKey).(*ctxOptions) 58 | if !ok { 59 | return Tags{} 60 | } 61 | return t.tags 62 | } 63 | -------------------------------------------------------------------------------- /examples/ent/enttest/enttest.go: -------------------------------------------------------------------------------- 1 | // Code generated by entc, DO NOT EDIT. 2 | 3 | package enttest 4 | 5 | import ( 6 | "context" 7 | 8 | "ariga.io/sqlcomment/examples/ent" 9 | // required by schema hooks. 10 | _ "ariga.io/sqlcomment/examples/ent/runtime" 11 | 12 | "entgo.io/ent/dialect/sql/schema" 13 | ) 14 | 15 | type ( 16 | // TestingT is the interface that is shared between 17 | // testing.T and testing.B and used by enttest. 18 | TestingT interface { 19 | FailNow() 20 | Error(...interface{}) 21 | } 22 | 23 | // Option configures client creation. 24 | Option func(*options) 25 | 26 | options struct { 27 | opts []ent.Option 28 | migrateOpts []schema.MigrateOption 29 | } 30 | ) 31 | 32 | // WithOptions forwards options to client creation. 33 | func WithOptions(opts ...ent.Option) Option { 34 | return func(o *options) { 35 | o.opts = append(o.opts, opts...) 36 | } 37 | } 38 | 39 | // WithMigrateOptions forwards options to auto migration. 40 | func WithMigrateOptions(opts ...schema.MigrateOption) Option { 41 | return func(o *options) { 42 | o.migrateOpts = append(o.migrateOpts, opts...) 43 | } 44 | } 45 | 46 | func newOptions(opts []Option) *options { 47 | o := &options{} 48 | for _, opt := range opts { 49 | opt(o) 50 | } 51 | return o 52 | } 53 | 54 | // Open calls ent.Open and auto-run migration. 55 | func Open(t TestingT, driverName, dataSourceName string, opts ...Option) *ent.Client { 56 | o := newOptions(opts) 57 | c, err := ent.Open(driverName, dataSourceName, o.opts...) 58 | if err != nil { 59 | t.Error(err) 60 | t.FailNow() 61 | } 62 | if err := c.Schema.Create(context.Background(), o.migrateOpts...); err != nil { 63 | t.Error(err) 64 | t.FailNow() 65 | } 66 | return c 67 | } 68 | 69 | // NewClient calls ent.NewClient and auto-run migration. 70 | func NewClient(t TestingT, opts ...Option) *ent.Client { 71 | o := newOptions(opts) 72 | c := ent.NewClient(o.opts...) 73 | if err := c.Schema.Create(context.Background(), o.migrateOpts...); err != nil { 74 | t.Error(err) 75 | t.FailNow() 76 | } 77 | return c 78 | } 79 | -------------------------------------------------------------------------------- /examples/ent/migrate/migrate.go: -------------------------------------------------------------------------------- 1 | // Code generated by entc, DO NOT EDIT. 2 | 3 | package migrate 4 | 5 | import ( 6 | "context" 7 | "fmt" 8 | "io" 9 | 10 | "entgo.io/ent/dialect" 11 | "entgo.io/ent/dialect/sql/schema" 12 | ) 13 | 14 | var ( 15 | // WithGlobalUniqueID sets the universal ids options to the migration. 16 | // If this option is enabled, ent migration will allocate a 1<<32 range 17 | // for the ids of each entity (table). 18 | // Note that this option cannot be applied on tables that already exist. 19 | WithGlobalUniqueID = schema.WithGlobalUniqueID 20 | // WithDropColumn sets the drop column option to the migration. 21 | // If this option is enabled, ent migration will drop old columns 22 | // that were used for both fields and edges. This defaults to false. 23 | WithDropColumn = schema.WithDropColumn 24 | // WithDropIndex sets the drop index option to the migration. 25 | // If this option is enabled, ent migration will drop old indexes 26 | // that were defined in the schema. This defaults to false. 27 | // Note that unique constraints are defined using `UNIQUE INDEX`, 28 | // and therefore, it's recommended to enable this option to get more 29 | // flexibility in the schema changes. 30 | WithDropIndex = schema.WithDropIndex 31 | // WithForeignKeys enables creating foreign-key in schema DDL. This defaults to true. 32 | WithForeignKeys = schema.WithForeignKeys 33 | ) 34 | 35 | // Schema is the API for creating, migrating and dropping a schema. 36 | type Schema struct { 37 | drv dialect.Driver 38 | universalID bool 39 | } 40 | 41 | // NewSchema creates a new schema client. 42 | func NewSchema(drv dialect.Driver) *Schema { return &Schema{drv: drv} } 43 | 44 | // Create creates all schema resources. 45 | func (s *Schema) Create(ctx context.Context, opts ...schema.MigrateOption) error { 46 | migrate, err := schema.NewMigrate(s.drv, opts...) 47 | if err != nil { 48 | return fmt.Errorf("ent/migrate: %w", err) 49 | } 50 | return migrate.Create(ctx, Tables...) 51 | } 52 | 53 | // WriteTo writes the schema changes to w instead of running them against the database. 54 | // 55 | // if err := client.Schema.WriteTo(context.Background(), os.Stdout); err != nil { 56 | // log.Fatal(err) 57 | // } 58 | // 59 | func (s *Schema) WriteTo(ctx context.Context, w io.Writer, opts ...schema.MigrateOption) error { 60 | drv := &schema.WriteDriver{ 61 | Writer: w, 62 | Driver: s.drv, 63 | } 64 | migrate, err := schema.NewMigrate(drv, opts...) 65 | if err != nil { 66 | return fmt.Errorf("ent/migrate: %w", err) 67 | } 68 | return migrate.Create(ctx, Tables...) 69 | } 70 | -------------------------------------------------------------------------------- /examples/oc/example_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "log" 7 | "net/http" 8 | "net/http/httptest" 9 | "time" 10 | 11 | "ariga.io/sqlcomment" 12 | "ariga.io/sqlcomment/examples/ent" 13 | "entgo.io/ent/dialect" 14 | "entgo.io/ent/dialect/sql" 15 | 16 | "go.opencensus.io/examples/exporter" 17 | "go.opencensus.io/plugin/ochttp" 18 | "go.opencensus.io/stats/view" 19 | "go.opencensus.io/trace" 20 | 21 | _ "github.com/mattn/go-sqlite3" 22 | ) 23 | 24 | const ( 25 | metricsLogFile = "/tmp/metrics.log" 26 | tracesLogFile = "/tmp/trace.log" 27 | ) 28 | 29 | func initTracer() func() { 30 | // Using log exporter to export metrics but you can choose any supported exporter. 31 | exporter, err := exporter.NewLogExporter(exporter.Options{ 32 | ReportingInterval: 10 * time.Second, 33 | MetricsLogFile: metricsLogFile, 34 | TracesLogFile: tracesLogFile, 35 | }) 36 | if err != nil { 37 | log.Fatalf("Error creating log exporter: %v", err) 38 | } 39 | exporter.Start() 40 | trace.ApplyConfig(trace.Config{DefaultSampler: trace.AlwaysSample()}) 41 | 42 | // Report stats at every second. 43 | view.SetReportingPeriod(1 * time.Second) 44 | return func() { 45 | exporter.Stop() 46 | exporter.Close() 47 | } 48 | } 49 | 50 | func main() { 51 | closeTracer := initTracer() 52 | defer closeTracer() 53 | // Create db driver. 54 | db, err := sql.Open("sqlite3", "file:ent?mode=memory&cache=shared&_fk=1") 55 | if err != nil { 56 | log.Fatalf("Failed to connect to database: %v", err) 57 | } 58 | // create sqlcommenter driver which wraps debug driver which wraps sqlite driver 59 | // we should have sqlcommenter and debug logs on every query to our sqlite DB 60 | commentedDriver := sqlcomment.NewDriver(dialect.Debug(db), 61 | // add OpenCensus tracing tags 62 | sqlcomment.WithTagger(sqlcomment.NewOCTagger()), 63 | sqlcomment.WithDriverVerTag(), 64 | sqlcomment.WithTags(sqlcomment.Tags{ 65 | sqlcomment.KeyApplication: "users", 66 | sqlcomment.KeyFramework: "net/http", 67 | }), 68 | ) 69 | // create and configure ent client 70 | client := ent.NewClient(ent.Driver(commentedDriver)) 71 | defer client.Close() 72 | // Run the auto migration tool. 73 | if err := client.Schema.Create(context.Background()); err != nil { 74 | log.Fatalf("failed creating schema resources: %v", err) 75 | } 76 | 77 | client.User.Create().SetName("hedwigz").SaveX(context.Background()) 78 | getUsersHandler := func(rw http.ResponseWriter, r *http.Request) { 79 | users := client.User.Query().AllX(r.Context()) 80 | b, _ := json.Marshal(users) 81 | rw.WriteHeader(http.StatusOK) 82 | rw.Write(b) 83 | } 84 | 85 | backend := &ochttp.Handler{ 86 | Handler: http.HandlerFunc(getUsersHandler), 87 | } 88 | testRequest(backend) 89 | } 90 | 91 | func testRequest(handler http.Handler) { 92 | req := httptest.NewRequest(http.MethodGet, "/", nil) 93 | w := httptest.NewRecorder() 94 | 95 | // debug printer should print sql statement with comment 96 | handler.ServeHTTP(w, req) 97 | } 98 | -------------------------------------------------------------------------------- /examples/ent/user_delete.go: -------------------------------------------------------------------------------- 1 | // Code generated by entc, DO NOT EDIT. 2 | 3 | package ent 4 | 5 | import ( 6 | "context" 7 | "fmt" 8 | 9 | "ariga.io/sqlcomment/examples/ent/predicate" 10 | "ariga.io/sqlcomment/examples/ent/user" 11 | "entgo.io/ent/dialect/sql" 12 | "entgo.io/ent/dialect/sql/sqlgraph" 13 | "entgo.io/ent/schema/field" 14 | ) 15 | 16 | // UserDelete is the builder for deleting a User entity. 17 | type UserDelete struct { 18 | config 19 | hooks []Hook 20 | mutation *UserMutation 21 | } 22 | 23 | // Where appends a list predicates to the UserDelete builder. 24 | func (ud *UserDelete) Where(ps ...predicate.User) *UserDelete { 25 | ud.mutation.Where(ps...) 26 | return ud 27 | } 28 | 29 | // Exec executes the deletion query and returns how many vertices were deleted. 30 | func (ud *UserDelete) Exec(ctx context.Context) (int, error) { 31 | var ( 32 | err error 33 | affected int 34 | ) 35 | if len(ud.hooks) == 0 { 36 | affected, err = ud.sqlExec(ctx) 37 | } else { 38 | var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { 39 | mutation, ok := m.(*UserMutation) 40 | if !ok { 41 | return nil, fmt.Errorf("unexpected mutation type %T", m) 42 | } 43 | ud.mutation = mutation 44 | affected, err = ud.sqlExec(ctx) 45 | mutation.done = true 46 | return affected, err 47 | }) 48 | for i := len(ud.hooks) - 1; i >= 0; i-- { 49 | if ud.hooks[i] == nil { 50 | return 0, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)") 51 | } 52 | mut = ud.hooks[i](mut) 53 | } 54 | if _, err := mut.Mutate(ctx, ud.mutation); err != nil { 55 | return 0, err 56 | } 57 | } 58 | return affected, err 59 | } 60 | 61 | // ExecX is like Exec, but panics if an error occurs. 62 | func (ud *UserDelete) ExecX(ctx context.Context) int { 63 | n, err := ud.Exec(ctx) 64 | if err != nil { 65 | panic(err) 66 | } 67 | return n 68 | } 69 | 70 | func (ud *UserDelete) sqlExec(ctx context.Context) (int, error) { 71 | _spec := &sqlgraph.DeleteSpec{ 72 | Node: &sqlgraph.NodeSpec{ 73 | Table: user.Table, 74 | ID: &sqlgraph.FieldSpec{ 75 | Type: field.TypeInt, 76 | Column: user.FieldID, 77 | }, 78 | }, 79 | } 80 | if ps := ud.mutation.predicates; len(ps) > 0 { 81 | _spec.Predicate = func(selector *sql.Selector) { 82 | for i := range ps { 83 | ps[i](selector) 84 | } 85 | } 86 | } 87 | return sqlgraph.DeleteNodes(ctx, ud.driver, _spec) 88 | } 89 | 90 | // UserDeleteOne is the builder for deleting a single User entity. 91 | type UserDeleteOne struct { 92 | ud *UserDelete 93 | } 94 | 95 | // Exec executes the deletion query. 96 | func (udo *UserDeleteOne) Exec(ctx context.Context) error { 97 | n, err := udo.ud.Exec(ctx) 98 | switch { 99 | case err != nil: 100 | return err 101 | case n == 0: 102 | return &NotFoundError{user.Label} 103 | default: 104 | return nil 105 | } 106 | } 107 | 108 | // ExecX is like Exec, but panics if an error occurs. 109 | func (udo *UserDeleteOne) ExecX(ctx context.Context) { 110 | udo.ud.ExecX(ctx) 111 | } 112 | -------------------------------------------------------------------------------- /examples/ent/user.go: -------------------------------------------------------------------------------- 1 | // Code generated by entc, DO NOT EDIT. 2 | 3 | package ent 4 | 5 | import ( 6 | "fmt" 7 | "strings" 8 | 9 | "ariga.io/sqlcomment/examples/ent/user" 10 | "entgo.io/ent/dialect/sql" 11 | ) 12 | 13 | // User is the model entity for the User schema. 14 | type User struct { 15 | config `json:"-"` 16 | // ID of the ent. 17 | ID int `json:"id,omitempty"` 18 | // Name holds the value of the "name" field. 19 | Name string `json:"name,omitempty"` 20 | } 21 | 22 | // scanValues returns the types for scanning values from sql.Rows. 23 | func (*User) scanValues(columns []string) ([]interface{}, error) { 24 | values := make([]interface{}, len(columns)) 25 | for i := range columns { 26 | switch columns[i] { 27 | case user.FieldID: 28 | values[i] = new(sql.NullInt64) 29 | case user.FieldName: 30 | values[i] = new(sql.NullString) 31 | default: 32 | return nil, fmt.Errorf("unexpected column %q for type User", columns[i]) 33 | } 34 | } 35 | return values, nil 36 | } 37 | 38 | // assignValues assigns the values that were returned from sql.Rows (after scanning) 39 | // to the User fields. 40 | func (u *User) assignValues(columns []string, values []interface{}) error { 41 | if m, n := len(values), len(columns); m < n { 42 | return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) 43 | } 44 | for i := range columns { 45 | switch columns[i] { 46 | case user.FieldID: 47 | value, ok := values[i].(*sql.NullInt64) 48 | if !ok { 49 | return fmt.Errorf("unexpected type %T for field id", value) 50 | } 51 | u.ID = int(value.Int64) 52 | case user.FieldName: 53 | if value, ok := values[i].(*sql.NullString); !ok { 54 | return fmt.Errorf("unexpected type %T for field name", values[i]) 55 | } else if value.Valid { 56 | u.Name = value.String 57 | } 58 | } 59 | } 60 | return nil 61 | } 62 | 63 | // Update returns a builder for updating this User. 64 | // Note that you need to call User.Unwrap() before calling this method if this User 65 | // was returned from a transaction, and the transaction was committed or rolled back. 66 | func (u *User) Update() *UserUpdateOne { 67 | return (&UserClient{config: u.config}).UpdateOne(u) 68 | } 69 | 70 | // Unwrap unwraps the User entity that was returned from a transaction after it was closed, 71 | // so that all future queries will be executed through the driver which created the transaction. 72 | func (u *User) Unwrap() *User { 73 | tx, ok := u.config.driver.(*txDriver) 74 | if !ok { 75 | panic("ent: User is not a transactional entity") 76 | } 77 | u.config.driver = tx.drv 78 | return u 79 | } 80 | 81 | // String implements the fmt.Stringer. 82 | func (u *User) String() string { 83 | var builder strings.Builder 84 | builder.WriteString("User(") 85 | builder.WriteString(fmt.Sprintf("id=%v", u.ID)) 86 | builder.WriteString(", name=") 87 | builder.WriteString(u.Name) 88 | builder.WriteByte(')') 89 | return builder.String() 90 | } 91 | 92 | // Users is a parsable slice of User. 93 | type Users []*User 94 | 95 | func (u Users) config(cfg config) { 96 | for _i := range u { 97 | u[_i].config = cfg 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /examples/otel/example_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "io" 7 | "log" 8 | "net/http" 9 | "net/http/httptest" 10 | 11 | "ariga.io/sqlcomment" 12 | "entgo.io/ent/dialect" 13 | "entgo.io/ent/dialect/sql" 14 | _ "github.com/mattn/go-sqlite3" 15 | 16 | "ariga.io/sqlcomment/examples/ent" 17 | "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" 18 | "go.opentelemetry.io/otel" 19 | stdout "go.opentelemetry.io/otel/exporters/stdout/stdouttrace" 20 | "go.opentelemetry.io/otel/propagation" 21 | "go.opentelemetry.io/otel/sdk/resource" 22 | sdktrace "go.opentelemetry.io/otel/sdk/trace" 23 | semconv "go.opentelemetry.io/otel/semconv/v1.4.0" 24 | ) 25 | 26 | type CustomCommenter struct{} 27 | 28 | func (mcc CustomCommenter) Tag(ctx context.Context) sqlcomment.Tags { 29 | return sqlcomment.Tags{ 30 | "key": "value", 31 | } 32 | } 33 | 34 | func Example_otelIntegration() { 35 | tp := initTracer() 36 | defer func() { 37 | if err := tp.Shutdown(context.Background()); err != nil { 38 | log.Printf("Error shutting down tracer provider: %v", err) 39 | } 40 | }() 41 | 42 | // Create db driver. 43 | db, err := sql.Open("sqlite3", "file:ent?mode=memory&cache=shared&_fk=1") 44 | if err != nil { 45 | log.Fatalf("Failed to connect to database: %v", err) 46 | } 47 | commentedDriver := sqlcomment.NewDriver(dialect.Debug(db), 48 | sqlcomment.WithTagger( 49 | // add tracing info with Open Telemetry. 50 | sqlcomment.NewOTELTagger(), 51 | // use your custom commenter 52 | CustomCommenter{}, 53 | ), 54 | // add `db_driver` version tag 55 | sqlcomment.WithDriverVerTag(), 56 | // add some global tags to all queries 57 | sqlcomment.WithTags(sqlcomment.Tags{ 58 | sqlcomment.KeyApplication: "bootcamp", 59 | sqlcomment.KeyFramework: "go-chi", 60 | })) 61 | // create and configure ent client 62 | client := ent.NewClient(ent.Driver(commentedDriver)) 63 | defer client.Close() 64 | // Run the auto migration tool. 65 | if err := client.Schema.Create(context.Background()); err != nil { 66 | log.Fatalf("failed creating schema resources: %v", err) 67 | } 68 | 69 | client.User.Create().SetName("hedwigz").SaveX(context.Background()) 70 | 71 | // An HTTP middleware that adds the URL path to sqlcomment tags, under the key "route". 72 | middleware := func(next http.Handler) http.Handler { 73 | fn := func(w http.ResponseWriter, r *http.Request) { 74 | ctx := sqlcomment.WithTag(r.Context(), "route", r.URL.Path) 75 | next.ServeHTTP(w, r.WithContext(ctx)) 76 | } 77 | return http.HandlerFunc(fn) 78 | } 79 | // Application-level http handler. 80 | getUsersHandler := func(rw http.ResponseWriter, r *http.Request) { 81 | users := client.User.Query().AllX(r.Context()) 82 | b, _ := json.Marshal(users) 83 | rw.WriteHeader(http.StatusOK) 84 | rw.Write(b) 85 | } 86 | 87 | backend := otelhttp.NewHandler(middleware(http.HandlerFunc(getUsersHandler)), "app") 88 | testRequest(backend) 89 | } 90 | 91 | func initTracer() *sdktrace.TracerProvider { 92 | exporter, err := stdout.New(stdout.WithWriter(io.Discard)) 93 | if err != nil { 94 | log.Fatal(err) 95 | } 96 | // For the demonstration, use sdktrace.AlwaysSample sampler to sample all traces. 97 | // In a production application, use sdktrace.ProbabilitySampler with a desired probability. 98 | tp := sdktrace.NewTracerProvider( 99 | sdktrace.WithSampler(sdktrace.AlwaysSample()), 100 | sdktrace.WithBatcher(exporter), 101 | sdktrace.WithResource(resource.NewWithAttributes(semconv.SchemaURL, semconv.ServiceNameKey.String("ExampleService"))), 102 | ) 103 | otel.SetTracerProvider(tp) 104 | // Add propagation.TaceContext{} which will be used by OtelTagger to inject trace information. 105 | otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{})) 106 | return tp 107 | } 108 | 109 | func testRequest(handler http.Handler) { 110 | req := httptest.NewRequest(http.MethodGet, "/api/resource", nil) 111 | w := httptest.NewRecorder() 112 | 113 | // Debug printer should print SQL statement with comment. 114 | handler.ServeHTTP(w, req) 115 | } 116 | -------------------------------------------------------------------------------- /driver.go: -------------------------------------------------------------------------------- 1 | package sqlcomment 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | "fmt" 7 | 8 | "entgo.io/ent/dialect" 9 | ) 10 | 11 | type ( 12 | // Driver is a driver that adds an SQL comment. 13 | // See: https://google.github.io/sqlcommenter. 14 | Driver struct { 15 | dialect.Driver // underlying driver. 16 | commenter 17 | } 18 | 19 | // Tx is a transaction implementation that adds an SQL comment. 20 | Tx struct { 21 | dialect.Tx // underlying transaction. 22 | ctx context.Context // underlying transaction context. 23 | commenter 24 | } 25 | 26 | commenter struct { 27 | options 28 | } 29 | ) 30 | 31 | // NewDriver decorates the given driver and adds an SQL comment to every query. 32 | func NewDriver(drv dialect.Driver, options ...Option) dialect.Driver { 33 | taggers := []Tagger{contextTagger{}} 34 | opts := buildOptions(append(options, WithTagger(taggers...))) 35 | return &Driver{drv, commenter{opts}} 36 | } 37 | 38 | func (c commenter) withComment(ctx context.Context, query string) string { 39 | tags := make(Tags) 40 | for _, h := range c.taggers { 41 | tags.Merge(h.Tag(ctx)) 42 | } 43 | return fmt.Sprintf("%s /*%s*/", query, tags.Marshal()) 44 | } 45 | 46 | // Query adds an SQL comment to the original query and calls the underlying driver Query method. 47 | func (d *Driver) Query(ctx context.Context, query string, args, v interface{}) error { 48 | return d.Driver.Query(ctx, d.withComment(ctx, query), args, v) 49 | } 50 | 51 | // QueryContext calls QueryContext of the underlying driver, or fails if it is not supported. 52 | func (d *Driver) QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error) { 53 | drv, ok := d.Driver.(interface { 54 | QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error) 55 | }) 56 | if !ok { 57 | return nil, fmt.Errorf("Driver.QueryContext is not supported") 58 | } 59 | return drv.QueryContext(ctx, d.withComment(ctx, query), args...) 60 | } 61 | 62 | // Exec adds an SQL comment to the original query and calls the underlying driver Exec method. 63 | func (d *Driver) Exec(ctx context.Context, query string, args, v interface{}) error { 64 | return d.Driver.Exec(ctx, d.withComment(ctx, query), args, v) 65 | } 66 | 67 | // ExecContext calls ExecContext of the underlying driver, or fails if it is not supported. 68 | func (d *Driver) ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error) { 69 | drv, ok := d.Driver.(interface { 70 | ExecContext(context.Context, string, ...interface{}) (sql.Result, error) 71 | }) 72 | if !ok { 73 | return nil, fmt.Errorf("Driver.ExecContext is not supported") 74 | } 75 | return drv.ExecContext(ctx, d.withComment(ctx, query), args...) 76 | } 77 | 78 | // Tx wraps the underlying Tx command with a commenter. 79 | func (d *Driver) Tx(ctx context.Context) (dialect.Tx, error) { 80 | tx, err := d.Driver.Tx(ctx) 81 | if err != nil { 82 | return nil, err 83 | } 84 | return &Tx{tx, ctx, d.commenter}, nil 85 | } 86 | 87 | // BeginTx wraps the underlying transaction with commenter and calls the underlying driver BeginTx command if it's supported. 88 | func (d *Driver) BeginTx(ctx context.Context, opts *sql.TxOptions) (dialect.Tx, error) { 89 | drv, ok := d.Driver.(interface { 90 | BeginTx(context.Context, *sql.TxOptions) (dialect.Tx, error) 91 | }) 92 | if !ok { 93 | return nil, fmt.Errorf("Driver.BeginTx is not supported") 94 | } 95 | tx, err := drv.BeginTx(ctx, opts) 96 | if err != nil { 97 | return nil, err 98 | } 99 | return &Tx{tx, ctx, d.commenter}, nil 100 | } 101 | 102 | // Exec adds an SQL comment and calls the underlying transaction Exec method. 103 | func (d *Tx) Exec(ctx context.Context, query string, args, v interface{}) error { 104 | return d.Tx.Exec(ctx, d.withComment(ctx, query), args, v) 105 | } 106 | 107 | // ExecContext logs its params and calls the underlying transaction ExecContext method if it is supported. 108 | func (d *Tx) ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error) { 109 | tx, ok := d.Tx.(interface { 110 | ExecContext(context.Context, string, ...interface{}) (sql.Result, error) 111 | }) 112 | if !ok { 113 | return nil, fmt.Errorf("Tx.ExecContext is not supported") 114 | } 115 | return tx.ExecContext(ctx, d.withComment(ctx, query), args...) 116 | } 117 | 118 | // Query adds an SQL comment and calls the underlying transaction Query method. 119 | func (d *Tx) Query(ctx context.Context, query string, args, v interface{}) error { 120 | return d.Tx.Query(ctx, d.withComment(ctx, query), args, v) 121 | } 122 | 123 | // QueryContext logs its params and calls the underlying transaction QueryContext method if it is supported. 124 | func (d *Tx) QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error) { 125 | tx, ok := d.Tx.(interface { 126 | QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error) 127 | }) 128 | if !ok { 129 | return nil, fmt.Errorf("Tx.QueryContext is not supported") 130 | } 131 | return tx.QueryContext(ctx, d.withComment(ctx, query), args...) 132 | } 133 | 134 | // Commit commits the underlying Tx. 135 | func (d *Tx) Commit() error { 136 | return d.Tx.Commit() 137 | } 138 | 139 | // Rollback rolls back the underlying Tx. 140 | func (d *Tx) Rollback() error { 141 | return d.Tx.Rollback() 142 | } 143 | -------------------------------------------------------------------------------- /examples/ent/hook/hook.go: -------------------------------------------------------------------------------- 1 | // Code generated by entc, DO NOT EDIT. 2 | 3 | package hook 4 | 5 | import ( 6 | "context" 7 | "fmt" 8 | 9 | "ariga.io/sqlcomment/examples/ent" 10 | ) 11 | 12 | // The UserFunc type is an adapter to allow the use of ordinary 13 | // function as User mutator. 14 | type UserFunc func(context.Context, *ent.UserMutation) (ent.Value, error) 15 | 16 | // Mutate calls f(ctx, m). 17 | func (f UserFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { 18 | mv, ok := m.(*ent.UserMutation) 19 | if !ok { 20 | return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.UserMutation", m) 21 | } 22 | return f(ctx, mv) 23 | } 24 | 25 | // Condition is a hook condition function. 26 | type Condition func(context.Context, ent.Mutation) bool 27 | 28 | // And groups conditions with the AND operator. 29 | func And(first, second Condition, rest ...Condition) Condition { 30 | return func(ctx context.Context, m ent.Mutation) bool { 31 | if !first(ctx, m) || !second(ctx, m) { 32 | return false 33 | } 34 | for _, cond := range rest { 35 | if !cond(ctx, m) { 36 | return false 37 | } 38 | } 39 | return true 40 | } 41 | } 42 | 43 | // Or groups conditions with the OR operator. 44 | func Or(first, second Condition, rest ...Condition) Condition { 45 | return func(ctx context.Context, m ent.Mutation) bool { 46 | if first(ctx, m) || second(ctx, m) { 47 | return true 48 | } 49 | for _, cond := range rest { 50 | if cond(ctx, m) { 51 | return true 52 | } 53 | } 54 | return false 55 | } 56 | } 57 | 58 | // Not negates a given condition. 59 | func Not(cond Condition) Condition { 60 | return func(ctx context.Context, m ent.Mutation) bool { 61 | return !cond(ctx, m) 62 | } 63 | } 64 | 65 | // HasOp is a condition testing mutation operation. 66 | func HasOp(op ent.Op) Condition { 67 | return func(_ context.Context, m ent.Mutation) bool { 68 | return m.Op().Is(op) 69 | } 70 | } 71 | 72 | // HasAddedFields is a condition validating `.AddedField` on fields. 73 | func HasAddedFields(field string, fields ...string) Condition { 74 | return func(_ context.Context, m ent.Mutation) bool { 75 | if _, exists := m.AddedField(field); !exists { 76 | return false 77 | } 78 | for _, field := range fields { 79 | if _, exists := m.AddedField(field); !exists { 80 | return false 81 | } 82 | } 83 | return true 84 | } 85 | } 86 | 87 | // HasClearedFields is a condition validating `.FieldCleared` on fields. 88 | func HasClearedFields(field string, fields ...string) Condition { 89 | return func(_ context.Context, m ent.Mutation) bool { 90 | if exists := m.FieldCleared(field); !exists { 91 | return false 92 | } 93 | for _, field := range fields { 94 | if exists := m.FieldCleared(field); !exists { 95 | return false 96 | } 97 | } 98 | return true 99 | } 100 | } 101 | 102 | // HasFields is a condition validating `.Field` on fields. 103 | func HasFields(field string, fields ...string) Condition { 104 | return func(_ context.Context, m ent.Mutation) bool { 105 | if _, exists := m.Field(field); !exists { 106 | return false 107 | } 108 | for _, field := range fields { 109 | if _, exists := m.Field(field); !exists { 110 | return false 111 | } 112 | } 113 | return true 114 | } 115 | } 116 | 117 | // If executes the given hook under condition. 118 | // 119 | // hook.If(ComputeAverage, And(HasFields(...), HasAddedFields(...))) 120 | // 121 | func If(hk ent.Hook, cond Condition) ent.Hook { 122 | return func(next ent.Mutator) ent.Mutator { 123 | return ent.MutateFunc(func(ctx context.Context, m ent.Mutation) (ent.Value, error) { 124 | if cond(ctx, m) { 125 | return hk(next).Mutate(ctx, m) 126 | } 127 | return next.Mutate(ctx, m) 128 | }) 129 | } 130 | } 131 | 132 | // On executes the given hook only for the given operation. 133 | // 134 | // hook.On(Log, ent.Delete|ent.Create) 135 | // 136 | func On(hk ent.Hook, op ent.Op) ent.Hook { 137 | return If(hk, HasOp(op)) 138 | } 139 | 140 | // Unless skips the given hook only for the given operation. 141 | // 142 | // hook.Unless(Log, ent.Update|ent.UpdateOne) 143 | // 144 | func Unless(hk ent.Hook, op ent.Op) ent.Hook { 145 | return If(hk, Not(HasOp(op))) 146 | } 147 | 148 | // FixedError is a hook returning a fixed error. 149 | func FixedError(err error) ent.Hook { 150 | return func(ent.Mutator) ent.Mutator { 151 | return ent.MutateFunc(func(context.Context, ent.Mutation) (ent.Value, error) { 152 | return nil, err 153 | }) 154 | } 155 | } 156 | 157 | // Reject returns a hook that rejects all operations that match op. 158 | // 159 | // func (T) Hooks() []ent.Hook { 160 | // return []ent.Hook{ 161 | // Reject(ent.Delete|ent.Update), 162 | // } 163 | // } 164 | // 165 | func Reject(op ent.Op) ent.Hook { 166 | hk := FixedError(fmt.Errorf("%s operation is not allowed", op)) 167 | return On(hk, op) 168 | } 169 | 170 | // Chain acts as a list of hooks and is effectively immutable. 171 | // Once created, it will always hold the same set of hooks in the same order. 172 | type Chain struct { 173 | hooks []ent.Hook 174 | } 175 | 176 | // NewChain creates a new chain of hooks. 177 | func NewChain(hooks ...ent.Hook) Chain { 178 | return Chain{append([]ent.Hook(nil), hooks...)} 179 | } 180 | 181 | // Hook chains the list of hooks and returns the final hook. 182 | func (c Chain) Hook() ent.Hook { 183 | return func(mutator ent.Mutator) ent.Mutator { 184 | for i := len(c.hooks) - 1; i >= 0; i-- { 185 | mutator = c.hooks[i](mutator) 186 | } 187 | return mutator 188 | } 189 | } 190 | 191 | // Append extends a chain, adding the specified hook 192 | // as the last ones in the mutation flow. 193 | func (c Chain) Append(hooks ...ent.Hook) Chain { 194 | newHooks := make([]ent.Hook, 0, len(c.hooks)+len(hooks)) 195 | newHooks = append(newHooks, c.hooks...) 196 | newHooks = append(newHooks, hooks...) 197 | return Chain{newHooks} 198 | } 199 | 200 | // Extend extends a chain, adding the specified chain 201 | // as the last ones in the mutation flow. 202 | func (c Chain) Extend(chain Chain) Chain { 203 | return c.Append(chain.hooks...) 204 | } 205 | -------------------------------------------------------------------------------- /examples/ent/user_create.go: -------------------------------------------------------------------------------- 1 | // Code generated by entc, DO NOT EDIT. 2 | 3 | package ent 4 | 5 | import ( 6 | "context" 7 | "errors" 8 | "fmt" 9 | 10 | "ariga.io/sqlcomment/examples/ent/user" 11 | "entgo.io/ent/dialect/sql/sqlgraph" 12 | "entgo.io/ent/schema/field" 13 | ) 14 | 15 | // UserCreate is the builder for creating a User entity. 16 | type UserCreate struct { 17 | config 18 | mutation *UserMutation 19 | hooks []Hook 20 | } 21 | 22 | // SetName sets the "name" field. 23 | func (uc *UserCreate) SetName(s string) *UserCreate { 24 | uc.mutation.SetName(s) 25 | return uc 26 | } 27 | 28 | // Mutation returns the UserMutation object of the builder. 29 | func (uc *UserCreate) Mutation() *UserMutation { 30 | return uc.mutation 31 | } 32 | 33 | // Save creates the User in the database. 34 | func (uc *UserCreate) Save(ctx context.Context) (*User, error) { 35 | var ( 36 | err error 37 | node *User 38 | ) 39 | if len(uc.hooks) == 0 { 40 | if err = uc.check(); err != nil { 41 | return nil, err 42 | } 43 | node, err = uc.sqlSave(ctx) 44 | } else { 45 | var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { 46 | mutation, ok := m.(*UserMutation) 47 | if !ok { 48 | return nil, fmt.Errorf("unexpected mutation type %T", m) 49 | } 50 | if err = uc.check(); err != nil { 51 | return nil, err 52 | } 53 | uc.mutation = mutation 54 | if node, err = uc.sqlSave(ctx); err != nil { 55 | return nil, err 56 | } 57 | mutation.id = &node.ID 58 | mutation.done = true 59 | return node, err 60 | }) 61 | for i := len(uc.hooks) - 1; i >= 0; i-- { 62 | if uc.hooks[i] == nil { 63 | return nil, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)") 64 | } 65 | mut = uc.hooks[i](mut) 66 | } 67 | if _, err := mut.Mutate(ctx, uc.mutation); err != nil { 68 | return nil, err 69 | } 70 | } 71 | return node, err 72 | } 73 | 74 | // SaveX calls Save and panics if Save returns an error. 75 | func (uc *UserCreate) SaveX(ctx context.Context) *User { 76 | v, err := uc.Save(ctx) 77 | if err != nil { 78 | panic(err) 79 | } 80 | return v 81 | } 82 | 83 | // Exec executes the query. 84 | func (uc *UserCreate) Exec(ctx context.Context) error { 85 | _, err := uc.Save(ctx) 86 | return err 87 | } 88 | 89 | // ExecX is like Exec, but panics if an error occurs. 90 | func (uc *UserCreate) ExecX(ctx context.Context) { 91 | if err := uc.Exec(ctx); err != nil { 92 | panic(err) 93 | } 94 | } 95 | 96 | // check runs all checks and user-defined validators on the builder. 97 | func (uc *UserCreate) check() error { 98 | if _, ok := uc.mutation.Name(); !ok { 99 | return &ValidationError{Name: "name", err: errors.New(`ent: missing required field "name"`)} 100 | } 101 | return nil 102 | } 103 | 104 | func (uc *UserCreate) sqlSave(ctx context.Context) (*User, error) { 105 | _node, _spec := uc.createSpec() 106 | if err := sqlgraph.CreateNode(ctx, uc.driver, _spec); err != nil { 107 | if sqlgraph.IsConstraintError(err) { 108 | err = &ConstraintError{err.Error(), err} 109 | } 110 | return nil, err 111 | } 112 | id := _spec.ID.Value.(int64) 113 | _node.ID = int(id) 114 | return _node, nil 115 | } 116 | 117 | func (uc *UserCreate) createSpec() (*User, *sqlgraph.CreateSpec) { 118 | var ( 119 | _node = &User{config: uc.config} 120 | _spec = &sqlgraph.CreateSpec{ 121 | Table: user.Table, 122 | ID: &sqlgraph.FieldSpec{ 123 | Type: field.TypeInt, 124 | Column: user.FieldID, 125 | }, 126 | } 127 | ) 128 | if value, ok := uc.mutation.Name(); ok { 129 | _spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{ 130 | Type: field.TypeString, 131 | Value: value, 132 | Column: user.FieldName, 133 | }) 134 | _node.Name = value 135 | } 136 | return _node, _spec 137 | } 138 | 139 | // UserCreateBulk is the builder for creating many User entities in bulk. 140 | type UserCreateBulk struct { 141 | config 142 | builders []*UserCreate 143 | } 144 | 145 | // Save creates the User entities in the database. 146 | func (ucb *UserCreateBulk) Save(ctx context.Context) ([]*User, error) { 147 | specs := make([]*sqlgraph.CreateSpec, len(ucb.builders)) 148 | nodes := make([]*User, len(ucb.builders)) 149 | mutators := make([]Mutator, len(ucb.builders)) 150 | for i := range ucb.builders { 151 | func(i int, root context.Context) { 152 | builder := ucb.builders[i] 153 | var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { 154 | mutation, ok := m.(*UserMutation) 155 | if !ok { 156 | return nil, fmt.Errorf("unexpected mutation type %T", m) 157 | } 158 | if err := builder.check(); err != nil { 159 | return nil, err 160 | } 161 | builder.mutation = mutation 162 | nodes[i], specs[i] = builder.createSpec() 163 | var err error 164 | if i < len(mutators)-1 { 165 | _, err = mutators[i+1].Mutate(root, ucb.builders[i+1].mutation) 166 | } else { 167 | spec := &sqlgraph.BatchCreateSpec{Nodes: specs} 168 | // Invoke the actual operation on the latest mutation in the chain. 169 | if err = sqlgraph.BatchCreate(ctx, ucb.driver, spec); err != nil { 170 | if sqlgraph.IsConstraintError(err) { 171 | err = &ConstraintError{err.Error(), err} 172 | } 173 | } 174 | } 175 | if err != nil { 176 | return nil, err 177 | } 178 | mutation.id = &nodes[i].ID 179 | mutation.done = true 180 | if specs[i].ID.Value != nil { 181 | id := specs[i].ID.Value.(int64) 182 | nodes[i].ID = int(id) 183 | } 184 | return nodes[i], nil 185 | }) 186 | for i := len(builder.hooks) - 1; i >= 0; i-- { 187 | mut = builder.hooks[i](mut) 188 | } 189 | mutators[i] = mut 190 | }(i, ctx) 191 | } 192 | if len(mutators) > 0 { 193 | if _, err := mutators[0].Mutate(ctx, ucb.builders[0].mutation); err != nil { 194 | return nil, err 195 | } 196 | } 197 | return nodes, nil 198 | } 199 | 200 | // SaveX is like Save, but panics if an error occurs. 201 | func (ucb *UserCreateBulk) SaveX(ctx context.Context) []*User { 202 | v, err := ucb.Save(ctx) 203 | if err != nil { 204 | panic(err) 205 | } 206 | return v 207 | } 208 | 209 | // Exec executes the query. 210 | func (ucb *UserCreateBulk) Exec(ctx context.Context) error { 211 | _, err := ucb.Save(ctx) 212 | return err 213 | } 214 | 215 | // ExecX is like Exec, but panics if an error occurs. 216 | func (ucb *UserCreateBulk) ExecX(ctx context.Context) { 217 | if err := ucb.Exec(ctx); err != nil { 218 | panic(err) 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /examples/ent/client.go: -------------------------------------------------------------------------------- 1 | // Code generated by entc, DO NOT EDIT. 2 | 3 | package ent 4 | 5 | import ( 6 | "context" 7 | "fmt" 8 | "log" 9 | 10 | "ariga.io/sqlcomment/examples/ent/migrate" 11 | 12 | "ariga.io/sqlcomment/examples/ent/user" 13 | 14 | "entgo.io/ent/dialect" 15 | "entgo.io/ent/dialect/sql" 16 | ) 17 | 18 | // Client is the client that holds all ent builders. 19 | type Client struct { 20 | config 21 | // Schema is the client for creating, migrating and dropping schema. 22 | Schema *migrate.Schema 23 | // User is the client for interacting with the User builders. 24 | User *UserClient 25 | } 26 | 27 | // NewClient creates a new client configured with the given options. 28 | func NewClient(opts ...Option) *Client { 29 | cfg := config{log: log.Println, hooks: &hooks{}} 30 | cfg.options(opts...) 31 | client := &Client{config: cfg} 32 | client.init() 33 | return client 34 | } 35 | 36 | func (c *Client) init() { 37 | c.Schema = migrate.NewSchema(c.driver) 38 | c.User = NewUserClient(c.config) 39 | } 40 | 41 | // Open opens a database/sql.DB specified by the driver name and 42 | // the data source name, and returns a new client attached to it. 43 | // Optional parameters can be added for configuring the client. 44 | func Open(driverName, dataSourceName string, options ...Option) (*Client, error) { 45 | switch driverName { 46 | case dialect.MySQL, dialect.Postgres, dialect.SQLite: 47 | drv, err := sql.Open(driverName, dataSourceName) 48 | if err != nil { 49 | return nil, err 50 | } 51 | return NewClient(append(options, Driver(drv))...), nil 52 | default: 53 | return nil, fmt.Errorf("unsupported driver: %q", driverName) 54 | } 55 | } 56 | 57 | // Tx returns a new transactional client. The provided context 58 | // is used until the transaction is committed or rolled back. 59 | func (c *Client) Tx(ctx context.Context) (*Tx, error) { 60 | if _, ok := c.driver.(*txDriver); ok { 61 | return nil, fmt.Errorf("ent: cannot start a transaction within a transaction") 62 | } 63 | tx, err := newTx(ctx, c.driver) 64 | if err != nil { 65 | return nil, fmt.Errorf("ent: starting a transaction: %w", err) 66 | } 67 | cfg := c.config 68 | cfg.driver = tx 69 | return &Tx{ 70 | ctx: ctx, 71 | config: cfg, 72 | User: NewUserClient(cfg), 73 | }, nil 74 | } 75 | 76 | // BeginTx returns a transactional client with specified options. 77 | func (c *Client) BeginTx(ctx context.Context, opts *sql.TxOptions) (*Tx, error) { 78 | if _, ok := c.driver.(*txDriver); ok { 79 | return nil, fmt.Errorf("ent: cannot start a transaction within a transaction") 80 | } 81 | tx, err := c.driver.(interface { 82 | BeginTx(context.Context, *sql.TxOptions) (dialect.Tx, error) 83 | }).BeginTx(ctx, opts) 84 | if err != nil { 85 | return nil, fmt.Errorf("ent: starting a transaction: %w", err) 86 | } 87 | cfg := c.config 88 | cfg.driver = &txDriver{tx: tx, drv: c.driver} 89 | return &Tx{ 90 | config: cfg, 91 | User: NewUserClient(cfg), 92 | }, nil 93 | } 94 | 95 | // Debug returns a new debug-client. It's used to get verbose logging on specific operations. 96 | // 97 | // client.Debug(). 98 | // User. 99 | // Query(). 100 | // Count(ctx) 101 | // 102 | func (c *Client) Debug() *Client { 103 | if c.debug { 104 | return c 105 | } 106 | cfg := c.config 107 | cfg.driver = dialect.Debug(c.driver, c.log) 108 | client := &Client{config: cfg} 109 | client.init() 110 | return client 111 | } 112 | 113 | // Close closes the database connection and prevents new queries from starting. 114 | func (c *Client) Close() error { 115 | return c.driver.Close() 116 | } 117 | 118 | // Use adds the mutation hooks to all the entity clients. 119 | // In order to add hooks to a specific client, call: `client.Node.Use(...)`. 120 | func (c *Client) Use(hooks ...Hook) { 121 | c.User.Use(hooks...) 122 | } 123 | 124 | // UserClient is a client for the User schema. 125 | type UserClient struct { 126 | config 127 | } 128 | 129 | // NewUserClient returns a client for the User from the given config. 130 | func NewUserClient(c config) *UserClient { 131 | return &UserClient{config: c} 132 | } 133 | 134 | // Use adds a list of mutation hooks to the hooks stack. 135 | // A call to `Use(f, g, h)` equals to `user.Hooks(f(g(h())))`. 136 | func (c *UserClient) Use(hooks ...Hook) { 137 | c.hooks.User = append(c.hooks.User, hooks...) 138 | } 139 | 140 | // Create returns a create builder for User. 141 | func (c *UserClient) Create() *UserCreate { 142 | mutation := newUserMutation(c.config, OpCreate) 143 | return &UserCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} 144 | } 145 | 146 | // CreateBulk returns a builder for creating a bulk of User entities. 147 | func (c *UserClient) CreateBulk(builders ...*UserCreate) *UserCreateBulk { 148 | return &UserCreateBulk{config: c.config, builders: builders} 149 | } 150 | 151 | // Update returns an update builder for User. 152 | func (c *UserClient) Update() *UserUpdate { 153 | mutation := newUserMutation(c.config, OpUpdate) 154 | return &UserUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} 155 | } 156 | 157 | // UpdateOne returns an update builder for the given entity. 158 | func (c *UserClient) UpdateOne(u *User) *UserUpdateOne { 159 | mutation := newUserMutation(c.config, OpUpdateOne, withUser(u)) 160 | return &UserUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} 161 | } 162 | 163 | // UpdateOneID returns an update builder for the given id. 164 | func (c *UserClient) UpdateOneID(id int) *UserUpdateOne { 165 | mutation := newUserMutation(c.config, OpUpdateOne, withUserID(id)) 166 | return &UserUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} 167 | } 168 | 169 | // Delete returns a delete builder for User. 170 | func (c *UserClient) Delete() *UserDelete { 171 | mutation := newUserMutation(c.config, OpDelete) 172 | return &UserDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} 173 | } 174 | 175 | // DeleteOne returns a delete builder for the given entity. 176 | func (c *UserClient) DeleteOne(u *User) *UserDeleteOne { 177 | return c.DeleteOneID(u.ID) 178 | } 179 | 180 | // DeleteOneID returns a delete builder for the given id. 181 | func (c *UserClient) DeleteOneID(id int) *UserDeleteOne { 182 | builder := c.Delete().Where(user.ID(id)) 183 | builder.mutation.id = &id 184 | builder.mutation.op = OpDeleteOne 185 | return &UserDeleteOne{builder} 186 | } 187 | 188 | // Query returns a query builder for User. 189 | func (c *UserClient) Query() *UserQuery { 190 | return &UserQuery{ 191 | config: c.config, 192 | } 193 | } 194 | 195 | // Get returns a User entity by its id. 196 | func (c *UserClient) Get(ctx context.Context, id int) (*User, error) { 197 | return c.Query().Where(user.ID(id)).Only(ctx) 198 | } 199 | 200 | // GetX is like Get, but panics if an error occurs. 201 | func (c *UserClient) GetX(ctx context.Context, id int) *User { 202 | obj, err := c.Get(ctx, id) 203 | if err != nil { 204 | panic(err) 205 | } 206 | return obj 207 | } 208 | 209 | // Hooks returns the client hooks. 210 | func (c *UserClient) Hooks() []Hook { 211 | return c.hooks.User 212 | } 213 | -------------------------------------------------------------------------------- /examples/ent/tx.go: -------------------------------------------------------------------------------- 1 | // Code generated by entc, DO NOT EDIT. 2 | 3 | package ent 4 | 5 | import ( 6 | "context" 7 | "sync" 8 | 9 | "entgo.io/ent/dialect" 10 | ) 11 | 12 | // Tx is a transactional client that is created by calling Client.Tx(). 13 | type Tx struct { 14 | config 15 | // User is the client for interacting with the User builders. 16 | User *UserClient 17 | 18 | // lazily loaded. 19 | client *Client 20 | clientOnce sync.Once 21 | 22 | // completion callbacks. 23 | mu sync.Mutex 24 | onCommit []CommitHook 25 | onRollback []RollbackHook 26 | 27 | // ctx lives for the life of the transaction. It is 28 | // the same context used by the underlying connection. 29 | ctx context.Context 30 | } 31 | 32 | type ( 33 | // Committer is the interface that wraps the Committer method. 34 | Committer interface { 35 | Commit(context.Context, *Tx) error 36 | } 37 | 38 | // The CommitFunc type is an adapter to allow the use of ordinary 39 | // function as a Committer. If f is a function with the appropriate 40 | // signature, CommitFunc(f) is a Committer that calls f. 41 | CommitFunc func(context.Context, *Tx) error 42 | 43 | // CommitHook defines the "commit middleware". A function that gets a Committer 44 | // and returns a Committer. For example: 45 | // 46 | // hook := func(next ent.Committer) ent.Committer { 47 | // return ent.CommitFunc(func(context.Context, tx *ent.Tx) error { 48 | // // Do some stuff before. 49 | // if err := next.Commit(ctx, tx); err != nil { 50 | // return err 51 | // } 52 | // // Do some stuff after. 53 | // return nil 54 | // }) 55 | // } 56 | // 57 | CommitHook func(Committer) Committer 58 | ) 59 | 60 | // Commit calls f(ctx, m). 61 | func (f CommitFunc) Commit(ctx context.Context, tx *Tx) error { 62 | return f(ctx, tx) 63 | } 64 | 65 | // Commit commits the transaction. 66 | func (tx *Tx) Commit() error { 67 | txDriver := tx.config.driver.(*txDriver) 68 | var fn Committer = CommitFunc(func(context.Context, *Tx) error { 69 | return txDriver.tx.Commit() 70 | }) 71 | tx.mu.Lock() 72 | hooks := append([]CommitHook(nil), tx.onCommit...) 73 | tx.mu.Unlock() 74 | for i := len(hooks) - 1; i >= 0; i-- { 75 | fn = hooks[i](fn) 76 | } 77 | return fn.Commit(tx.ctx, tx) 78 | } 79 | 80 | // OnCommit adds a hook to call on commit. 81 | func (tx *Tx) OnCommit(f CommitHook) { 82 | tx.mu.Lock() 83 | defer tx.mu.Unlock() 84 | tx.onCommit = append(tx.onCommit, f) 85 | } 86 | 87 | type ( 88 | // Rollbacker is the interface that wraps the Rollbacker method. 89 | Rollbacker interface { 90 | Rollback(context.Context, *Tx) error 91 | } 92 | 93 | // The RollbackFunc type is an adapter to allow the use of ordinary 94 | // function as a Rollbacker. If f is a function with the appropriate 95 | // signature, RollbackFunc(f) is a Rollbacker that calls f. 96 | RollbackFunc func(context.Context, *Tx) error 97 | 98 | // RollbackHook defines the "rollback middleware". A function that gets a Rollbacker 99 | // and returns a Rollbacker. For example: 100 | // 101 | // hook := func(next ent.Rollbacker) ent.Rollbacker { 102 | // return ent.RollbackFunc(func(context.Context, tx *ent.Tx) error { 103 | // // Do some stuff before. 104 | // if err := next.Rollback(ctx, tx); err != nil { 105 | // return err 106 | // } 107 | // // Do some stuff after. 108 | // return nil 109 | // }) 110 | // } 111 | // 112 | RollbackHook func(Rollbacker) Rollbacker 113 | ) 114 | 115 | // Rollback calls f(ctx, m). 116 | func (f RollbackFunc) Rollback(ctx context.Context, tx *Tx) error { 117 | return f(ctx, tx) 118 | } 119 | 120 | // Rollback rollbacks the transaction. 121 | func (tx *Tx) Rollback() error { 122 | txDriver := tx.config.driver.(*txDriver) 123 | var fn Rollbacker = RollbackFunc(func(context.Context, *Tx) error { 124 | return txDriver.tx.Rollback() 125 | }) 126 | tx.mu.Lock() 127 | hooks := append([]RollbackHook(nil), tx.onRollback...) 128 | tx.mu.Unlock() 129 | for i := len(hooks) - 1; i >= 0; i-- { 130 | fn = hooks[i](fn) 131 | } 132 | return fn.Rollback(tx.ctx, tx) 133 | } 134 | 135 | // OnRollback adds a hook to call on rollback. 136 | func (tx *Tx) OnRollback(f RollbackHook) { 137 | tx.mu.Lock() 138 | defer tx.mu.Unlock() 139 | tx.onRollback = append(tx.onRollback, f) 140 | } 141 | 142 | // Client returns a Client that binds to current transaction. 143 | func (tx *Tx) Client() *Client { 144 | tx.clientOnce.Do(func() { 145 | tx.client = &Client{config: tx.config} 146 | tx.client.init() 147 | }) 148 | return tx.client 149 | } 150 | 151 | func (tx *Tx) init() { 152 | tx.User = NewUserClient(tx.config) 153 | } 154 | 155 | // txDriver wraps the given dialect.Tx with a nop dialect.Driver implementation. 156 | // The idea is to support transactions without adding any extra code to the builders. 157 | // When a builder calls to driver.Tx(), it gets the same dialect.Tx instance. 158 | // Commit and Rollback are nop for the internal builders and the user must call one 159 | // of them in order to commit or rollback the transaction. 160 | // 161 | // If a closed transaction is embedded in one of the generated entities, and the entity 162 | // applies a query, for example: User.QueryXXX(), the query will be executed 163 | // through the driver which created this transaction. 164 | // 165 | // Note that txDriver is not goroutine safe. 166 | type txDriver struct { 167 | // the driver we started the transaction from. 168 | drv dialect.Driver 169 | // tx is the underlying transaction. 170 | tx dialect.Tx 171 | } 172 | 173 | // newTx creates a new transactional driver. 174 | func newTx(ctx context.Context, drv dialect.Driver) (*txDriver, error) { 175 | tx, err := drv.Tx(ctx) 176 | if err != nil { 177 | return nil, err 178 | } 179 | return &txDriver{tx: tx, drv: drv}, nil 180 | } 181 | 182 | // Tx returns the transaction wrapper (txDriver) to avoid Commit or Rollback calls 183 | // from the internal builders. Should be called only by the internal builders. 184 | func (tx *txDriver) Tx(context.Context) (dialect.Tx, error) { return tx, nil } 185 | 186 | // Dialect returns the dialect of the driver we started the transaction from. 187 | func (tx *txDriver) Dialect() string { return tx.drv.Dialect() } 188 | 189 | // Close is a nop close. 190 | func (*txDriver) Close() error { return nil } 191 | 192 | // Commit is a nop commit for the internal builders. 193 | // User must call `Tx.Commit` in order to commit the transaction. 194 | func (*txDriver) Commit() error { return nil } 195 | 196 | // Rollback is a nop rollback for the internal builders. 197 | // User must call `Tx.Rollback` in order to rollback the transaction. 198 | func (*txDriver) Rollback() error { return nil } 199 | 200 | // Exec calls tx.Exec. 201 | func (tx *txDriver) Exec(ctx context.Context, query string, args, v interface{}) error { 202 | return tx.tx.Exec(ctx, query, args, v) 203 | } 204 | 205 | // Query calls tx.Query. 206 | func (tx *txDriver) Query(ctx context.Context, query string, args, v interface{}) error { 207 | return tx.tx.Query(ctx, query, args, v) 208 | } 209 | 210 | var _ dialect.Driver = (*txDriver)(nil) 211 | -------------------------------------------------------------------------------- /examples/ent/user/where.go: -------------------------------------------------------------------------------- 1 | // Code generated by entc, DO NOT EDIT. 2 | 3 | package user 4 | 5 | import ( 6 | "ariga.io/sqlcomment/examples/ent/predicate" 7 | "entgo.io/ent/dialect/sql" 8 | ) 9 | 10 | // ID filters vertices based on their ID field. 11 | func ID(id int) predicate.User { 12 | return predicate.User(func(s *sql.Selector) { 13 | s.Where(sql.EQ(s.C(FieldID), id)) 14 | }) 15 | } 16 | 17 | // IDEQ applies the EQ predicate on the ID field. 18 | func IDEQ(id int) predicate.User { 19 | return predicate.User(func(s *sql.Selector) { 20 | s.Where(sql.EQ(s.C(FieldID), id)) 21 | }) 22 | } 23 | 24 | // IDNEQ applies the NEQ predicate on the ID field. 25 | func IDNEQ(id int) predicate.User { 26 | return predicate.User(func(s *sql.Selector) { 27 | s.Where(sql.NEQ(s.C(FieldID), id)) 28 | }) 29 | } 30 | 31 | // IDIn applies the In predicate on the ID field. 32 | func IDIn(ids ...int) predicate.User { 33 | return predicate.User(func(s *sql.Selector) { 34 | // if not arguments were provided, append the FALSE constants, 35 | // since we can't apply "IN ()". This will make this predicate falsy. 36 | if len(ids) == 0 { 37 | s.Where(sql.False()) 38 | return 39 | } 40 | v := make([]interface{}, len(ids)) 41 | for i := range v { 42 | v[i] = ids[i] 43 | } 44 | s.Where(sql.In(s.C(FieldID), v...)) 45 | }) 46 | } 47 | 48 | // IDNotIn applies the NotIn predicate on the ID field. 49 | func IDNotIn(ids ...int) predicate.User { 50 | return predicate.User(func(s *sql.Selector) { 51 | // if not arguments were provided, append the FALSE constants, 52 | // since we can't apply "IN ()". This will make this predicate falsy. 53 | if len(ids) == 0 { 54 | s.Where(sql.False()) 55 | return 56 | } 57 | v := make([]interface{}, len(ids)) 58 | for i := range v { 59 | v[i] = ids[i] 60 | } 61 | s.Where(sql.NotIn(s.C(FieldID), v...)) 62 | }) 63 | } 64 | 65 | // IDGT applies the GT predicate on the ID field. 66 | func IDGT(id int) predicate.User { 67 | return predicate.User(func(s *sql.Selector) { 68 | s.Where(sql.GT(s.C(FieldID), id)) 69 | }) 70 | } 71 | 72 | // IDGTE applies the GTE predicate on the ID field. 73 | func IDGTE(id int) predicate.User { 74 | return predicate.User(func(s *sql.Selector) { 75 | s.Where(sql.GTE(s.C(FieldID), id)) 76 | }) 77 | } 78 | 79 | // IDLT applies the LT predicate on the ID field. 80 | func IDLT(id int) predicate.User { 81 | return predicate.User(func(s *sql.Selector) { 82 | s.Where(sql.LT(s.C(FieldID), id)) 83 | }) 84 | } 85 | 86 | // IDLTE applies the LTE predicate on the ID field. 87 | func IDLTE(id int) predicate.User { 88 | return predicate.User(func(s *sql.Selector) { 89 | s.Where(sql.LTE(s.C(FieldID), id)) 90 | }) 91 | } 92 | 93 | // Name applies equality check predicate on the "name" field. It's identical to NameEQ. 94 | func Name(v string) predicate.User { 95 | return predicate.User(func(s *sql.Selector) { 96 | s.Where(sql.EQ(s.C(FieldName), v)) 97 | }) 98 | } 99 | 100 | // NameEQ applies the EQ predicate on the "name" field. 101 | func NameEQ(v string) predicate.User { 102 | return predicate.User(func(s *sql.Selector) { 103 | s.Where(sql.EQ(s.C(FieldName), v)) 104 | }) 105 | } 106 | 107 | // NameNEQ applies the NEQ predicate on the "name" field. 108 | func NameNEQ(v string) predicate.User { 109 | return predicate.User(func(s *sql.Selector) { 110 | s.Where(sql.NEQ(s.C(FieldName), v)) 111 | }) 112 | } 113 | 114 | // NameIn applies the In predicate on the "name" field. 115 | func NameIn(vs ...string) predicate.User { 116 | v := make([]interface{}, len(vs)) 117 | for i := range v { 118 | v[i] = vs[i] 119 | } 120 | return predicate.User(func(s *sql.Selector) { 121 | // if not arguments were provided, append the FALSE constants, 122 | // since we can't apply "IN ()". This will make this predicate falsy. 123 | if len(v) == 0 { 124 | s.Where(sql.False()) 125 | return 126 | } 127 | s.Where(sql.In(s.C(FieldName), v...)) 128 | }) 129 | } 130 | 131 | // NameNotIn applies the NotIn predicate on the "name" field. 132 | func NameNotIn(vs ...string) predicate.User { 133 | v := make([]interface{}, len(vs)) 134 | for i := range v { 135 | v[i] = vs[i] 136 | } 137 | return predicate.User(func(s *sql.Selector) { 138 | // if not arguments were provided, append the FALSE constants, 139 | // since we can't apply "IN ()". This will make this predicate falsy. 140 | if len(v) == 0 { 141 | s.Where(sql.False()) 142 | return 143 | } 144 | s.Where(sql.NotIn(s.C(FieldName), v...)) 145 | }) 146 | } 147 | 148 | // NameGT applies the GT predicate on the "name" field. 149 | func NameGT(v string) predicate.User { 150 | return predicate.User(func(s *sql.Selector) { 151 | s.Where(sql.GT(s.C(FieldName), v)) 152 | }) 153 | } 154 | 155 | // NameGTE applies the GTE predicate on the "name" field. 156 | func NameGTE(v string) predicate.User { 157 | return predicate.User(func(s *sql.Selector) { 158 | s.Where(sql.GTE(s.C(FieldName), v)) 159 | }) 160 | } 161 | 162 | // NameLT applies the LT predicate on the "name" field. 163 | func NameLT(v string) predicate.User { 164 | return predicate.User(func(s *sql.Selector) { 165 | s.Where(sql.LT(s.C(FieldName), v)) 166 | }) 167 | } 168 | 169 | // NameLTE applies the LTE predicate on the "name" field. 170 | func NameLTE(v string) predicate.User { 171 | return predicate.User(func(s *sql.Selector) { 172 | s.Where(sql.LTE(s.C(FieldName), v)) 173 | }) 174 | } 175 | 176 | // NameContains applies the Contains predicate on the "name" field. 177 | func NameContains(v string) predicate.User { 178 | return predicate.User(func(s *sql.Selector) { 179 | s.Where(sql.Contains(s.C(FieldName), v)) 180 | }) 181 | } 182 | 183 | // NameHasPrefix applies the HasPrefix predicate on the "name" field. 184 | func NameHasPrefix(v string) predicate.User { 185 | return predicate.User(func(s *sql.Selector) { 186 | s.Where(sql.HasPrefix(s.C(FieldName), v)) 187 | }) 188 | } 189 | 190 | // NameHasSuffix applies the HasSuffix predicate on the "name" field. 191 | func NameHasSuffix(v string) predicate.User { 192 | return predicate.User(func(s *sql.Selector) { 193 | s.Where(sql.HasSuffix(s.C(FieldName), v)) 194 | }) 195 | } 196 | 197 | // NameEqualFold applies the EqualFold predicate on the "name" field. 198 | func NameEqualFold(v string) predicate.User { 199 | return predicate.User(func(s *sql.Selector) { 200 | s.Where(sql.EqualFold(s.C(FieldName), v)) 201 | }) 202 | } 203 | 204 | // NameContainsFold applies the ContainsFold predicate on the "name" field. 205 | func NameContainsFold(v string) predicate.User { 206 | return predicate.User(func(s *sql.Selector) { 207 | s.Where(sql.ContainsFold(s.C(FieldName), v)) 208 | }) 209 | } 210 | 211 | // And groups predicates with the AND operator between them. 212 | func And(predicates ...predicate.User) predicate.User { 213 | return predicate.User(func(s *sql.Selector) { 214 | s1 := s.Clone().SetP(nil) 215 | for _, p := range predicates { 216 | p(s1) 217 | } 218 | s.Where(s1.P()) 219 | }) 220 | } 221 | 222 | // Or groups predicates with the OR operator between them. 223 | func Or(predicates ...predicate.User) predicate.User { 224 | return predicate.User(func(s *sql.Selector) { 225 | s1 := s.Clone().SetP(nil) 226 | for i, p := range predicates { 227 | if i > 0 { 228 | s1.Or() 229 | } 230 | p(s1) 231 | } 232 | s.Where(s1.P()) 233 | }) 234 | } 235 | 236 | // Not applies the not operator on the given predicate. 237 | func Not(p predicate.User) predicate.User { 238 | return predicate.User(func(s *sql.Selector) { 239 | p(s.Not()) 240 | }) 241 | } 242 | -------------------------------------------------------------------------------- /examples/ent/ent.go: -------------------------------------------------------------------------------- 1 | // Code generated by entc, DO NOT EDIT. 2 | 3 | package ent 4 | 5 | import ( 6 | "errors" 7 | "fmt" 8 | 9 | "ariga.io/sqlcomment/examples/ent/user" 10 | "entgo.io/ent" 11 | "entgo.io/ent/dialect/sql" 12 | ) 13 | 14 | // ent aliases to avoid import conflicts in user's code. 15 | type ( 16 | Op = ent.Op 17 | Hook = ent.Hook 18 | Value = ent.Value 19 | Query = ent.Query 20 | Policy = ent.Policy 21 | Mutator = ent.Mutator 22 | Mutation = ent.Mutation 23 | MutateFunc = ent.MutateFunc 24 | ) 25 | 26 | // OrderFunc applies an ordering on the sql selector. 27 | type OrderFunc func(*sql.Selector) 28 | 29 | // columnChecker returns a function indicates if the column exists in the given column. 30 | func columnChecker(table string) func(string) error { 31 | checks := map[string]func(string) bool{ 32 | user.Table: user.ValidColumn, 33 | } 34 | check, ok := checks[table] 35 | if !ok { 36 | return func(string) error { 37 | return fmt.Errorf("unknown table %q", table) 38 | } 39 | } 40 | return func(column string) error { 41 | if !check(column) { 42 | return fmt.Errorf("unknown column %q for table %q", column, table) 43 | } 44 | return nil 45 | } 46 | } 47 | 48 | // Asc applies the given fields in ASC order. 49 | func Asc(fields ...string) OrderFunc { 50 | return func(s *sql.Selector) { 51 | check := columnChecker(s.TableName()) 52 | for _, f := range fields { 53 | if err := check(f); err != nil { 54 | s.AddError(&ValidationError{Name: f, err: fmt.Errorf("ent: %w", err)}) 55 | } 56 | s.OrderBy(sql.Asc(s.C(f))) 57 | } 58 | } 59 | } 60 | 61 | // Desc applies the given fields in DESC order. 62 | func Desc(fields ...string) OrderFunc { 63 | return func(s *sql.Selector) { 64 | check := columnChecker(s.TableName()) 65 | for _, f := range fields { 66 | if err := check(f); err != nil { 67 | s.AddError(&ValidationError{Name: f, err: fmt.Errorf("ent: %w", err)}) 68 | } 69 | s.OrderBy(sql.Desc(s.C(f))) 70 | } 71 | } 72 | } 73 | 74 | // AggregateFunc applies an aggregation step on the group-by traversal/selector. 75 | type AggregateFunc func(*sql.Selector) string 76 | 77 | // As is a pseudo aggregation function for renaming another other functions with custom names. For example: 78 | // 79 | // GroupBy(field1, field2). 80 | // Aggregate(ent.As(ent.Sum(field1), "sum_field1"), (ent.As(ent.Sum(field2), "sum_field2")). 81 | // Scan(ctx, &v) 82 | // 83 | func As(fn AggregateFunc, end string) AggregateFunc { 84 | return func(s *sql.Selector) string { 85 | return sql.As(fn(s), end) 86 | } 87 | } 88 | 89 | // Count applies the "count" aggregation function on each group. 90 | func Count() AggregateFunc { 91 | return func(s *sql.Selector) string { 92 | return sql.Count("*") 93 | } 94 | } 95 | 96 | // Max applies the "max" aggregation function on the given field of each group. 97 | func Max(field string) AggregateFunc { 98 | return func(s *sql.Selector) string { 99 | check := columnChecker(s.TableName()) 100 | if err := check(field); err != nil { 101 | s.AddError(&ValidationError{Name: field, err: fmt.Errorf("ent: %w", err)}) 102 | return "" 103 | } 104 | return sql.Max(s.C(field)) 105 | } 106 | } 107 | 108 | // Mean applies the "mean" aggregation function on the given field of each group. 109 | func Mean(field string) AggregateFunc { 110 | return func(s *sql.Selector) string { 111 | check := columnChecker(s.TableName()) 112 | if err := check(field); err != nil { 113 | s.AddError(&ValidationError{Name: field, err: fmt.Errorf("ent: %w", err)}) 114 | return "" 115 | } 116 | return sql.Avg(s.C(field)) 117 | } 118 | } 119 | 120 | // Min applies the "min" aggregation function on the given field of each group. 121 | func Min(field string) AggregateFunc { 122 | return func(s *sql.Selector) string { 123 | check := columnChecker(s.TableName()) 124 | if err := check(field); err != nil { 125 | s.AddError(&ValidationError{Name: field, err: fmt.Errorf("ent: %w", err)}) 126 | return "" 127 | } 128 | return sql.Min(s.C(field)) 129 | } 130 | } 131 | 132 | // Sum applies the "sum" aggregation function on the given field of each group. 133 | func Sum(field string) AggregateFunc { 134 | return func(s *sql.Selector) string { 135 | check := columnChecker(s.TableName()) 136 | if err := check(field); err != nil { 137 | s.AddError(&ValidationError{Name: field, err: fmt.Errorf("ent: %w", err)}) 138 | return "" 139 | } 140 | return sql.Sum(s.C(field)) 141 | } 142 | } 143 | 144 | // ValidationError returns when validating a field fails. 145 | type ValidationError struct { 146 | Name string // Field or edge name. 147 | err error 148 | } 149 | 150 | // Error implements the error interface. 151 | func (e *ValidationError) Error() string { 152 | return e.err.Error() 153 | } 154 | 155 | // Unwrap implements the errors.Wrapper interface. 156 | func (e *ValidationError) Unwrap() error { 157 | return e.err 158 | } 159 | 160 | // IsValidationError returns a boolean indicating whether the error is a validation error. 161 | func IsValidationError(err error) bool { 162 | if err == nil { 163 | return false 164 | } 165 | var e *ValidationError 166 | return errors.As(err, &e) 167 | } 168 | 169 | // NotFoundError returns when trying to fetch a specific entity and it was not found in the database. 170 | type NotFoundError struct { 171 | label string 172 | } 173 | 174 | // Error implements the error interface. 175 | func (e *NotFoundError) Error() string { 176 | return "ent: " + e.label + " not found" 177 | } 178 | 179 | // IsNotFound returns a boolean indicating whether the error is a not found error. 180 | func IsNotFound(err error) bool { 181 | if err == nil { 182 | return false 183 | } 184 | var e *NotFoundError 185 | return errors.As(err, &e) 186 | } 187 | 188 | // MaskNotFound masks not found error. 189 | func MaskNotFound(err error) error { 190 | if IsNotFound(err) { 191 | return nil 192 | } 193 | return err 194 | } 195 | 196 | // NotSingularError returns when trying to fetch a singular entity and more then one was found in the database. 197 | type NotSingularError struct { 198 | label string 199 | } 200 | 201 | // Error implements the error interface. 202 | func (e *NotSingularError) Error() string { 203 | return "ent: " + e.label + " not singular" 204 | } 205 | 206 | // IsNotSingular returns a boolean indicating whether the error is a not singular error. 207 | func IsNotSingular(err error) bool { 208 | if err == nil { 209 | return false 210 | } 211 | var e *NotSingularError 212 | return errors.As(err, &e) 213 | } 214 | 215 | // NotLoadedError returns when trying to get a node that was not loaded by the query. 216 | type NotLoadedError struct { 217 | edge string 218 | } 219 | 220 | // Error implements the error interface. 221 | func (e *NotLoadedError) Error() string { 222 | return "ent: " + e.edge + " edge was not loaded" 223 | } 224 | 225 | // IsNotLoaded returns a boolean indicating whether the error is a not loaded error. 226 | func IsNotLoaded(err error) bool { 227 | if err == nil { 228 | return false 229 | } 230 | var e *NotLoadedError 231 | return errors.As(err, &e) 232 | } 233 | 234 | // ConstraintError returns when trying to create/update one or more entities and 235 | // one or more of their constraints failed. For example, violation of edge or 236 | // field uniqueness. 237 | type ConstraintError struct { 238 | msg string 239 | wrap error 240 | } 241 | 242 | // Error implements the error interface. 243 | func (e ConstraintError) Error() string { 244 | return "ent: constraint failed: " + e.msg 245 | } 246 | 247 | // Unwrap implements the errors.Wrapper interface. 248 | func (e *ConstraintError) Unwrap() error { 249 | return e.wrap 250 | } 251 | 252 | // IsConstraintError returns a boolean indicating whether the error is a constraint failure. 253 | func IsConstraintError(err error) bool { 254 | if err == nil { 255 | return false 256 | } 257 | var e *ConstraintError 258 | return errors.As(err, &e) 259 | } 260 | -------------------------------------------------------------------------------- /examples/ent/user_update.go: -------------------------------------------------------------------------------- 1 | // Code generated by entc, DO NOT EDIT. 2 | 3 | package ent 4 | 5 | import ( 6 | "context" 7 | "fmt" 8 | 9 | "ariga.io/sqlcomment/examples/ent/predicate" 10 | "ariga.io/sqlcomment/examples/ent/user" 11 | "entgo.io/ent/dialect/sql" 12 | "entgo.io/ent/dialect/sql/sqlgraph" 13 | "entgo.io/ent/schema/field" 14 | ) 15 | 16 | // UserUpdate is the builder for updating User entities. 17 | type UserUpdate struct { 18 | config 19 | hooks []Hook 20 | mutation *UserMutation 21 | } 22 | 23 | // Where appends a list predicates to the UserUpdate builder. 24 | func (uu *UserUpdate) Where(ps ...predicate.User) *UserUpdate { 25 | uu.mutation.Where(ps...) 26 | return uu 27 | } 28 | 29 | // SetName sets the "name" field. 30 | func (uu *UserUpdate) SetName(s string) *UserUpdate { 31 | uu.mutation.SetName(s) 32 | return uu 33 | } 34 | 35 | // Mutation returns the UserMutation object of the builder. 36 | func (uu *UserUpdate) Mutation() *UserMutation { 37 | return uu.mutation 38 | } 39 | 40 | // Save executes the query and returns the number of nodes affected by the update operation. 41 | func (uu *UserUpdate) Save(ctx context.Context) (int, error) { 42 | var ( 43 | err error 44 | affected int 45 | ) 46 | if len(uu.hooks) == 0 { 47 | affected, err = uu.sqlSave(ctx) 48 | } else { 49 | var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { 50 | mutation, ok := m.(*UserMutation) 51 | if !ok { 52 | return nil, fmt.Errorf("unexpected mutation type %T", m) 53 | } 54 | uu.mutation = mutation 55 | affected, err = uu.sqlSave(ctx) 56 | mutation.done = true 57 | return affected, err 58 | }) 59 | for i := len(uu.hooks) - 1; i >= 0; i-- { 60 | if uu.hooks[i] == nil { 61 | return 0, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)") 62 | } 63 | mut = uu.hooks[i](mut) 64 | } 65 | if _, err := mut.Mutate(ctx, uu.mutation); err != nil { 66 | return 0, err 67 | } 68 | } 69 | return affected, err 70 | } 71 | 72 | // SaveX is like Save, but panics if an error occurs. 73 | func (uu *UserUpdate) SaveX(ctx context.Context) int { 74 | affected, err := uu.Save(ctx) 75 | if err != nil { 76 | panic(err) 77 | } 78 | return affected 79 | } 80 | 81 | // Exec executes the query. 82 | func (uu *UserUpdate) Exec(ctx context.Context) error { 83 | _, err := uu.Save(ctx) 84 | return err 85 | } 86 | 87 | // ExecX is like Exec, but panics if an error occurs. 88 | func (uu *UserUpdate) ExecX(ctx context.Context) { 89 | if err := uu.Exec(ctx); err != nil { 90 | panic(err) 91 | } 92 | } 93 | 94 | func (uu *UserUpdate) sqlSave(ctx context.Context) (n int, err error) { 95 | _spec := &sqlgraph.UpdateSpec{ 96 | Node: &sqlgraph.NodeSpec{ 97 | Table: user.Table, 98 | Columns: user.Columns, 99 | ID: &sqlgraph.FieldSpec{ 100 | Type: field.TypeInt, 101 | Column: user.FieldID, 102 | }, 103 | }, 104 | } 105 | if ps := uu.mutation.predicates; len(ps) > 0 { 106 | _spec.Predicate = func(selector *sql.Selector) { 107 | for i := range ps { 108 | ps[i](selector) 109 | } 110 | } 111 | } 112 | if value, ok := uu.mutation.Name(); ok { 113 | _spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{ 114 | Type: field.TypeString, 115 | Value: value, 116 | Column: user.FieldName, 117 | }) 118 | } 119 | if n, err = sqlgraph.UpdateNodes(ctx, uu.driver, _spec); err != nil { 120 | if _, ok := err.(*sqlgraph.NotFoundError); ok { 121 | err = &NotFoundError{user.Label} 122 | } else if sqlgraph.IsConstraintError(err) { 123 | err = &ConstraintError{err.Error(), err} 124 | } 125 | return 0, err 126 | } 127 | return n, nil 128 | } 129 | 130 | // UserUpdateOne is the builder for updating a single User entity. 131 | type UserUpdateOne struct { 132 | config 133 | fields []string 134 | hooks []Hook 135 | mutation *UserMutation 136 | } 137 | 138 | // SetName sets the "name" field. 139 | func (uuo *UserUpdateOne) SetName(s string) *UserUpdateOne { 140 | uuo.mutation.SetName(s) 141 | return uuo 142 | } 143 | 144 | // Mutation returns the UserMutation object of the builder. 145 | func (uuo *UserUpdateOne) Mutation() *UserMutation { 146 | return uuo.mutation 147 | } 148 | 149 | // Select allows selecting one or more fields (columns) of the returned entity. 150 | // The default is selecting all fields defined in the entity schema. 151 | func (uuo *UserUpdateOne) Select(field string, fields ...string) *UserUpdateOne { 152 | uuo.fields = append([]string{field}, fields...) 153 | return uuo 154 | } 155 | 156 | // Save executes the query and returns the updated User entity. 157 | func (uuo *UserUpdateOne) Save(ctx context.Context) (*User, error) { 158 | var ( 159 | err error 160 | node *User 161 | ) 162 | if len(uuo.hooks) == 0 { 163 | node, err = uuo.sqlSave(ctx) 164 | } else { 165 | var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { 166 | mutation, ok := m.(*UserMutation) 167 | if !ok { 168 | return nil, fmt.Errorf("unexpected mutation type %T", m) 169 | } 170 | uuo.mutation = mutation 171 | node, err = uuo.sqlSave(ctx) 172 | mutation.done = true 173 | return node, err 174 | }) 175 | for i := len(uuo.hooks) - 1; i >= 0; i-- { 176 | if uuo.hooks[i] == nil { 177 | return nil, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)") 178 | } 179 | mut = uuo.hooks[i](mut) 180 | } 181 | if _, err := mut.Mutate(ctx, uuo.mutation); err != nil { 182 | return nil, err 183 | } 184 | } 185 | return node, err 186 | } 187 | 188 | // SaveX is like Save, but panics if an error occurs. 189 | func (uuo *UserUpdateOne) SaveX(ctx context.Context) *User { 190 | node, err := uuo.Save(ctx) 191 | if err != nil { 192 | panic(err) 193 | } 194 | return node 195 | } 196 | 197 | // Exec executes the query on the entity. 198 | func (uuo *UserUpdateOne) Exec(ctx context.Context) error { 199 | _, err := uuo.Save(ctx) 200 | return err 201 | } 202 | 203 | // ExecX is like Exec, but panics if an error occurs. 204 | func (uuo *UserUpdateOne) ExecX(ctx context.Context) { 205 | if err := uuo.Exec(ctx); err != nil { 206 | panic(err) 207 | } 208 | } 209 | 210 | func (uuo *UserUpdateOne) sqlSave(ctx context.Context) (_node *User, err error) { 211 | _spec := &sqlgraph.UpdateSpec{ 212 | Node: &sqlgraph.NodeSpec{ 213 | Table: user.Table, 214 | Columns: user.Columns, 215 | ID: &sqlgraph.FieldSpec{ 216 | Type: field.TypeInt, 217 | Column: user.FieldID, 218 | }, 219 | }, 220 | } 221 | id, ok := uuo.mutation.ID() 222 | if !ok { 223 | return nil, &ValidationError{Name: "ID", err: fmt.Errorf("missing User.ID for update")} 224 | } 225 | _spec.Node.ID.Value = id 226 | if fields := uuo.fields; len(fields) > 0 { 227 | _spec.Node.Columns = make([]string, 0, len(fields)) 228 | _spec.Node.Columns = append(_spec.Node.Columns, user.FieldID) 229 | for _, f := range fields { 230 | if !user.ValidColumn(f) { 231 | return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} 232 | } 233 | if f != user.FieldID { 234 | _spec.Node.Columns = append(_spec.Node.Columns, f) 235 | } 236 | } 237 | } 238 | if ps := uuo.mutation.predicates; len(ps) > 0 { 239 | _spec.Predicate = func(selector *sql.Selector) { 240 | for i := range ps { 241 | ps[i](selector) 242 | } 243 | } 244 | } 245 | if value, ok := uuo.mutation.Name(); ok { 246 | _spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{ 247 | Type: field.TypeString, 248 | Value: value, 249 | Column: user.FieldName, 250 | }) 251 | } 252 | _node = &User{config: uuo.config} 253 | _spec.Assign = _node.assignValues 254 | _spec.ScanValues = _node.scanValues 255 | if err = sqlgraph.UpdateNode(ctx, uuo.driver, _spec); err != nil { 256 | if _, ok := err.(*sqlgraph.NotFoundError); ok { 257 | err = &NotFoundError{user.Label} 258 | } else if sqlgraph.IsConstraintError(err) { 259 | err = &ConstraintError{err.Error(), err} 260 | } 261 | return nil, err 262 | } 263 | return _node, nil 264 | } 265 | -------------------------------------------------------------------------------- /examples/ent/mutation.go: -------------------------------------------------------------------------------- 1 | // Code generated by entc, DO NOT EDIT. 2 | 3 | package ent 4 | 5 | import ( 6 | "context" 7 | "fmt" 8 | "sync" 9 | 10 | "ariga.io/sqlcomment/examples/ent/predicate" 11 | "ariga.io/sqlcomment/examples/ent/user" 12 | 13 | "entgo.io/ent" 14 | ) 15 | 16 | const ( 17 | // Operation types. 18 | OpCreate = ent.OpCreate 19 | OpDelete = ent.OpDelete 20 | OpDeleteOne = ent.OpDeleteOne 21 | OpUpdate = ent.OpUpdate 22 | OpUpdateOne = ent.OpUpdateOne 23 | 24 | // Node types. 25 | TypeUser = "User" 26 | ) 27 | 28 | // UserMutation represents an operation that mutates the User nodes in the graph. 29 | type UserMutation struct { 30 | config 31 | op Op 32 | typ string 33 | id *int 34 | name *string 35 | clearedFields map[string]struct{} 36 | done bool 37 | oldValue func(context.Context) (*User, error) 38 | predicates []predicate.User 39 | } 40 | 41 | var _ ent.Mutation = (*UserMutation)(nil) 42 | 43 | // userOption allows management of the mutation configuration using functional options. 44 | type userOption func(*UserMutation) 45 | 46 | // newUserMutation creates new mutation for the User entity. 47 | func newUserMutation(c config, op Op, opts ...userOption) *UserMutation { 48 | m := &UserMutation{ 49 | config: c, 50 | op: op, 51 | typ: TypeUser, 52 | clearedFields: make(map[string]struct{}), 53 | } 54 | for _, opt := range opts { 55 | opt(m) 56 | } 57 | return m 58 | } 59 | 60 | // withUserID sets the ID field of the mutation. 61 | func withUserID(id int) userOption { 62 | return func(m *UserMutation) { 63 | var ( 64 | err error 65 | once sync.Once 66 | value *User 67 | ) 68 | m.oldValue = func(ctx context.Context) (*User, error) { 69 | once.Do(func() { 70 | if m.done { 71 | err = fmt.Errorf("querying old values post mutation is not allowed") 72 | } else { 73 | value, err = m.Client().User.Get(ctx, id) 74 | } 75 | }) 76 | return value, err 77 | } 78 | m.id = &id 79 | } 80 | } 81 | 82 | // withUser sets the old User of the mutation. 83 | func withUser(node *User) userOption { 84 | return func(m *UserMutation) { 85 | m.oldValue = func(context.Context) (*User, error) { 86 | return node, nil 87 | } 88 | m.id = &node.ID 89 | } 90 | } 91 | 92 | // Client returns a new `ent.Client` from the mutation. If the mutation was 93 | // executed in a transaction (ent.Tx), a transactional client is returned. 94 | func (m UserMutation) Client() *Client { 95 | client := &Client{config: m.config} 96 | client.init() 97 | return client 98 | } 99 | 100 | // Tx returns an `ent.Tx` for mutations that were executed in transactions; 101 | // it returns an error otherwise. 102 | func (m UserMutation) Tx() (*Tx, error) { 103 | if _, ok := m.driver.(*txDriver); !ok { 104 | return nil, fmt.Errorf("ent: mutation is not running in a transaction") 105 | } 106 | tx := &Tx{config: m.config} 107 | tx.init() 108 | return tx, nil 109 | } 110 | 111 | // ID returns the ID value in the mutation. Note that the ID is only available 112 | // if it was provided to the builder or after it was returned from the database. 113 | func (m *UserMutation) ID() (id int, exists bool) { 114 | if m.id == nil { 115 | return 116 | } 117 | return *m.id, true 118 | } 119 | 120 | // SetName sets the "name" field. 121 | func (m *UserMutation) SetName(s string) { 122 | m.name = &s 123 | } 124 | 125 | // Name returns the value of the "name" field in the mutation. 126 | func (m *UserMutation) Name() (r string, exists bool) { 127 | v := m.name 128 | if v == nil { 129 | return 130 | } 131 | return *v, true 132 | } 133 | 134 | // OldName returns the old "name" field's value of the User entity. 135 | // If the User object wasn't provided to the builder, the object is fetched from the database. 136 | // An error is returned if the mutation operation is not UpdateOne, or the database query fails. 137 | func (m *UserMutation) OldName(ctx context.Context) (v string, err error) { 138 | if !m.op.Is(OpUpdateOne) { 139 | return v, fmt.Errorf("OldName is only allowed on UpdateOne operations") 140 | } 141 | if m.id == nil || m.oldValue == nil { 142 | return v, fmt.Errorf("OldName requires an ID field in the mutation") 143 | } 144 | oldValue, err := m.oldValue(ctx) 145 | if err != nil { 146 | return v, fmt.Errorf("querying old value for OldName: %w", err) 147 | } 148 | return oldValue.Name, nil 149 | } 150 | 151 | // ResetName resets all changes to the "name" field. 152 | func (m *UserMutation) ResetName() { 153 | m.name = nil 154 | } 155 | 156 | // Where appends a list predicates to the UserMutation builder. 157 | func (m *UserMutation) Where(ps ...predicate.User) { 158 | m.predicates = append(m.predicates, ps...) 159 | } 160 | 161 | // Op returns the operation name. 162 | func (m *UserMutation) Op() Op { 163 | return m.op 164 | } 165 | 166 | // Type returns the node type of this mutation (User). 167 | func (m *UserMutation) Type() string { 168 | return m.typ 169 | } 170 | 171 | // Fields returns all fields that were changed during this mutation. Note that in 172 | // order to get all numeric fields that were incremented/decremented, call 173 | // AddedFields(). 174 | func (m *UserMutation) Fields() []string { 175 | fields := make([]string, 0, 1) 176 | if m.name != nil { 177 | fields = append(fields, user.FieldName) 178 | } 179 | return fields 180 | } 181 | 182 | // Field returns the value of a field with the given name. The second boolean 183 | // return value indicates that this field was not set, or was not defined in the 184 | // schema. 185 | func (m *UserMutation) Field(name string) (ent.Value, bool) { 186 | switch name { 187 | case user.FieldName: 188 | return m.Name() 189 | } 190 | return nil, false 191 | } 192 | 193 | // OldField returns the old value of the field from the database. An error is 194 | // returned if the mutation operation is not UpdateOne, or the query to the 195 | // database failed. 196 | func (m *UserMutation) OldField(ctx context.Context, name string) (ent.Value, error) { 197 | switch name { 198 | case user.FieldName: 199 | return m.OldName(ctx) 200 | } 201 | return nil, fmt.Errorf("unknown User field %s", name) 202 | } 203 | 204 | // SetField sets the value of a field with the given name. It returns an error if 205 | // the field is not defined in the schema, or if the type mismatched the field 206 | // type. 207 | func (m *UserMutation) SetField(name string, value ent.Value) error { 208 | switch name { 209 | case user.FieldName: 210 | v, ok := value.(string) 211 | if !ok { 212 | return fmt.Errorf("unexpected type %T for field %s", value, name) 213 | } 214 | m.SetName(v) 215 | return nil 216 | } 217 | return fmt.Errorf("unknown User field %s", name) 218 | } 219 | 220 | // AddedFields returns all numeric fields that were incremented/decremented during 221 | // this mutation. 222 | func (m *UserMutation) AddedFields() []string { 223 | return nil 224 | } 225 | 226 | // AddedField returns the numeric value that was incremented/decremented on a field 227 | // with the given name. The second boolean return value indicates that this field 228 | // was not set, or was not defined in the schema. 229 | func (m *UserMutation) AddedField(name string) (ent.Value, bool) { 230 | return nil, false 231 | } 232 | 233 | // AddField adds the value to the field with the given name. It returns an error if 234 | // the field is not defined in the schema, or if the type mismatched the field 235 | // type. 236 | func (m *UserMutation) AddField(name string, value ent.Value) error { 237 | switch name { 238 | } 239 | return fmt.Errorf("unknown User numeric field %s", name) 240 | } 241 | 242 | // ClearedFields returns all nullable fields that were cleared during this 243 | // mutation. 244 | func (m *UserMutation) ClearedFields() []string { 245 | return nil 246 | } 247 | 248 | // FieldCleared returns a boolean indicating if a field with the given name was 249 | // cleared in this mutation. 250 | func (m *UserMutation) FieldCleared(name string) bool { 251 | _, ok := m.clearedFields[name] 252 | return ok 253 | } 254 | 255 | // ClearField clears the value of the field with the given name. It returns an 256 | // error if the field is not defined in the schema. 257 | func (m *UserMutation) ClearField(name string) error { 258 | return fmt.Errorf("unknown User nullable field %s", name) 259 | } 260 | 261 | // ResetField resets all changes in the mutation for the field with the given name. 262 | // It returns an error if the field is not defined in the schema. 263 | func (m *UserMutation) ResetField(name string) error { 264 | switch name { 265 | case user.FieldName: 266 | m.ResetName() 267 | return nil 268 | } 269 | return fmt.Errorf("unknown User field %s", name) 270 | } 271 | 272 | // AddedEdges returns all edge names that were set/added in this mutation. 273 | func (m *UserMutation) AddedEdges() []string { 274 | edges := make([]string, 0, 0) 275 | return edges 276 | } 277 | 278 | // AddedIDs returns all IDs (to other nodes) that were added for the given edge 279 | // name in this mutation. 280 | func (m *UserMutation) AddedIDs(name string) []ent.Value { 281 | return nil 282 | } 283 | 284 | // RemovedEdges returns all edge names that were removed in this mutation. 285 | func (m *UserMutation) RemovedEdges() []string { 286 | edges := make([]string, 0, 0) 287 | return edges 288 | } 289 | 290 | // RemovedIDs returns all IDs (to other nodes) that were removed for the edge with 291 | // the given name in this mutation. 292 | func (m *UserMutation) RemovedIDs(name string) []ent.Value { 293 | return nil 294 | } 295 | 296 | // ClearedEdges returns all edge names that were cleared in this mutation. 297 | func (m *UserMutation) ClearedEdges() []string { 298 | edges := make([]string, 0, 0) 299 | return edges 300 | } 301 | 302 | // EdgeCleared returns a boolean which indicates if the edge with the given name 303 | // was cleared in this mutation. 304 | func (m *UserMutation) EdgeCleared(name string) bool { 305 | return false 306 | } 307 | 308 | // ClearEdge clears the value of the edge with the given name. It returns an error 309 | // if that edge is not defined in the schema. 310 | func (m *UserMutation) ClearEdge(name string) error { 311 | return fmt.Errorf("unknown User unique edge %s", name) 312 | } 313 | 314 | // ResetEdge resets all changes to the edge with the given name in this mutation. 315 | // It returns an error if the edge is not defined in the schema. 316 | func (m *UserMutation) ResetEdge(name string) error { 317 | return fmt.Errorf("unknown User edge %s", name) 318 | } 319 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | ariga.io/atlas v0.31.1-0.20250212144724-069be8033e83 h1:nX4HXncwIdvQ8/8sIUIf1nyCkK8qdBaHQ7EtzPpuiGE= 2 | ariga.io/atlas v0.31.1-0.20250212144724-069be8033e83/go.mod h1:Oe1xWPuu5q9LzyrWfbZmEZxFYeu4BHTyzfjeW2aZp/w= 3 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 4 | entgo.io/ent v0.14.4 h1:/DhDraSLXIkBhyiVoJeSshr4ZYi7femzhj6/TckzZuI= 5 | entgo.io/ent v0.14.4/go.mod h1:aDPE/OziPEu8+OWbzy4UlvWmD2/kbRuWfK2A40hcxJM= 6 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 7 | github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= 8 | github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= 9 | github.com/agext/levenshtein v1.2.1 h1:QmvMAjj2aEICytGiWzmxoE0x2KZvE0fvmqMOfy2tjT8= 10 | github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= 11 | github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw= 12 | github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= 13 | github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY= 14 | github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4= 15 | github.com/bmatcuk/doublestar v1.3.4 h1:gPypJ5xD31uhX6Tf54sDPUOBXTqKH4c9aPY66CyQrS0= 16 | github.com/bmatcuk/doublestar v1.3.4/go.mod h1:wiQtGV+rzVYxB7WIlirSN++5HPtPlXEo9MEoZQC/PmE= 17 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 18 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 19 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= 20 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 21 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 22 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 23 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 24 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 25 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= 26 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 27 | github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= 28 | github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= 29 | github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 30 | github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= 31 | github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= 32 | github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= 33 | github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= 34 | github.com/go-openapi/inflect v0.19.0 h1:9jCH9scKIbHeV9m12SmPilScz6krDxKRasNNSNPXu/4= 35 | github.com/go-openapi/inflect v0.19.0/go.mod h1:lHpZVlpIQqLyKwJ4N+YSc9hchQy/i12fJykb83CRBH4= 36 | github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= 37 | github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= 38 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 39 | github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 40 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= 41 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 42 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 43 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 44 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 45 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 46 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 47 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 48 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 49 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 50 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 51 | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 52 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 53 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 54 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 55 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 56 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 57 | github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 58 | github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= 59 | github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= 60 | github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 61 | github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= 62 | github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 63 | github.com/hashicorp/hcl/v2 v2.13.0 h1:0Apadu1w6M11dyGFxWnmhhcMjkbAiKCv7G1r/2QgCNc= 64 | github.com/hashicorp/hcl/v2 v2.13.0/go.mod h1:e4z5nxYlWNPdDSNYX+ph14EvWYMFm3eP0zIUqPc2jr0= 65 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 66 | github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 67 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 68 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 69 | github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= 70 | github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= 71 | github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEum7A= 72 | github.com/mattn/go-sqlite3 v1.14.28/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= 73 | github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 h1:DpOJ2HYzCv8LZP15IdmG+YdwD2luVPHITV96TkirNBM= 74 | github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= 75 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 76 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 77 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 78 | github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= 79 | github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= 80 | github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= 81 | github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= 82 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 83 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 84 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 85 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 86 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 87 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 88 | github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= 89 | github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 90 | github.com/zclconf/go-cty v1.14.4 h1:uXXczd9QDGsgu0i/QFR/hzI5NYCHLf6NQw/atrbnhq8= 91 | github.com/zclconf/go-cty v1.14.4/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= 92 | github.com/zclconf/go-cty-yaml v1.1.0 h1:nP+jp0qPHv2IhUVqmQSzjvqAWcObN0KBkUl2rWBdig0= 93 | github.com/zclconf/go-cty-yaml v1.1.0/go.mod h1:9YLUH4g7lOhVWqUbctnVlZ5KLpg7JAprQNgxSZ1Gyxs= 94 | go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= 95 | go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= 96 | go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= 97 | go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= 98 | go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0 h1:Hf9xI/XLML9ElpiHVDNwvqI0hIFlzV8dgIr35kV1kRU= 99 | go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0/go.mod h1:NfchwuyNoMcZ5MLHwPrODwUF1HWCXWrL31s8gSAdIKY= 100 | go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ= 101 | go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I= 102 | go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.37.0 h1:SNhVp/9q4Go/XHBkQ1/d5u9P/U+L1yaGPoi0x+mStaI= 103 | go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.37.0/go.mod h1:tx8OOlGH6R4kLV67YaYO44GFXloEjGPZuMjEkaaqIp4= 104 | go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE= 105 | go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E= 106 | go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI= 107 | go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg= 108 | go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc= 109 | go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps= 110 | go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4= 111 | go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0= 112 | go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= 113 | go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= 114 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 115 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 116 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 117 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 118 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 119 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 120 | golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM= 121 | golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= 122 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 123 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 124 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 125 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 126 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 127 | golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME= 128 | golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 129 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 130 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 131 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 132 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 133 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 134 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 135 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 136 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 137 | golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= 138 | golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= 139 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 140 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 141 | golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= 142 | golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= 143 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 144 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 145 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 146 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 147 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 148 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 149 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 150 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 151 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 152 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 153 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 154 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 155 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 156 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 157 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 158 | google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= 159 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 160 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 161 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 162 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 163 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 164 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 165 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 166 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 167 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 168 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 169 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 170 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 171 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 172 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 173 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 174 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 175 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 176 | -------------------------------------------------------------------------------- /examples/ent/user_query.go: -------------------------------------------------------------------------------- 1 | // Code generated by entc, DO NOT EDIT. 2 | 3 | package ent 4 | 5 | import ( 6 | "context" 7 | "errors" 8 | "fmt" 9 | "math" 10 | 11 | "ariga.io/sqlcomment/examples/ent/predicate" 12 | "ariga.io/sqlcomment/examples/ent/user" 13 | "entgo.io/ent/dialect/sql" 14 | "entgo.io/ent/dialect/sql/sqlgraph" 15 | "entgo.io/ent/schema/field" 16 | ) 17 | 18 | // UserQuery is the builder for querying User entities. 19 | type UserQuery struct { 20 | config 21 | limit *int 22 | offset *int 23 | unique *bool 24 | order []OrderFunc 25 | fields []string 26 | predicates []predicate.User 27 | // intermediate query (i.e. traversal path). 28 | sql *sql.Selector 29 | path func(context.Context) (*sql.Selector, error) 30 | } 31 | 32 | // Where adds a new predicate for the UserQuery builder. 33 | func (uq *UserQuery) Where(ps ...predicate.User) *UserQuery { 34 | uq.predicates = append(uq.predicates, ps...) 35 | return uq 36 | } 37 | 38 | // Limit adds a limit step to the query. 39 | func (uq *UserQuery) Limit(limit int) *UserQuery { 40 | uq.limit = &limit 41 | return uq 42 | } 43 | 44 | // Offset adds an offset step to the query. 45 | func (uq *UserQuery) Offset(offset int) *UserQuery { 46 | uq.offset = &offset 47 | return uq 48 | } 49 | 50 | // Unique configures the query builder to filter duplicate records on query. 51 | // By default, unique is set to true, and can be disabled using this method. 52 | func (uq *UserQuery) Unique(unique bool) *UserQuery { 53 | uq.unique = &unique 54 | return uq 55 | } 56 | 57 | // Order adds an order step to the query. 58 | func (uq *UserQuery) Order(o ...OrderFunc) *UserQuery { 59 | uq.order = append(uq.order, o...) 60 | return uq 61 | } 62 | 63 | // First returns the first User entity from the query. 64 | // Returns a *NotFoundError when no User was found. 65 | func (uq *UserQuery) First(ctx context.Context) (*User, error) { 66 | nodes, err := uq.Limit(1).All(ctx) 67 | if err != nil { 68 | return nil, err 69 | } 70 | if len(nodes) == 0 { 71 | return nil, &NotFoundError{user.Label} 72 | } 73 | return nodes[0], nil 74 | } 75 | 76 | // FirstX is like First, but panics if an error occurs. 77 | func (uq *UserQuery) FirstX(ctx context.Context) *User { 78 | node, err := uq.First(ctx) 79 | if err != nil && !IsNotFound(err) { 80 | panic(err) 81 | } 82 | return node 83 | } 84 | 85 | // FirstID returns the first User ID from the query. 86 | // Returns a *NotFoundError when no User ID was found. 87 | func (uq *UserQuery) FirstID(ctx context.Context) (id int, err error) { 88 | var ids []int 89 | if ids, err = uq.Limit(1).IDs(ctx); err != nil { 90 | return 91 | } 92 | if len(ids) == 0 { 93 | err = &NotFoundError{user.Label} 94 | return 95 | } 96 | return ids[0], nil 97 | } 98 | 99 | // FirstIDX is like FirstID, but panics if an error occurs. 100 | func (uq *UserQuery) FirstIDX(ctx context.Context) int { 101 | id, err := uq.FirstID(ctx) 102 | if err != nil && !IsNotFound(err) { 103 | panic(err) 104 | } 105 | return id 106 | } 107 | 108 | // Only returns a single User entity found by the query, ensuring it only returns one. 109 | // Returns a *NotSingularError when exactly one User entity is not found. 110 | // Returns a *NotFoundError when no User entities are found. 111 | func (uq *UserQuery) Only(ctx context.Context) (*User, error) { 112 | nodes, err := uq.Limit(2).All(ctx) 113 | if err != nil { 114 | return nil, err 115 | } 116 | switch len(nodes) { 117 | case 1: 118 | return nodes[0], nil 119 | case 0: 120 | return nil, &NotFoundError{user.Label} 121 | default: 122 | return nil, &NotSingularError{user.Label} 123 | } 124 | } 125 | 126 | // OnlyX is like Only, but panics if an error occurs. 127 | func (uq *UserQuery) OnlyX(ctx context.Context) *User { 128 | node, err := uq.Only(ctx) 129 | if err != nil { 130 | panic(err) 131 | } 132 | return node 133 | } 134 | 135 | // OnlyID is like Only, but returns the only User ID in the query. 136 | // Returns a *NotSingularError when exactly one User ID is not found. 137 | // Returns a *NotFoundError when no entities are found. 138 | func (uq *UserQuery) OnlyID(ctx context.Context) (id int, err error) { 139 | var ids []int 140 | if ids, err = uq.Limit(2).IDs(ctx); err != nil { 141 | return 142 | } 143 | switch len(ids) { 144 | case 1: 145 | id = ids[0] 146 | case 0: 147 | err = &NotFoundError{user.Label} 148 | default: 149 | err = &NotSingularError{user.Label} 150 | } 151 | return 152 | } 153 | 154 | // OnlyIDX is like OnlyID, but panics if an error occurs. 155 | func (uq *UserQuery) OnlyIDX(ctx context.Context) int { 156 | id, err := uq.OnlyID(ctx) 157 | if err != nil { 158 | panic(err) 159 | } 160 | return id 161 | } 162 | 163 | // All executes the query and returns a list of Users. 164 | func (uq *UserQuery) All(ctx context.Context) ([]*User, error) { 165 | if err := uq.prepareQuery(ctx); err != nil { 166 | return nil, err 167 | } 168 | return uq.sqlAll(ctx) 169 | } 170 | 171 | // AllX is like All, but panics if an error occurs. 172 | func (uq *UserQuery) AllX(ctx context.Context) []*User { 173 | nodes, err := uq.All(ctx) 174 | if err != nil { 175 | panic(err) 176 | } 177 | return nodes 178 | } 179 | 180 | // IDs executes the query and returns a list of User IDs. 181 | func (uq *UserQuery) IDs(ctx context.Context) ([]int, error) { 182 | var ids []int 183 | if err := uq.Select(user.FieldID).Scan(ctx, &ids); err != nil { 184 | return nil, err 185 | } 186 | return ids, nil 187 | } 188 | 189 | // IDsX is like IDs, but panics if an error occurs. 190 | func (uq *UserQuery) IDsX(ctx context.Context) []int { 191 | ids, err := uq.IDs(ctx) 192 | if err != nil { 193 | panic(err) 194 | } 195 | return ids 196 | } 197 | 198 | // Count returns the count of the given query. 199 | func (uq *UserQuery) Count(ctx context.Context) (int, error) { 200 | if err := uq.prepareQuery(ctx); err != nil { 201 | return 0, err 202 | } 203 | return uq.sqlCount(ctx) 204 | } 205 | 206 | // CountX is like Count, but panics if an error occurs. 207 | func (uq *UserQuery) CountX(ctx context.Context) int { 208 | count, err := uq.Count(ctx) 209 | if err != nil { 210 | panic(err) 211 | } 212 | return count 213 | } 214 | 215 | // Exist returns true if the query has elements in the graph. 216 | func (uq *UserQuery) Exist(ctx context.Context) (bool, error) { 217 | if err := uq.prepareQuery(ctx); err != nil { 218 | return false, err 219 | } 220 | return uq.sqlExist(ctx) 221 | } 222 | 223 | // ExistX is like Exist, but panics if an error occurs. 224 | func (uq *UserQuery) ExistX(ctx context.Context) bool { 225 | exist, err := uq.Exist(ctx) 226 | if err != nil { 227 | panic(err) 228 | } 229 | return exist 230 | } 231 | 232 | // Clone returns a duplicate of the UserQuery builder, including all associated steps. It can be 233 | // used to prepare common query builders and use them differently after the clone is made. 234 | func (uq *UserQuery) Clone() *UserQuery { 235 | if uq == nil { 236 | return nil 237 | } 238 | return &UserQuery{ 239 | config: uq.config, 240 | limit: uq.limit, 241 | offset: uq.offset, 242 | order: append([]OrderFunc{}, uq.order...), 243 | predicates: append([]predicate.User{}, uq.predicates...), 244 | // clone intermediate query. 245 | sql: uq.sql.Clone(), 246 | path: uq.path, 247 | } 248 | } 249 | 250 | // GroupBy is used to group vertices by one or more fields/columns. 251 | // It is often used with aggregate functions, like: count, max, mean, min, sum. 252 | // 253 | // Example: 254 | // 255 | // var v []struct { 256 | // Name string `json:"name,omitempty"` 257 | // Count int `json:"count,omitempty"` 258 | // } 259 | // 260 | // client.User.Query(). 261 | // GroupBy(user.FieldName). 262 | // Aggregate(ent.Count()). 263 | // Scan(ctx, &v) 264 | // 265 | func (uq *UserQuery) GroupBy(field string, fields ...string) *UserGroupBy { 266 | group := &UserGroupBy{config: uq.config} 267 | group.fields = append([]string{field}, fields...) 268 | group.path = func(ctx context.Context) (prev *sql.Selector, err error) { 269 | if err := uq.prepareQuery(ctx); err != nil { 270 | return nil, err 271 | } 272 | return uq.sqlQuery(ctx), nil 273 | } 274 | return group 275 | } 276 | 277 | // Select allows the selection one or more fields/columns for the given query, 278 | // instead of selecting all fields in the entity. 279 | // 280 | // Example: 281 | // 282 | // var v []struct { 283 | // Name string `json:"name,omitempty"` 284 | // } 285 | // 286 | // client.User.Query(). 287 | // Select(user.FieldName). 288 | // Scan(ctx, &v) 289 | // 290 | func (uq *UserQuery) Select(fields ...string) *UserSelect { 291 | uq.fields = append(uq.fields, fields...) 292 | return &UserSelect{UserQuery: uq} 293 | } 294 | 295 | func (uq *UserQuery) prepareQuery(ctx context.Context) error { 296 | for _, f := range uq.fields { 297 | if !user.ValidColumn(f) { 298 | return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} 299 | } 300 | } 301 | if uq.path != nil { 302 | prev, err := uq.path(ctx) 303 | if err != nil { 304 | return err 305 | } 306 | uq.sql = prev 307 | } 308 | return nil 309 | } 310 | 311 | func (uq *UserQuery) sqlAll(ctx context.Context) ([]*User, error) { 312 | var ( 313 | nodes = []*User{} 314 | _spec = uq.querySpec() 315 | ) 316 | _spec.ScanValues = func(columns []string) ([]interface{}, error) { 317 | node := &User{config: uq.config} 318 | nodes = append(nodes, node) 319 | return node.scanValues(columns) 320 | } 321 | _spec.Assign = func(columns []string, values []interface{}) error { 322 | if len(nodes) == 0 { 323 | return fmt.Errorf("ent: Assign called without calling ScanValues") 324 | } 325 | node := nodes[len(nodes)-1] 326 | return node.assignValues(columns, values) 327 | } 328 | if err := sqlgraph.QueryNodes(ctx, uq.driver, _spec); err != nil { 329 | return nil, err 330 | } 331 | if len(nodes) == 0 { 332 | return nodes, nil 333 | } 334 | return nodes, nil 335 | } 336 | 337 | func (uq *UserQuery) sqlCount(ctx context.Context) (int, error) { 338 | _spec := uq.querySpec() 339 | return sqlgraph.CountNodes(ctx, uq.driver, _spec) 340 | } 341 | 342 | func (uq *UserQuery) sqlExist(ctx context.Context) (bool, error) { 343 | n, err := uq.sqlCount(ctx) 344 | if err != nil { 345 | return false, fmt.Errorf("ent: check existence: %w", err) 346 | } 347 | return n > 0, nil 348 | } 349 | 350 | func (uq *UserQuery) querySpec() *sqlgraph.QuerySpec { 351 | _spec := &sqlgraph.QuerySpec{ 352 | Node: &sqlgraph.NodeSpec{ 353 | Table: user.Table, 354 | Columns: user.Columns, 355 | ID: &sqlgraph.FieldSpec{ 356 | Type: field.TypeInt, 357 | Column: user.FieldID, 358 | }, 359 | }, 360 | From: uq.sql, 361 | Unique: true, 362 | } 363 | if unique := uq.unique; unique != nil { 364 | _spec.Unique = *unique 365 | } 366 | if fields := uq.fields; len(fields) > 0 { 367 | _spec.Node.Columns = make([]string, 0, len(fields)) 368 | _spec.Node.Columns = append(_spec.Node.Columns, user.FieldID) 369 | for i := range fields { 370 | if fields[i] != user.FieldID { 371 | _spec.Node.Columns = append(_spec.Node.Columns, fields[i]) 372 | } 373 | } 374 | } 375 | if ps := uq.predicates; len(ps) > 0 { 376 | _spec.Predicate = func(selector *sql.Selector) { 377 | for i := range ps { 378 | ps[i](selector) 379 | } 380 | } 381 | } 382 | if limit := uq.limit; limit != nil { 383 | _spec.Limit = *limit 384 | } 385 | if offset := uq.offset; offset != nil { 386 | _spec.Offset = *offset 387 | } 388 | if ps := uq.order; len(ps) > 0 { 389 | _spec.Order = func(selector *sql.Selector) { 390 | for i := range ps { 391 | ps[i](selector) 392 | } 393 | } 394 | } 395 | return _spec 396 | } 397 | 398 | func (uq *UserQuery) sqlQuery(ctx context.Context) *sql.Selector { 399 | builder := sql.Dialect(uq.driver.Dialect()) 400 | t1 := builder.Table(user.Table) 401 | columns := uq.fields 402 | if len(columns) == 0 { 403 | columns = user.Columns 404 | } 405 | selector := builder.Select(t1.Columns(columns...)...).From(t1) 406 | if uq.sql != nil { 407 | selector = uq.sql 408 | selector.Select(selector.Columns(columns...)...) 409 | } 410 | for _, p := range uq.predicates { 411 | p(selector) 412 | } 413 | for _, p := range uq.order { 414 | p(selector) 415 | } 416 | if offset := uq.offset; offset != nil { 417 | // limit is mandatory for offset clause. We start 418 | // with default value, and override it below if needed. 419 | selector.Offset(*offset).Limit(math.MaxInt32) 420 | } 421 | if limit := uq.limit; limit != nil { 422 | selector.Limit(*limit) 423 | } 424 | return selector 425 | } 426 | 427 | // UserGroupBy is the group-by builder for User entities. 428 | type UserGroupBy struct { 429 | config 430 | fields []string 431 | fns []AggregateFunc 432 | // intermediate query (i.e. traversal path). 433 | sql *sql.Selector 434 | path func(context.Context) (*sql.Selector, error) 435 | } 436 | 437 | // Aggregate adds the given aggregation functions to the group-by query. 438 | func (ugb *UserGroupBy) Aggregate(fns ...AggregateFunc) *UserGroupBy { 439 | ugb.fns = append(ugb.fns, fns...) 440 | return ugb 441 | } 442 | 443 | // Scan applies the group-by query and scans the result into the given value. 444 | func (ugb *UserGroupBy) Scan(ctx context.Context, v interface{}) error { 445 | query, err := ugb.path(ctx) 446 | if err != nil { 447 | return err 448 | } 449 | ugb.sql = query 450 | return ugb.sqlScan(ctx, v) 451 | } 452 | 453 | // ScanX is like Scan, but panics if an error occurs. 454 | func (ugb *UserGroupBy) ScanX(ctx context.Context, v interface{}) { 455 | if err := ugb.Scan(ctx, v); err != nil { 456 | panic(err) 457 | } 458 | } 459 | 460 | // Strings returns list of strings from group-by. 461 | // It is only allowed when executing a group-by query with one field. 462 | func (ugb *UserGroupBy) Strings(ctx context.Context) ([]string, error) { 463 | if len(ugb.fields) > 1 { 464 | return nil, errors.New("ent: UserGroupBy.Strings is not achievable when grouping more than 1 field") 465 | } 466 | var v []string 467 | if err := ugb.Scan(ctx, &v); err != nil { 468 | return nil, err 469 | } 470 | return v, nil 471 | } 472 | 473 | // StringsX is like Strings, but panics if an error occurs. 474 | func (ugb *UserGroupBy) StringsX(ctx context.Context) []string { 475 | v, err := ugb.Strings(ctx) 476 | if err != nil { 477 | panic(err) 478 | } 479 | return v 480 | } 481 | 482 | // String returns a single string from a group-by query. 483 | // It is only allowed when executing a group-by query with one field. 484 | func (ugb *UserGroupBy) String(ctx context.Context) (_ string, err error) { 485 | var v []string 486 | if v, err = ugb.Strings(ctx); err != nil { 487 | return 488 | } 489 | switch len(v) { 490 | case 1: 491 | return v[0], nil 492 | case 0: 493 | err = &NotFoundError{user.Label} 494 | default: 495 | err = fmt.Errorf("ent: UserGroupBy.Strings returned %d results when one was expected", len(v)) 496 | } 497 | return 498 | } 499 | 500 | // StringX is like String, but panics if an error occurs. 501 | func (ugb *UserGroupBy) StringX(ctx context.Context) string { 502 | v, err := ugb.String(ctx) 503 | if err != nil { 504 | panic(err) 505 | } 506 | return v 507 | } 508 | 509 | // Ints returns list of ints from group-by. 510 | // It is only allowed when executing a group-by query with one field. 511 | func (ugb *UserGroupBy) Ints(ctx context.Context) ([]int, error) { 512 | if len(ugb.fields) > 1 { 513 | return nil, errors.New("ent: UserGroupBy.Ints is not achievable when grouping more than 1 field") 514 | } 515 | var v []int 516 | if err := ugb.Scan(ctx, &v); err != nil { 517 | return nil, err 518 | } 519 | return v, nil 520 | } 521 | 522 | // IntsX is like Ints, but panics if an error occurs. 523 | func (ugb *UserGroupBy) IntsX(ctx context.Context) []int { 524 | v, err := ugb.Ints(ctx) 525 | if err != nil { 526 | panic(err) 527 | } 528 | return v 529 | } 530 | 531 | // Int returns a single int from a group-by query. 532 | // It is only allowed when executing a group-by query with one field. 533 | func (ugb *UserGroupBy) Int(ctx context.Context) (_ int, err error) { 534 | var v []int 535 | if v, err = ugb.Ints(ctx); err != nil { 536 | return 537 | } 538 | switch len(v) { 539 | case 1: 540 | return v[0], nil 541 | case 0: 542 | err = &NotFoundError{user.Label} 543 | default: 544 | err = fmt.Errorf("ent: UserGroupBy.Ints returned %d results when one was expected", len(v)) 545 | } 546 | return 547 | } 548 | 549 | // IntX is like Int, but panics if an error occurs. 550 | func (ugb *UserGroupBy) IntX(ctx context.Context) int { 551 | v, err := ugb.Int(ctx) 552 | if err != nil { 553 | panic(err) 554 | } 555 | return v 556 | } 557 | 558 | // Float64s returns list of float64s from group-by. 559 | // It is only allowed when executing a group-by query with one field. 560 | func (ugb *UserGroupBy) Float64s(ctx context.Context) ([]float64, error) { 561 | if len(ugb.fields) > 1 { 562 | return nil, errors.New("ent: UserGroupBy.Float64s is not achievable when grouping more than 1 field") 563 | } 564 | var v []float64 565 | if err := ugb.Scan(ctx, &v); err != nil { 566 | return nil, err 567 | } 568 | return v, nil 569 | } 570 | 571 | // Float64sX is like Float64s, but panics if an error occurs. 572 | func (ugb *UserGroupBy) Float64sX(ctx context.Context) []float64 { 573 | v, err := ugb.Float64s(ctx) 574 | if err != nil { 575 | panic(err) 576 | } 577 | return v 578 | } 579 | 580 | // Float64 returns a single float64 from a group-by query. 581 | // It is only allowed when executing a group-by query with one field. 582 | func (ugb *UserGroupBy) Float64(ctx context.Context) (_ float64, err error) { 583 | var v []float64 584 | if v, err = ugb.Float64s(ctx); err != nil { 585 | return 586 | } 587 | switch len(v) { 588 | case 1: 589 | return v[0], nil 590 | case 0: 591 | err = &NotFoundError{user.Label} 592 | default: 593 | err = fmt.Errorf("ent: UserGroupBy.Float64s returned %d results when one was expected", len(v)) 594 | } 595 | return 596 | } 597 | 598 | // Float64X is like Float64, but panics if an error occurs. 599 | func (ugb *UserGroupBy) Float64X(ctx context.Context) float64 { 600 | v, err := ugb.Float64(ctx) 601 | if err != nil { 602 | panic(err) 603 | } 604 | return v 605 | } 606 | 607 | // Bools returns list of bools from group-by. 608 | // It is only allowed when executing a group-by query with one field. 609 | func (ugb *UserGroupBy) Bools(ctx context.Context) ([]bool, error) { 610 | if len(ugb.fields) > 1 { 611 | return nil, errors.New("ent: UserGroupBy.Bools is not achievable when grouping more than 1 field") 612 | } 613 | var v []bool 614 | if err := ugb.Scan(ctx, &v); err != nil { 615 | return nil, err 616 | } 617 | return v, nil 618 | } 619 | 620 | // BoolsX is like Bools, but panics if an error occurs. 621 | func (ugb *UserGroupBy) BoolsX(ctx context.Context) []bool { 622 | v, err := ugb.Bools(ctx) 623 | if err != nil { 624 | panic(err) 625 | } 626 | return v 627 | } 628 | 629 | // Bool returns a single bool from a group-by query. 630 | // It is only allowed when executing a group-by query with one field. 631 | func (ugb *UserGroupBy) Bool(ctx context.Context) (_ bool, err error) { 632 | var v []bool 633 | if v, err = ugb.Bools(ctx); err != nil { 634 | return 635 | } 636 | switch len(v) { 637 | case 1: 638 | return v[0], nil 639 | case 0: 640 | err = &NotFoundError{user.Label} 641 | default: 642 | err = fmt.Errorf("ent: UserGroupBy.Bools returned %d results when one was expected", len(v)) 643 | } 644 | return 645 | } 646 | 647 | // BoolX is like Bool, but panics if an error occurs. 648 | func (ugb *UserGroupBy) BoolX(ctx context.Context) bool { 649 | v, err := ugb.Bool(ctx) 650 | if err != nil { 651 | panic(err) 652 | } 653 | return v 654 | } 655 | 656 | func (ugb *UserGroupBy) sqlScan(ctx context.Context, v interface{}) error { 657 | for _, f := range ugb.fields { 658 | if !user.ValidColumn(f) { 659 | return &ValidationError{Name: f, err: fmt.Errorf("invalid field %q for group-by", f)} 660 | } 661 | } 662 | selector := ugb.sqlQuery() 663 | if err := selector.Err(); err != nil { 664 | return err 665 | } 666 | rows := &sql.Rows{} 667 | query, args := selector.Query() 668 | if err := ugb.driver.Query(ctx, query, args, rows); err != nil { 669 | return err 670 | } 671 | defer rows.Close() 672 | return sql.ScanSlice(rows, v) 673 | } 674 | 675 | func (ugb *UserGroupBy) sqlQuery() *sql.Selector { 676 | selector := ugb.sql.Select() 677 | aggregation := make([]string, 0, len(ugb.fns)) 678 | for _, fn := range ugb.fns { 679 | aggregation = append(aggregation, fn(selector)) 680 | } 681 | // If no columns were selected in a custom aggregation function, the default 682 | // selection is the fields used for "group-by", and the aggregation functions. 683 | if len(selector.SelectedColumns()) == 0 { 684 | columns := make([]string, 0, len(ugb.fields)+len(ugb.fns)) 685 | for _, f := range ugb.fields { 686 | columns = append(columns, selector.C(f)) 687 | } 688 | for _, c := range aggregation { 689 | columns = append(columns, c) 690 | } 691 | selector.Select(columns...) 692 | } 693 | return selector.GroupBy(selector.Columns(ugb.fields...)...) 694 | } 695 | 696 | // UserSelect is the builder for selecting fields of User entities. 697 | type UserSelect struct { 698 | *UserQuery 699 | // intermediate query (i.e. traversal path). 700 | sql *sql.Selector 701 | } 702 | 703 | // Scan applies the selector query and scans the result into the given value. 704 | func (us *UserSelect) Scan(ctx context.Context, v interface{}) error { 705 | if err := us.prepareQuery(ctx); err != nil { 706 | return err 707 | } 708 | us.sql = us.UserQuery.sqlQuery(ctx) 709 | return us.sqlScan(ctx, v) 710 | } 711 | 712 | // ScanX is like Scan, but panics if an error occurs. 713 | func (us *UserSelect) ScanX(ctx context.Context, v interface{}) { 714 | if err := us.Scan(ctx, v); err != nil { 715 | panic(err) 716 | } 717 | } 718 | 719 | // Strings returns list of strings from a selector. It is only allowed when selecting one field. 720 | func (us *UserSelect) Strings(ctx context.Context) ([]string, error) { 721 | if len(us.fields) > 1 { 722 | return nil, errors.New("ent: UserSelect.Strings is not achievable when selecting more than 1 field") 723 | } 724 | var v []string 725 | if err := us.Scan(ctx, &v); err != nil { 726 | return nil, err 727 | } 728 | return v, nil 729 | } 730 | 731 | // StringsX is like Strings, but panics if an error occurs. 732 | func (us *UserSelect) StringsX(ctx context.Context) []string { 733 | v, err := us.Strings(ctx) 734 | if err != nil { 735 | panic(err) 736 | } 737 | return v 738 | } 739 | 740 | // String returns a single string from a selector. It is only allowed when selecting one field. 741 | func (us *UserSelect) String(ctx context.Context) (_ string, err error) { 742 | var v []string 743 | if v, err = us.Strings(ctx); err != nil { 744 | return 745 | } 746 | switch len(v) { 747 | case 1: 748 | return v[0], nil 749 | case 0: 750 | err = &NotFoundError{user.Label} 751 | default: 752 | err = fmt.Errorf("ent: UserSelect.Strings returned %d results when one was expected", len(v)) 753 | } 754 | return 755 | } 756 | 757 | // StringX is like String, but panics if an error occurs. 758 | func (us *UserSelect) StringX(ctx context.Context) string { 759 | v, err := us.String(ctx) 760 | if err != nil { 761 | panic(err) 762 | } 763 | return v 764 | } 765 | 766 | // Ints returns list of ints from a selector. It is only allowed when selecting one field. 767 | func (us *UserSelect) Ints(ctx context.Context) ([]int, error) { 768 | if len(us.fields) > 1 { 769 | return nil, errors.New("ent: UserSelect.Ints is not achievable when selecting more than 1 field") 770 | } 771 | var v []int 772 | if err := us.Scan(ctx, &v); err != nil { 773 | return nil, err 774 | } 775 | return v, nil 776 | } 777 | 778 | // IntsX is like Ints, but panics if an error occurs. 779 | func (us *UserSelect) IntsX(ctx context.Context) []int { 780 | v, err := us.Ints(ctx) 781 | if err != nil { 782 | panic(err) 783 | } 784 | return v 785 | } 786 | 787 | // Int returns a single int from a selector. It is only allowed when selecting one field. 788 | func (us *UserSelect) Int(ctx context.Context) (_ int, err error) { 789 | var v []int 790 | if v, err = us.Ints(ctx); err != nil { 791 | return 792 | } 793 | switch len(v) { 794 | case 1: 795 | return v[0], nil 796 | case 0: 797 | err = &NotFoundError{user.Label} 798 | default: 799 | err = fmt.Errorf("ent: UserSelect.Ints returned %d results when one was expected", len(v)) 800 | } 801 | return 802 | } 803 | 804 | // IntX is like Int, but panics if an error occurs. 805 | func (us *UserSelect) IntX(ctx context.Context) int { 806 | v, err := us.Int(ctx) 807 | if err != nil { 808 | panic(err) 809 | } 810 | return v 811 | } 812 | 813 | // Float64s returns list of float64s from a selector. It is only allowed when selecting one field. 814 | func (us *UserSelect) Float64s(ctx context.Context) ([]float64, error) { 815 | if len(us.fields) > 1 { 816 | return nil, errors.New("ent: UserSelect.Float64s is not achievable when selecting more than 1 field") 817 | } 818 | var v []float64 819 | if err := us.Scan(ctx, &v); err != nil { 820 | return nil, err 821 | } 822 | return v, nil 823 | } 824 | 825 | // Float64sX is like Float64s, but panics if an error occurs. 826 | func (us *UserSelect) Float64sX(ctx context.Context) []float64 { 827 | v, err := us.Float64s(ctx) 828 | if err != nil { 829 | panic(err) 830 | } 831 | return v 832 | } 833 | 834 | // Float64 returns a single float64 from a selector. It is only allowed when selecting one field. 835 | func (us *UserSelect) Float64(ctx context.Context) (_ float64, err error) { 836 | var v []float64 837 | if v, err = us.Float64s(ctx); err != nil { 838 | return 839 | } 840 | switch len(v) { 841 | case 1: 842 | return v[0], nil 843 | case 0: 844 | err = &NotFoundError{user.Label} 845 | default: 846 | err = fmt.Errorf("ent: UserSelect.Float64s returned %d results when one was expected", len(v)) 847 | } 848 | return 849 | } 850 | 851 | // Float64X is like Float64, but panics if an error occurs. 852 | func (us *UserSelect) Float64X(ctx context.Context) float64 { 853 | v, err := us.Float64(ctx) 854 | if err != nil { 855 | panic(err) 856 | } 857 | return v 858 | } 859 | 860 | // Bools returns list of bools from a selector. It is only allowed when selecting one field. 861 | func (us *UserSelect) Bools(ctx context.Context) ([]bool, error) { 862 | if len(us.fields) > 1 { 863 | return nil, errors.New("ent: UserSelect.Bools is not achievable when selecting more than 1 field") 864 | } 865 | var v []bool 866 | if err := us.Scan(ctx, &v); err != nil { 867 | return nil, err 868 | } 869 | return v, nil 870 | } 871 | 872 | // BoolsX is like Bools, but panics if an error occurs. 873 | func (us *UserSelect) BoolsX(ctx context.Context) []bool { 874 | v, err := us.Bools(ctx) 875 | if err != nil { 876 | panic(err) 877 | } 878 | return v 879 | } 880 | 881 | // Bool returns a single bool from a selector. It is only allowed when selecting one field. 882 | func (us *UserSelect) Bool(ctx context.Context) (_ bool, err error) { 883 | var v []bool 884 | if v, err = us.Bools(ctx); err != nil { 885 | return 886 | } 887 | switch len(v) { 888 | case 1: 889 | return v[0], nil 890 | case 0: 891 | err = &NotFoundError{user.Label} 892 | default: 893 | err = fmt.Errorf("ent: UserSelect.Bools returned %d results when one was expected", len(v)) 894 | } 895 | return 896 | } 897 | 898 | // BoolX is like Bool, but panics if an error occurs. 899 | func (us *UserSelect) BoolX(ctx context.Context) bool { 900 | v, err := us.Bool(ctx) 901 | if err != nil { 902 | panic(err) 903 | } 904 | return v 905 | } 906 | 907 | func (us *UserSelect) sqlScan(ctx context.Context, v interface{}) error { 908 | rows := &sql.Rows{} 909 | query, args := us.sql.Query() 910 | if err := us.driver.Query(ctx, query, args, rows); err != nil { 911 | return err 912 | } 913 | defer rows.Close() 914 | return sql.ScanSlice(rows, v) 915 | } 916 | --------------------------------------------------------------------------------