├── benchs ├── ent │ ├── generate.go │ ├── predicate │ │ └── predicate.go │ ├── runtime.go │ ├── runtime │ │ └── runtime.go │ ├── schema │ │ └── model.go │ ├── context.go │ ├── migrate │ │ ├── schema.go │ │ └── migrate.go │ ├── config.go │ ├── model │ │ └── model.go │ ├── enttest │ │ └── enttest.go │ ├── model_delete.go │ ├── model.go │ ├── hook │ │ └── hook.go │ ├── tx.go │ ├── client.go │ ├── ent.go │ ├── model_create.go │ └── model_update.go ├── sqlc │ ├── README.md │ ├── db │ │ ├── models.go │ │ ├── db.go │ │ └── queries.sql.go │ ├── schema.sql │ ├── sqlc.yaml │ └── queries.sql ├── sqlboiler │ ├── boil_table_names.go │ ├── boil_queries.go │ ├── boil_types.go │ └── psql_upsert.go ├── reform │ ├── reform_models.go │ └── reform_models_reform.go ├── pop.go ├── xorm.go ├── godb.go ├── gorp.go ├── beego.go ├── sqlboiler.go ├── gorm.go ├── rel.go ├── pg.go ├── dbr.go ├── gorm_prep.go ├── reform.go ├── utils.go ├── upper.go ├── bun.go ├── zorm.go ├── pgx.go ├── sqlx.go ├── pgx_pool.go ├── sqlgen.go ├── ent.go ├── models.go ├── sqlc.go ├── raw.go ├── benchmark_suite.go └── generated.go ├── .github ├── dependabot.yml ├── workflows │ ├── security.yaml │ ├── golangci-lint.yaml │ ├── codeql.yaml │ ├── build.yaml │ └── results.yaml └── run_benchmarks_actions.sh ├── data └── results.md.proto ├── Dockerfile ├── docker-compose.yml ├── run_benchmarks.sh ├── sqlgen.yml ├── LICENSE ├── .gitignore ├── main.go ├── README.md ├── go.mod ├── .gitattributes └── db └── operator.go /benchs/ent/generate.go: -------------------------------------------------------------------------------- 1 | package ent 2 | 3 | //go:generate go run -mod=mod entgo.io/ent/cmd/ent generate ./schema 4 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "gomod" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | -------------------------------------------------------------------------------- /benchs/sqlc/README.md: -------------------------------------------------------------------------------- 1 | ### How to generate on Windows (PowerShell): 2 | 3 | ```powershell 4 | docker run --rm -v ${PWD}:/src -w /src kjconroy/sqlc generate 5 | ``` 6 | -------------------------------------------------------------------------------- /benchs/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 | // Model is the predicate function for model builders. 10 | type Model func(*sql.Selector) 11 | -------------------------------------------------------------------------------- /.github/workflows/security.yaml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | name: GoSec 3 | jobs: 4 | GoSec: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - name: Fetch Repository 8 | uses: actions/checkout@v2 9 | - name: Run GoSec Security Scanner 10 | uses: securego/gosec@master -------------------------------------------------------------------------------- /benchs/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 | -------------------------------------------------------------------------------- /.github/workflows/golangci-lint.yaml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | name: GoLint 3 | jobs: 4 | GoLint: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - name: Fetch Repository 8 | uses: actions/checkout@v2 9 | - name: Check GoLint Errors 10 | uses: reviewdog/action-golangci-lint@v2 -------------------------------------------------------------------------------- /benchs/sqlc/db/models.go: -------------------------------------------------------------------------------- 1 | // Code generated by sqlc. DO NOT EDIT. 2 | // versions: 3 | // sqlc v1.13.0 4 | 5 | package db 6 | 7 | import () 8 | 9 | type Model struct { 10 | ID int32 11 | Name string 12 | Title string 13 | Fax string 14 | Web string 15 | Age int32 16 | Right bool 17 | Counter int64 18 | } 19 | -------------------------------------------------------------------------------- /benchs/sqlc/schema.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE models ( 2 | ID SERIAL NOT NULL, 3 | NAME TEXT NOT NULL, 4 | title TEXT NOT NULL, 5 | fax TEXT NOT NULL, 6 | web TEXT NOT NULL, 7 | age INTEGER NOT NULL, 8 | "right" BOOLEAN NOT NULL, 9 | counter BIGINT NOT NULL, 10 | CONSTRAINT models_pkey PRIMARY KEY (ID) 11 | ) WITH (OIDS = FALSE); -------------------------------------------------------------------------------- /data/results.md.proto: -------------------------------------------------------------------------------- 1 | # Results 2 | 3 | - orm-benchmark (with no flags) 4 | 5 | ``` 6 | @(with-no-flag) 7 | ``` 8 | 9 | - orm-benchmark -multi=5 10 | 11 | ``` 12 | @(multi-5) 13 | ``` 14 | 15 | - orm-benchmark -multi=10 16 | 17 | ``` 18 | @(multi-10) 19 | ``` 20 | 21 | - orm-benchmark -multi=20 22 | 23 | ``` 24 | @(multi-20) 25 | ``` 26 | -------------------------------------------------------------------------------- /benchs/sqlc/sqlc.yaml: -------------------------------------------------------------------------------- 1 | version: "1" 2 | packages: 3 | - name: "db" 4 | path: "db" 5 | queries: "./queries.sql" 6 | schema: "./schema.sql" 7 | engine: "postgresql" 8 | # emit_prepared_queries: true 9 | # emit_interface: false 10 | # emit_exact_table_names: false 11 | # emit_empty_slices: false 12 | # emit_json_tags: true 13 | # json_tags_case_style: "camel" 14 | -------------------------------------------------------------------------------- /benchs/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 github.com/efectn/go-orm-benchmarks/benchs/ent/runtime.go 6 | 7 | const ( 8 | Version = "v0.10.1" // Version of ent codegen. 9 | Sum = "h1:dM5h4Zk6yHGIgw4dCqVzGw3nWgpGYJiV4/kyHEF6PFo=" // Sum of ent codegen. 10 | ) 11 | -------------------------------------------------------------------------------- /benchs/sqlboiler/boil_table_names.go: -------------------------------------------------------------------------------- 1 | // Code generated by SQLBoiler 4.8.3 (https://github.com/volatiletech/sqlboiler). DO NOT EDIT. 2 | // This file is meant to be re-generated in place and/or deleted at any time. 3 | 4 | package models 5 | 6 | var TableNames = struct { 7 | Model string 8 | Models string 9 | ModelsGorp string 10 | }{ 11 | Model: "model", 12 | Models: "models", 13 | ModelsGorp: "models_gorp", 14 | } 15 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Define specific args for benchmark 2 | FROM golang:alpine 3 | 4 | # Define env vars 5 | ARG MULTI 6 | ARG ORM 7 | ENV ORM=${ORM:-all} 8 | ENV MULTI=${MULTI:-1} 9 | 10 | # Build 11 | WORKDIR /app 12 | COPY . . 13 | RUN CGO_ENABLED=0 GOOS=linux go build -ldflags "-s -w" -o /bin/go-orm-benchmarks ./main.go 14 | 15 | # Run 16 | CMD /bin/go-orm-benchmarks -source="host=postgres user=postgres password=postgres dbname=test sslmode=disable" -orm=$ORM -multi=$MULTI -debug=false -------------------------------------------------------------------------------- /benchs/reform/reform_models.go: -------------------------------------------------------------------------------- 1 | package reform 2 | 3 | //go:generate reform 4 | 5 | // reform_models represents a row in models table. 6 | //reform:models 7 | type ReformModels struct { 8 | ID int `reform:"id,pk"` 9 | Name string `reform:"name"` 10 | Title string `reform:"title"` 11 | Fax string `reform:"fax"` 12 | Web string `reform:"web"` 13 | Age int32 `reform:"age"` 14 | Right bool `reform:"right"` 15 | Counter int64 `reform:"counter"` 16 | } 17 | -------------------------------------------------------------------------------- /benchs/sqlc/queries.sql: -------------------------------------------------------------------------------- 1 | -- name: CreateModel :one 2 | INSERT INTO models (NAME, title, fax, web, age, "right", counter) 3 | VALUES ($1, $2, $3, $4, $5, $6, $7) 4 | RETURNING *; 5 | -- name: UpdateModel :exec 6 | UPDATE models 7 | SET name = $1, 8 | title = $2, 9 | fax = $3, 10 | web = $4, 11 | age = $5, 12 | "right" = $6, 13 | counter = $7 14 | WHERE id = $8; 15 | -- name: GetModel :one 16 | SELECT * 17 | FROM models 18 | WHERE id = $1; 19 | -- name: ListModels :many 20 | SELECT * 21 | FROM models 22 | WHERE ID > $1 23 | ORDER BY ID 24 | LIMIT $2; -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | networks: 4 | default: 5 | driver: bridge 6 | 7 | services: 8 | postgres: 9 | image: 'postgres:14-alpine' 10 | networks: 11 | - default 12 | ports: 13 | - 5432:5432 14 | environment: 15 | - POSTGRES_USER=postgres 16 | - POSTGRES_PASSWORD=postgres 17 | - POSTGRES_DB=test 18 | 19 | benchmarker: 20 | build: 21 | context: . 22 | dockerfile: Dockerfile 23 | networks: 24 | - default 25 | depends_on: 26 | - postgres 27 | links: 28 | - postgres -------------------------------------------------------------------------------- /.github/workflows/codeql.yaml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | on: [push, pull_request] 3 | jobs: 4 | codeql: 5 | name: CodeQL Analysis 6 | runs-on: ubuntu-latest 7 | steps: 8 | - name: Fetch Repository 9 | uses: actions/checkout@v2 10 | with: 11 | fetch-depth: 2 12 | - run: git checkout HEAD^2 13 | if: ${{ github.event_name == 'pull_request' }} 14 | - name: Initialize CodeQL 15 | uses: github/codeql-action/init@v1 16 | with: 17 | languages: go 18 | - name: Autobuild 19 | uses: github/codeql-action/autobuild@v1 20 | - name: Perform CodeQL Analysis 21 | uses: github/codeql-action/analyze@v1 -------------------------------------------------------------------------------- /benchs/ent/schema/model.go: -------------------------------------------------------------------------------- 1 | package schema 2 | 3 | import ( 4 | "entgo.io/ent" 5 | "entgo.io/ent/schema/field" 6 | ) 7 | 8 | // Model holds the schema definition for the Model entity. 9 | type Model struct { 10 | ent.Schema 11 | } 12 | 13 | // Fields of the Model. 14 | func (Model) Fields() []ent.Field { 15 | return []ent.Field{ 16 | //field.Int("id").Unique().StorageKey("oid").Primar 17 | field.String("name"), 18 | field.String("title"), 19 | field.String("fax"), 20 | field.String("web"), 21 | field.Int("age"), 22 | field.Bool("right"), 23 | field.Int64("counter"), 24 | } 25 | } 26 | 27 | // Edges of the Model. 28 | func (Model) Edges() []ent.Edge { 29 | return nil 30 | } 31 | -------------------------------------------------------------------------------- /benchs/sqlc/db/db.go: -------------------------------------------------------------------------------- 1 | // Code generated by sqlc. DO NOT EDIT. 2 | // versions: 3 | // sqlc v1.13.0 4 | 5 | package db 6 | 7 | import ( 8 | "context" 9 | "database/sql" 10 | ) 11 | 12 | type DBTX interface { 13 | ExecContext(context.Context, string, ...interface{}) (sql.Result, error) 14 | PrepareContext(context.Context, string) (*sql.Stmt, error) 15 | QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error) 16 | QueryRowContext(context.Context, string, ...interface{}) *sql.Row 17 | } 18 | 19 | func New(db DBTX) *Queries { 20 | return &Queries{db: db} 21 | } 22 | 23 | type Queries struct { 24 | db DBTX 25 | } 26 | 27 | func (q *Queries) WithTx(tx *sql.Tx) *Queries { 28 | return &Queries{ 29 | db: tx, 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /.github/workflows/build.yaml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | name: Benchmark 3 | jobs: 4 | Build: 5 | strategy: 6 | matrix: 7 | platform: [ubuntu-latest] 8 | runs-on: ${{ matrix.platform }} 9 | services: 10 | postgres: 11 | image: 'postgres:latest' 12 | ports: 13 | - '5432:5432' 14 | env: 15 | POSTGRES_USER: postgres 16 | POSTGRES_PASSWORD: postgres 17 | POSTGRES_DB: test 18 | steps: 19 | - name: Fetch Repository 20 | uses: actions/checkout@v3 21 | - name: Build Benchmarker 22 | run: go build main.go 23 | - name: Run Benchmarker 24 | run: ./main -source="host=localhost user=postgres password=postgres dbname=test sslmode=disable" -orm=all -multi=1 -debug=false -------------------------------------------------------------------------------- /.github/run_benchmarks_actions.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Prototype benchmark results 4 | proto=$(cat data/results.md.proto) 5 | 6 | # Benchmark operations 7 | operations=("with-no-flag|1" "multi-5|5" "multi-10|10" "multi-20|20") 8 | 9 | # Build benchmarker 10 | go build main.go 11 | 12 | for operation in "${operations[@]}"; do 13 | IFS="|"; read -a operation <<< "$operation" 14 | 15 | # Apply output to template 16 | logs=$(./main -source="host=localhost user=postgres password=postgres dbname=test sslmode=disable" -multi=${operation[1]} -debug=false) 17 | escaped=$(echo "${logs}" | sed '$!s@$@\\@g') 18 | 19 | proto=$(sed "s|@(${operation[0]})|${escaped}|g" <<< $proto) 20 | done 21 | 22 | # Print final results & delete benchmarker 23 | rm main 24 | echo $proto -------------------------------------------------------------------------------- /.github/workflows/results.yaml: -------------------------------------------------------------------------------- 1 | on: [push] 2 | name: Generate Results 3 | jobs: 4 | Results: 5 | strategy: 6 | matrix: 7 | platform: [ubuntu-latest] 8 | runs-on: ${{ matrix.platform }} 9 | services: 10 | postgres: 11 | image: 'postgres:latest' 12 | ports: 13 | - '5432:5432' 14 | env: 15 | POSTGRES_DB: test 16 | POSTGRES_USER: postgres 17 | POSTGRES_PASSWORD: postgres 18 | steps: 19 | - name: Fetch Repository 20 | uses: actions/checkout@v3 21 | - name: Generate Results 22 | run: | 23 | git config user.name 'github-actions[bot]' 24 | git config user.email 'github-actions[bot]@users.noreply.github.com' 25 | 26 | .github/run_benchmarks_actions.sh > results.md 27 | git add results.md 28 | git commit -am "Generate benchmark results automatically." 29 | git push -------------------------------------------------------------------------------- /run_benchmarks.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Prototype benchmark results 4 | proto=$(cat data/results.md.proto) 5 | 6 | # Benchmark operations 7 | operations=("with-no-flag|1" "multi-5|5" "multi-10|10" "multi-20|20") 8 | #operations=("with-no-flag|") 9 | 10 | for operation in "${operations[@]}"; do 11 | IFS="|"; read -a operation <<< "$operation" 12 | 13 | # Debug Message 14 | echo "[go-orm-benchmarks] DBG: Benchmarks for ${operation[0]} is running at the moment." 15 | 16 | # Build & Run Container 17 | docker-compose build --build-arg MULTI=${operation[1]} &>/dev/null 18 | docker-compose up --exit-code-from benchmarker 19 | 20 | # Apply output to template 21 | logs=$(docker logs go-orm-benchmarks-benchmarker-1 --tail 102) 22 | escaped=$(echo "${logs}" | sed '$!s@$@\\@g') 23 | 24 | proto=$(sed "s|@(${operation[0]})|${escaped}|g" <<< $proto) 25 | 26 | echo "[go-orm-benchmarks] DBG: Benchmarks for ${operation[0]} is completed!" 27 | done 28 | 29 | # Print final results 30 | echo $proto -------------------------------------------------------------------------------- /benchs/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 | -------------------------------------------------------------------------------- /benchs/sqlboiler/boil_queries.go: -------------------------------------------------------------------------------- 1 | // Code generated by SQLBoiler 4.8.3 (https://github.com/volatiletech/sqlboiler). DO NOT EDIT. 2 | // This file is meant to be re-generated in place and/or deleted at any time. 3 | 4 | package models 5 | 6 | import ( 7 | "github.com/volatiletech/sqlboiler/v4/drivers" 8 | "github.com/volatiletech/sqlboiler/v4/queries" 9 | "github.com/volatiletech/sqlboiler/v4/queries/qm" 10 | ) 11 | 12 | var dialect = drivers.Dialect{ 13 | LQ: 0x22, 14 | RQ: 0x22, 15 | 16 | UseIndexPlaceholders: true, 17 | UseLastInsertID: false, 18 | UseSchema: false, 19 | UseDefaultKeyword: true, 20 | UseAutoColumns: false, 21 | UseTopClause: false, 22 | UseOutputClause: false, 23 | UseCaseWhenExistsClause: false, 24 | } 25 | 26 | // NewQuery initializes a new Query using the passed in QueryMods 27 | func NewQuery(mods ...qm.QueryMod) *queries.Query { 28 | q := &queries.Query{} 29 | queries.SetDialect(q, &dialect) 30 | qm.Apply(q, mods...) 31 | 32 | return q 33 | } 34 | -------------------------------------------------------------------------------- /sqlgen.yml: -------------------------------------------------------------------------------- 1 | # Where are all the model files located? globs are supported eg src/**/*.go 2 | src: 3 | - ./benchs/models*.go 4 | 5 | # Optional: 6 | # Possibly values : `mysql`, `postgres` or `sqlite` 7 | driver: mysql 8 | 9 | # Optional: 10 | # Possibly values : `snake_case`, `camelCase` or `PascalCase` 11 | naming_convention: snake_case 12 | 13 | # Optional: 14 | struct_tag: sql 15 | 16 | # Optional: 17 | quote_identifier: true 18 | 19 | # Optional: Where should the generated model code go? 20 | exec: 21 | skip_empty: false 22 | filename: generated.go 23 | 24 | # Optional: Where should the generated database code go? 25 | database: 26 | dir: db 27 | package: db 28 | filename: db.go 29 | operator: 30 | filename: operator.go 31 | 32 | # Optional: 33 | strict: true 34 | 35 | # Optional: turn on to not generate any file header in generated files 36 | skip_header: false 37 | 38 | # Optional: 39 | source_map: false 40 | 41 | # Optional: set to skip running `go mod tidy` when generating server code 42 | # skip_mod_tidy: false -------------------------------------------------------------------------------- /benchs/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 | // ModelsColumns holds the columns for the "models" table. 12 | ModelsColumns = []*schema.Column{ 13 | {Name: "id", Type: field.TypeInt, Increment: true}, 14 | {Name: "name", Type: field.TypeString}, 15 | {Name: "title", Type: field.TypeString}, 16 | {Name: "fax", Type: field.TypeString}, 17 | {Name: "web", Type: field.TypeString}, 18 | {Name: "age", Type: field.TypeInt}, 19 | {Name: "right", Type: field.TypeBool}, 20 | {Name: "counter", Type: field.TypeInt64}, 21 | } 22 | // ModelsTable holds the schema information for the "models" table. 23 | ModelsTable = &schema.Table{ 24 | Name: "models", 25 | Columns: ModelsColumns, 26 | PrimaryKey: []*schema.Column{ModelsColumns[0]}, 27 | } 28 | // Tables holds all the tables in the schema. 29 | Tables = []*schema.Table{ 30 | ModelsTable, 31 | } 32 | ) 33 | 34 | func init() { 35 | } 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 M. Efe Çetin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /benchs/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 | Model []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 | -------------------------------------------------------------------------------- /benchs/sqlboiler/boil_types.go: -------------------------------------------------------------------------------- 1 | // Code generated by SQLBoiler 4.8.3 (https://github.com/volatiletech/sqlboiler). DO NOT EDIT. 2 | // This file is meant to be re-generated in place and/or deleted at any time. 3 | 4 | package models 5 | 6 | import ( 7 | "strconv" 8 | 9 | "github.com/friendsofgo/errors" 10 | "github.com/volatiletech/sqlboiler/v4/boil" 11 | "github.com/volatiletech/strmangle" 12 | ) 13 | 14 | // M type is for providing columns and column values to UpdateAll. 15 | type M map[string]interface{} 16 | 17 | // ErrSyncFail occurs during insert when the record could not be retrieved in 18 | // order to populate default value information. This usually happens when LastInsertId 19 | // fails or there was a primary key configuration that was not resolvable. 20 | var ErrSyncFail = errors.New("models: failed to synchronize data after insert") 21 | 22 | type insertCache struct { 23 | query string 24 | retQuery string 25 | valueMapping []uint64 26 | retMapping []uint64 27 | } 28 | 29 | type updateCache struct { 30 | query string 31 | valueMapping []uint64 32 | } 33 | 34 | func makeCacheKey(cols boil.Columns, nzDefaults []string) string { 35 | buf := strmangle.GetBuffer() 36 | 37 | buf.WriteString(strconv.Itoa(cols.Kind)) 38 | for _, w := range cols.Cols { 39 | buf.WriteString(w) 40 | } 41 | 42 | if len(nzDefaults) != 0 { 43 | buf.WriteByte('.') 44 | } 45 | for _, nz := range nzDefaults { 46 | buf.WriteString(nz) 47 | } 48 | 49 | str := buf.String() 50 | strmangle.PutBuffer(buf) 51 | return str 52 | } 53 | -------------------------------------------------------------------------------- /benchs/ent/model/model.go: -------------------------------------------------------------------------------- 1 | // Code generated by entc, DO NOT EDIT. 2 | 3 | package model 4 | 5 | const ( 6 | // Label holds the string label denoting the model type in the database. 7 | Label = "model" 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 | // FieldTitle holds the string denoting the title field in the database. 13 | FieldTitle = "title" 14 | // FieldFax holds the string denoting the fax field in the database. 15 | FieldFax = "fax" 16 | // FieldWeb holds the string denoting the web field in the database. 17 | FieldWeb = "web" 18 | // FieldAge holds the string denoting the age field in the database. 19 | FieldAge = "age" 20 | // FieldRight holds the string denoting the right field in the database. 21 | FieldRight = "right" 22 | // FieldCounter holds the string denoting the counter field in the database. 23 | FieldCounter = "counter" 24 | // Table holds the table name of the model in the database. 25 | Table = "models" 26 | ) 27 | 28 | // Columns holds all SQL columns for model fields. 29 | var Columns = []string{ 30 | FieldID, 31 | FieldName, 32 | FieldTitle, 33 | FieldFax, 34 | FieldWeb, 35 | FieldAge, 36 | FieldRight, 37 | FieldCounter, 38 | } 39 | 40 | // ValidColumn reports if the column name is valid (part of the table columns). 41 | func ValidColumn(column string) bool { 42 | for i := range Columns { 43 | if column == Columns[i] { 44 | return true 45 | } 46 | } 47 | return false 48 | } 49 | -------------------------------------------------------------------------------- /benchs/sqlboiler/psql_upsert.go: -------------------------------------------------------------------------------- 1 | // Code generated by SQLBoiler 4.8.3 (https://github.com/volatiletech/sqlboiler). DO NOT EDIT. 2 | // This file is meant to be re-generated in place and/or deleted at any time. 3 | 4 | package models 5 | 6 | import ( 7 | "fmt" 8 | "strings" 9 | 10 | "github.com/volatiletech/sqlboiler/v4/drivers" 11 | "github.com/volatiletech/strmangle" 12 | ) 13 | 14 | // buildUpsertQueryPostgres builds a SQL statement string using the upsertData provided. 15 | func buildUpsertQueryPostgres(dia drivers.Dialect, tableName string, updateOnConflict bool, ret, update, conflict, whitelist []string) string { 16 | conflict = strmangle.IdentQuoteSlice(dia.LQ, dia.RQ, conflict) 17 | whitelist = strmangle.IdentQuoteSlice(dia.LQ, dia.RQ, whitelist) 18 | ret = strmangle.IdentQuoteSlice(dia.LQ, dia.RQ, ret) 19 | 20 | buf := strmangle.GetBuffer() 21 | defer strmangle.PutBuffer(buf) 22 | 23 | columns := "DEFAULT VALUES" 24 | if len(whitelist) != 0 { 25 | columns = fmt.Sprintf("(%s) VALUES (%s)", 26 | strings.Join(whitelist, ", "), 27 | strmangle.Placeholders(dia.UseIndexPlaceholders, len(whitelist), 1, 1)) 28 | } 29 | 30 | fmt.Fprintf( 31 | buf, 32 | "INSERT INTO %s %s ON CONFLICT ", 33 | tableName, 34 | columns, 35 | ) 36 | 37 | if !updateOnConflict || len(update) == 0 { 38 | buf.WriteString("DO NOTHING") 39 | } else { 40 | buf.WriteByte('(') 41 | buf.WriteString(strings.Join(conflict, ", ")) 42 | buf.WriteString(") DO UPDATE SET ") 43 | 44 | for i, v := range update { 45 | if i != 0 { 46 | buf.WriteByte(',') 47 | } 48 | quoted := strmangle.IdentQuote(dia.LQ, dia.RQ, v) 49 | buf.WriteString(quoted) 50 | buf.WriteString(" = EXCLUDED.") 51 | buf.WriteString(quoted) 52 | } 53 | } 54 | 55 | if len(ret) != 0 { 56 | buf.WriteString(" RETURNING ") 57 | buf.WriteString(strings.Join(ret, ", ")) 58 | } 59 | 60 | return buf.String() 61 | } 62 | -------------------------------------------------------------------------------- /benchs/ent/enttest/enttest.go: -------------------------------------------------------------------------------- 1 | // Code generated by entc, DO NOT EDIT. 2 | 3 | package enttest 4 | 5 | import ( 6 | "context" 7 | 8 | "github.com/efectn/go-orm-benchmarks/benchs/ent" 9 | // required by schema hooks. 10 | _ "github.com/efectn/go-orm-benchmarks/benchs/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 | -------------------------------------------------------------------------------- /benchs/pop.go: -------------------------------------------------------------------------------- 1 | package benchs 2 | 3 | import ( 4 | "fmt" 5 | 6 | popware "github.com/gobuffalo/pop/v6" 7 | _ "github.com/jackc/pgx/v4/stdlib" 8 | ) 9 | 10 | var pop *popware.Connection 11 | 12 | func init() { 13 | st := NewSuite("pop") 14 | st.InitF = func() { 15 | st.AddBenchmark("Insert", 200*OrmMulti, PopInsert) 16 | st.AddBenchmark("MultiInsert 100 row", 200*OrmMulti, PopInsertMulti) 17 | st.AddBenchmark("Update", 200*OrmMulti, PopUpdate) 18 | st.AddBenchmark("Read", 200*OrmMulti, PopRead) 19 | st.AddBenchmark("MultiRead limit 100", 200*OrmMulti, PopReadSlice) 20 | 21 | var err error 22 | pop, err = popware.NewConnection(&popware.ConnectionDetails{ 23 | Dialect: sqlDriver, 24 | URL: ConvertSourceToDSN(), 25 | }) 26 | CheckErr(err) 27 | 28 | err = pop.Open() 29 | CheckErr(err) 30 | } 31 | } 32 | 33 | func PopInsert(b *B) { 34 | var m *Model3 35 | WrapExecute(b, func() { 36 | InitDB() 37 | m = NewModel3() 38 | }) 39 | 40 | for i := 0; i < b.N; i++ { 41 | err := pop.Create(m) 42 | CheckErr(err, b) 43 | } 44 | } 45 | 46 | func PopInsertMulti(b *B) { 47 | panic(fmt.Errorf("doesn't support bulk-insert")) 48 | } 49 | 50 | func PopUpdate(b *B) { 51 | var m *Model3 52 | WrapExecute(b, func() { 53 | InitDB() 54 | m = NewModel3() 55 | err := pop.Create(m) 56 | CheckErr(err, b) 57 | }) 58 | 59 | for i := 0; i < b.N; i++ { 60 | err := pop.Update(m) 61 | CheckErr(err, b) 62 | } 63 | } 64 | 65 | func PopRead(b *B) { 66 | var m *Model3 67 | WrapExecute(b, func() { 68 | InitDB() 69 | m = NewModel3() 70 | err := pop.Create(m) 71 | CheckErr(err, b) 72 | }) 73 | 74 | for i := 0; i < b.N; i++ { 75 | err := pop.First(m) 76 | CheckErr(err, b) 77 | } 78 | } 79 | 80 | func PopReadSlice(b *B) { 81 | var m *Model3 82 | WrapExecute(b, func() { 83 | InitDB() 84 | m = NewModel3() 85 | for i := 0; i < 100; i++ { 86 | err := pop.Create(m) 87 | CheckErr(err, b) 88 | } 89 | }) 90 | 91 | for i := 0; i < b.N; i++ { 92 | var ms []Model3 93 | err := pop.Where("id > 0").Limit(100).All(&ms) 94 | CheckErr(err, b) 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows thumbnail cache files 2 | Thumbs.db 3 | Thumbs.db:encryptable 4 | ehthumbs.db 5 | ehthumbs_vista.db 6 | 7 | # Dump file 8 | *.stackdump 9 | 10 | # Folder config file 11 | [Dd]esktop.ini 12 | 13 | # Recycle Bin used on file shares 14 | $RECYCLE.BIN/ 15 | 16 | # Windows Installer files 17 | *.cab 18 | *.msi 19 | *.msix 20 | *.msm 21 | *.msp 22 | 23 | # Windows shortcuts 24 | *.lnk 25 | 26 | # General 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Icon must end with two \r 32 | Icon 33 | 34 | 35 | # Thumbnails 36 | ._* 37 | 38 | # Files that might appear in the root of a volume 39 | .DocumentRevisions-V100 40 | .fseventsd 41 | .Spotlight-V100 42 | .TemporaryItems 43 | .Trashes 44 | .VolumeIcon.icns 45 | .com.apple.timemachine.donotpresent 46 | 47 | # Directories potentially created on remote AFP share 48 | .AppleDB 49 | .AppleDesktop 50 | Network Trash Folder 51 | Temporary Items 52 | .apdisk 53 | 54 | # CMake 55 | cmake-build-*/ 56 | 57 | # Mongo Explorer plugin 58 | .idea/**/mongoSettings.xml 59 | 60 | # File-based project format 61 | *.iws 62 | 63 | # IntelliJ 64 | out/ 65 | 66 | # mpeltonen/sbt-idea plugin 67 | .idea_modules/ 68 | 69 | # JIRA plugin 70 | atlassian-ide-plugin.xml 71 | 72 | # Cursive Clojure plugin 73 | .idea/replstate.xml 74 | 75 | # Crashlytics plugin (for Android Studio and IntelliJ) 76 | com_crashlytics_export_strings.xml 77 | crashlytics.properties 78 | crashlytics-build.properties 79 | fabric.properties 80 | 81 | # Editor-based Rest Client 82 | .idea/httpRequests 83 | 84 | # Android studio 3.1+ serialized cache file 85 | .idea/caches/build_file_checksums.ser 86 | 87 | *.patch 88 | *.diff 89 | 90 | # Binaries for programs and plugins 91 | *.exe 92 | *.exe~ 93 | *.dll 94 | *.so 95 | *.dylib 96 | 97 | # Test binary, built with `go test -c` 98 | *.test 99 | 100 | # Output of the go coverage tool, specifically when used with LiteIDE 101 | *.out 102 | 103 | # Dependency directories (remove the comment below to include it) 104 | # vendor/ 105 | 106 | .idea -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "math/rand" 7 | "runtime" 8 | "strings" 9 | "time" 10 | 11 | "github.com/efectn/go-orm-benchmarks/benchs" 12 | 13 | _ "github.com/go-sql-driver/mysql" 14 | _ "github.com/lib/pq" 15 | ) 16 | 17 | // Version constant 18 | const VERSION = "1.0.2" 19 | 20 | type ListOpts []string 21 | 22 | func (opts *ListOpts) String() string { 23 | return fmt.Sprint(*opts) 24 | } 25 | 26 | func (opts *ListOpts) Set(value string) error { 27 | if value != "all" && !strings.Contains(" "+strings.Join(benchs.BrandNames, " ")+" ", " "+value+" ") { 28 | return fmt.Errorf("wrong run name %s", value) 29 | } 30 | *opts = append(*opts, value) 31 | return nil 32 | } 33 | 34 | func (opts ListOpts) Shuffle() { 35 | rd := rand.New(rand.NewSource(time.Now().UnixNano())) 36 | for i := 0; i < len(opts); i++ { 37 | a := rd.Intn(len(opts)) 38 | b := rd.Intn(len(opts)) 39 | opts[a], opts[b] = opts[b], opts[a] 40 | } 41 | } 42 | 43 | func main() { 44 | runtime.GOMAXPROCS(runtime.NumCPU()) 45 | 46 | var orms ListOpts 47 | flag.IntVar(&benchs.OrmMaxIdle, "max_idle", 200, "max idle conns") 48 | flag.IntVar(&benchs.OrmMaxConn, "max_conn", 200, "max open conns") 49 | flag.StringVar(&benchs.OrmSource, "source", "host=localhost user=postgres password=postgres dbname=test sslmode=disable", "postgres dsn source") 50 | flag.IntVar(&benchs.OrmMulti, "multi", 1, "base query nums x multi") 51 | flag.BoolVar(&benchs.DebugMode, "debug", true, "Enable debug mode (print not-sorted results of ORMs)") 52 | flag.Var(&orms, "orm", "orm name: all, "+strings.Join(benchs.BrandNames, ", ")) 53 | flag.Parse() 54 | 55 | var all bool 56 | 57 | if len(orms) == 0 { 58 | all = true 59 | } else { 60 | for _, n := range orms { 61 | if n == "all" { 62 | all = true 63 | } 64 | } 65 | } 66 | 67 | if all { 68 | orms = benchs.BrandNames 69 | } 70 | 71 | orms.Shuffle() 72 | 73 | for _, n := range orms { 74 | if benchs.DebugMode { 75 | fmt.Println(n) 76 | } 77 | benchs.RunBenchmark(n) 78 | } 79 | 80 | fmt.Print("\nReports:\n\n") 81 | fmt.Print(benchs.MakeReport()) 82 | 83 | } 84 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Go ORM Benchmarks 2 | 3 | [![Go Reference](https://pkg.go.dev/badge/github.com/efectn/go-orm-benchmarks.svg)](https://pkg.go.dev/github.com/efectn/go-orm-benchmarks) 4 | 5 | Advanced benchmarks for +10 Go ORMs. Originally forked from [orm-benchmark](https://github.com/frederikhors/orm-benchmark). 6 | 7 | ### ORMs 8 | 9 | All package run in no-cache mode. 10 | 11 | - [beego/orm](https://github.com/astaxie/beego/tree/master/orm) 12 | - [bun](https://github.com/uptrace/bun) 13 | - [gorm 2](https://github.com/go-gorm/gorm) 14 | - [pg](https://github.com/go-pg/pg) 15 | - [sqlc](https://github.com/kyleconroy/sqlc) 16 | - [xorm](https://github.com/xormplus/xorm) 17 | - [ent](https://github.com/ent/ent) 18 | - [upper](https://github.com/upper/db) 19 | - [gorp](https://github.com/go-gorp/gorp) 20 | - [godb](https://github.com/samonzeweb/godb) 21 | - [dbr](https://github.com/gocraft/dbr/) 22 | - [pop](https://github.com/gobuffalo/pop) 23 | - [rel](https://github.com/go-rel/rel) 24 | - [reform](https://github.com/go-reform/reform) 25 | - [sqlboiler](https://github.com/volatiletech/sqlboiler) 26 | - [sqlx](https://github.com/jmoiron/sqlx) 27 | - [pgx](https://github.com/jackc/pgx) 28 | - [zorm](https://gitee.com/chunanyong/zorm) 29 | - [sqlgen](https://github.com/si3nloong/sqlgen) 30 | 31 | See [`go.mod`](go.mod) for their latest versions. 32 | 33 | ### Run 34 | 35 | ```shell 36 | # install 37 | go install github.com/efectn/go-orm-benchmarks@latest 38 | # all 39 | go-orm-benchmarks -multi=20 -orm=all 40 | # portion 41 | go-orm-benchmarks -multi=20 -orm=gorm 42 | go-orm-benchmarks -multi=10 -orm=pg 43 | go-orm-benchmarks -multi=15 -orm=bun 44 | # ... and so on... 45 | ``` 46 | 47 | **_Note: Also, you can run `./run_benchmarks.sh` and you can get output like results.md format._** 48 | 49 | ### Results 50 | Look at [`results.md`](results.md) to see detailed benchmark results. 51 | 52 | **Note:** All results are automatically generated by [Github Actions](https://github.com/features/actions). Benchmark results may sometimes be wrong. 53 | 54 | ### License 55 | 56 | go-orm-benchmarks is licensed under the terms of the **MIT License** (see [LICENSE](LICENSE)). 57 | -------------------------------------------------------------------------------- /benchs/xorm.go: -------------------------------------------------------------------------------- 1 | package benchs 2 | 3 | import ( 4 | "xorm.io/xorm" 5 | ) 6 | 7 | var xo *xorm.Session 8 | 9 | func init() { 10 | st := NewSuite("xorm") 11 | st.InitF = func() { 12 | st.AddBenchmark("Insert", 200*OrmMulti, XormInsert) 13 | st.AddBenchmark("MultiInsert 100 row", 200*OrmMulti, XormInsertMulti) 14 | st.AddBenchmark("Update", 200*OrmMulti, XormUpdate) 15 | st.AddBenchmark("Read", 200*OrmMulti, XormRead) 16 | st.AddBenchmark("MultiRead limit 100", 200*OrmMulti, XormReadSlice) 17 | 18 | engine, err := xorm.NewEngine(sqlDriver, ConvertSourceToDSN()) 19 | CheckErr(err) 20 | 21 | xo = engine.NewSession() 22 | } 23 | } 24 | 25 | func XormInsert(b *B) { 26 | var m *Model5 27 | WrapExecute(b, func() { 28 | InitDB() 29 | m = NewModel5() 30 | }) 31 | 32 | for i := 0; i < b.N; i++ { 33 | m.Id = 0 34 | _, err := xo.Insert(m) 35 | CheckErr(err, b) 36 | } 37 | } 38 | 39 | func XormInsertMulti(b *B) { 40 | var ms []*Model5 41 | WrapExecute(b, func() { 42 | InitDB() 43 | ms = make([]*Model5, 0, 100) 44 | for i := 0; i < 100; i++ { 45 | ms = append(ms, NewModel5()) 46 | } 47 | }) 48 | 49 | for i := 0; i < b.N; i++ { 50 | _, err := xo.Insert(&ms) 51 | CheckErr(err, b) 52 | } 53 | } 54 | 55 | func XormUpdate(b *B) { 56 | var m *Model5 57 | WrapExecute(b, func() { 58 | InitDB() 59 | m = NewModel5() 60 | _, err := xo.Insert(m) 61 | CheckErr(err, b) 62 | }) 63 | 64 | for i := 0; i < b.N; i++ { 65 | _, err := xo.ID(m.Id).Update(m) 66 | CheckErr(err, b) 67 | } 68 | } 69 | 70 | func XormRead(b *B) { 71 | var m *Model5 72 | WrapExecute(b, func() { 73 | InitDB() 74 | m = NewModel5() 75 | _, err := xo.Insert(m) 76 | CheckErr(err, b) 77 | }) 78 | 79 | for i := 0; i < b.N; i++ { 80 | _, err := xo.ID(1).NoAutoCondition().Get(m) 81 | CheckErr(err, b) 82 | } 83 | } 84 | 85 | func XormReadSlice(b *B) { 86 | var m *Model5 87 | WrapExecute(b, func() { 88 | InitDB() 89 | m = NewModel5() 90 | for i := 0; i < 100; i++ { 91 | m.Id = 0 92 | _, err := xo.Insert(m) 93 | CheckErr(err, b) 94 | } 95 | }) 96 | 97 | for i := 0; i < b.N; i++ { 98 | var models []*Model5 99 | err := xo.Where("id > 0").Limit(100).Find(&models) 100 | CheckErr(err, b) 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /benchs/godb.go: -------------------------------------------------------------------------------- 1 | package benchs 2 | 3 | import ( 4 | _ "github.com/jackc/pgx/v4/stdlib" 5 | godbware "github.com/samonzeweb/godb" 6 | "github.com/samonzeweb/godb/adapters/mysql" 7 | ) 8 | 9 | var godb *godbware.DB 10 | 11 | func init() { 12 | st := NewSuite("godb") 13 | st.InitF = func() { 14 | st.AddBenchmark("Insert", 200*OrmMulti, GodbInsert) 15 | st.AddBenchmark("MultiInsert 100 row", 200*OrmMulti, GodbInsertMulti) 16 | st.AddBenchmark("Update", 200*OrmMulti, GodbUpdate) 17 | st.AddBenchmark("Read", 200*OrmMulti, GodbRead) 18 | st.AddBenchmark("MultiRead limit 100", 200*OrmMulti, GodbReadSlice) 19 | 20 | var err error 21 | godb, err = godbware.Open(mysql.Adapter, ConvertSourceToDSN()) 22 | CheckErr(err) 23 | } 24 | } 25 | 26 | func GodbInsert(b *B) { 27 | var m *Model2 28 | WrapExecute(b, func() { 29 | InitDB() 30 | m = NewModel2() 31 | }) 32 | 33 | for i := 0; i < b.N; i++ { 34 | err := godb.Insert(m).Do() 35 | CheckErr(err, b) 36 | } 37 | } 38 | 39 | func GodbInsertMulti(b *B) { 40 | var ms []*Model2 41 | WrapExecute(b, func() { 42 | InitDB() 43 | ms = make([]*Model2, 0, 100) 44 | for i := 1; i < 101; i++ { 45 | ms = append(ms, NewModel2()) 46 | } 47 | }) 48 | 49 | for i := 0; i < b.N; i++ { 50 | err := godb.BulkInsert(&ms).Do() 51 | CheckErr(err, b) 52 | 53 | } 54 | } 55 | 56 | func GodbUpdate(b *B) { 57 | var m *Model2 58 | WrapExecute(b, func() { 59 | InitDB() 60 | m = NewModel2() 61 | err := godb.Insert(m).Do() 62 | CheckErr(err, b) 63 | }) 64 | 65 | for i := 0; i < b.N; i++ { 66 | err := godb.Update(m).Do() 67 | CheckErr(err, b) 68 | } 69 | } 70 | 71 | func GodbRead(b *B) { 72 | var m *Model2 73 | WrapExecute(b, func() { 74 | InitDB() 75 | m = NewModel2() 76 | err := godb.Insert(m).Do() 77 | CheckErr(err, b) 78 | }) 79 | 80 | for i := 0; i < b.N; i++ { 81 | err := godb.Select(m).Do() 82 | CheckErr(err, b) 83 | } 84 | } 85 | 86 | func GodbReadSlice(b *B) { 87 | var m *Model2 88 | WrapExecute(b, func() { 89 | InitDB() 90 | m = NewModel2() 91 | for i := 0; i < 100; i++ { 92 | err := godb.Insert(m).Do() 93 | CheckErr(err, b) 94 | } 95 | }) 96 | 97 | for i := 0; i < b.N; i++ { 98 | var ms []*Model2 99 | err := godb.Select(&ms).Where("id > 0").Limit(100).Do() 100 | CheckErr(err, b) 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /benchs/gorp.go: -------------------------------------------------------------------------------- 1 | package benchs 2 | 3 | // import ( 4 | // "database/sql" 5 | // "fmt" 6 | 7 | // _ "github.com/jackc/pgx/v4/stdlib" 8 | // _ "github.com/lib/pq" 9 | // gorpware "gopkg.in/gorp.v1" 10 | // ) 11 | 12 | // var gorp *gorpware.DbMap 13 | 14 | // func init() { 15 | // st := NewSuite("gorp") 16 | // st.InitF = func() { 17 | // st.AddBenchmark("Insert", 200*OrmMulti, GorpInsert) 18 | // st.AddBenchmark("MultiInsert 100 row", 200*OrmMulti, GorpInsertMulti) 19 | // st.AddBenchmark("Update", 200*OrmMulti, GorpUpdate) 20 | // st.AddBenchmark("Read", 200*OrmMulti, GorpRead) 21 | // st.AddBenchmark("MultiRead limit 100", 200*OrmMulti, GorpReadSlice) 22 | 23 | // db, err := sql.Open("pgx", OrmSource) 24 | // CheckErr(err) 25 | 26 | // gorp = &gorpware.DbMap{Db: db, Dialect: gorpware.PostgresDialect{}} 27 | 28 | // gorp.AddTableWithName(Model{}, "models").SetKeys(true, "Id") 29 | // } 30 | // } 31 | 32 | // func GorpInsert(b *B) { 33 | // var m *Model 34 | // WrapExecute(b, func() { 35 | // InitDB() 36 | // m = NewModel() 37 | // }) 38 | 39 | // for i := 0; i < b.N; i++ { 40 | // err := gorp.Insert(m) 41 | // CheckErr(err, b) 42 | // } 43 | // } 44 | 45 | // func GorpInsertMulti(b *B) { 46 | // panic(fmt.Errorf("doesn't support bulk-insert")) 47 | // } 48 | 49 | // func GorpUpdate(b *B) { 50 | // var m *Model 51 | // WrapExecute(b, func() { 52 | // InitDB() 53 | // m = NewModel() 54 | // err := gorp.Insert(m) 55 | // CheckErr(err, b) 56 | // }) 57 | 58 | // for i := 0; i < b.N; i++ { 59 | // _, err := gorp.Update(m) 60 | 61 | // CheckErr(err, b) 62 | // } 63 | // } 64 | 65 | // func GorpRead(b *B) { 66 | // var m *Model 67 | // WrapExecute(b, func() { 68 | // InitDB() 69 | // m = NewModel() 70 | // err := gorp.Insert(m) 71 | // CheckErr(err, b) 72 | // }) 73 | 74 | // for i := 0; i < b.N; i++ { 75 | // err := gorp.SelectOne(m, "SELECT * FROM models") 76 | // CheckErr(err, b) 77 | // } 78 | // } 79 | 80 | // func GorpReadSlice(b *B) { 81 | // var m *Model 82 | // WrapExecute(b, func() { 83 | // InitDB() 84 | // m = NewModel() 85 | // for i := 0; i < 100; i++ { 86 | // err := gorp.Insert(m) 87 | // CheckErr(err, b) 88 | // } 89 | // }) 90 | 91 | // for i := 0; i < b.N; i++ { 92 | // var ms []*Model 93 | // _, err := gorp.Select(&ms, "select * from models where id > 0 LIMIT 100") 94 | // CheckErr(err, b) 95 | // } 96 | // } 97 | -------------------------------------------------------------------------------- /benchs/beego.go: -------------------------------------------------------------------------------- 1 | package benchs 2 | 3 | // import ( 4 | // "github.com/astaxie/beego/orm" 5 | // ) 6 | 7 | // var bo orm.Ormer 8 | 9 | // func init() { 10 | // st := NewSuite("beego") 11 | // st.InitF = func() { 12 | // st.AddBenchmark("Insert", 200*OrmMulti, BeegoOrmInsert) 13 | // st.AddBenchmark("MultiInsert 100 row", 200*OrmMulti, BeegoOrmInsertMulti) 14 | // st.AddBenchmark("Update", 200*OrmMulti, BeegoOrmUpdate) 15 | // st.AddBenchmark("Read", 200*OrmMulti, BeegoOrmRead) 16 | // st.AddBenchmark("MultiRead limit 100", 200*OrmMulti, BeegoOrmReadSlice) 17 | 18 | // err := orm.RegisterDataBase("default", "postgres", OrmSource, OrmMaxIdle, OrmMaxConn) 19 | // CheckErr(err) 20 | 21 | // orm.RegisterModel(new(Model)) 22 | // bo = orm.NewOrm() 23 | // } 24 | // } 25 | 26 | // func BeegoOrmInsert(b *B) { 27 | // var m *Model 28 | // WrapExecute(b, func() { 29 | // InitDB() 30 | // m = NewModel() 31 | // }) 32 | 33 | // for i := 0; i < b.N; i++ { 34 | // m.Id = 0 35 | 36 | // _, err := bo.Insert(m) 37 | // CheckErr(err, b) 38 | // } 39 | // } 40 | 41 | // func BeegoOrmInsertMulti(b *B) { 42 | // var ms []*Model 43 | // WrapExecute(b, func() { 44 | // InitDB() 45 | // ms = make([]*Model, 0, 100) 46 | // for i := 0; i < 100; i++ { 47 | // ms = append(ms, NewModel()) 48 | // } 49 | // }) 50 | 51 | // for i := 0; i < b.N; i++ { 52 | // _, err := bo.InsertMulti(100, ms) 53 | // CheckErr(err, b) 54 | // } 55 | // } 56 | 57 | // func BeegoOrmUpdate(b *B) { 58 | // var m *Model 59 | // WrapExecute(b, func() { 60 | // InitDB() 61 | // m = NewModel() 62 | 63 | // _, err := bo.Insert(m) 64 | // CheckErr(err, b) 65 | // }) 66 | 67 | // for i := 0; i < b.N; i++ { 68 | // _, err := bo.Update(m) 69 | // CheckErr(err, b) 70 | // } 71 | // } 72 | 73 | // func BeegoOrmRead(b *B) { 74 | // var m *Model 75 | // WrapExecute(b, func() { 76 | // InitDB() 77 | // m = NewModel() 78 | // _, err := bo.Insert(m) 79 | // CheckErr(err, b) 80 | // }) 81 | 82 | // for i := 0; i < b.N; i++ { 83 | // err := bo.Read(m) 84 | // CheckErr(err, b) 85 | // } 86 | // } 87 | 88 | // func BeegoOrmReadSlice(b *B) { 89 | // var m *Model 90 | // WrapExecute(b, func() { 91 | // InitDB() 92 | // m = NewModel() 93 | // for i := 0; i < 100; i++ { 94 | // m.Id = 0 95 | // _, err := bo.Insert(m) 96 | // CheckErr(err, b) 97 | // } 98 | // }) 99 | 100 | // for i := 0; i < b.N; i++ { 101 | // var models []*Model 102 | // _, err := bo.QueryTable("models").Filter("id__gt", 0).Limit(100).All(&models) 103 | // CheckErr(err, b) 104 | // } 105 | // } 106 | -------------------------------------------------------------------------------- /benchs/sqlboiler.go: -------------------------------------------------------------------------------- 1 | package benchs 2 | 3 | // import ( 4 | // "database/sql" 5 | // "fmt" 6 | 7 | // models "github.com/efectn/go-orm-benchmarks/benchs/sqlboiler" 8 | // _ "github.com/jackc/pgx/v4/stdlib" 9 | // "github.com/volatiletech/sqlboiler/v4/boil" 10 | // "github.com/volatiletech/sqlboiler/v4/queries/qm" 11 | // ) 12 | 13 | // var sqlboiler *sql.DB 14 | 15 | // func init() { 16 | // st := NewSuite("sqlboiler") 17 | // st.InitF = func() { 18 | // st.AddBenchmark("Insert", 200*OrmMulti, SqlboilerInsert) 19 | // st.AddBenchmark("MultiInsert 100 row", 200*OrmMulti, SqlboilerInsertMulti) 20 | // st.AddBenchmark("Update", 200*OrmMulti, SqlboilerUpdate) 21 | // st.AddBenchmark("Read", 200*OrmMulti, SqlboilerRead) 22 | // st.AddBenchmark("MultiRead limit 100", 200*OrmMulti, SqlboilerReadSlice) 23 | 24 | // var err error 25 | // sqlboiler, err = sql.Open("pgx", OrmSource) 26 | // CheckErr(err) 27 | 28 | // boil.SetDB(sqlboiler) 29 | // } 30 | // } 31 | 32 | // func SqlboilerInsert(b *B) { 33 | // var m *models.Model 34 | // WrapExecute(b, func() { 35 | // InitDB() 36 | // m = NewModel6() 37 | // }) 38 | 39 | // for i := 0; i < b.N; i++ { 40 | // m.ID = 0 41 | // err := m.Insert(ctx, sqlboiler, boil.Infer()) 42 | // CheckErr(err, b) 43 | // } 44 | // } 45 | 46 | // func SqlboilerInsertMulti(b *B) { 47 | // panic(fmt.Errorf("doesn't support bulk-insert")) 48 | // } 49 | 50 | // func SqlboilerUpdate(b *B) { 51 | // var m *models.Model 52 | // WrapExecute(b, func() { 53 | // InitDB() 54 | // m = NewModel6() 55 | // m.ID = 0 56 | // err := m.Insert(ctx, sqlboiler, boil.Infer()) 57 | // CheckErr(err, b) 58 | // }) 59 | 60 | // for i := 0; i < b.N; i++ { 61 | // _, err := m.Update(ctx, sqlboiler, boil.Infer()) 62 | // CheckErr(err, b) 63 | // } 64 | // } 65 | 66 | // func SqlboilerRead(b *B) { 67 | // var m *models.Model 68 | // WrapExecute(b, func() { 69 | // InitDB() 70 | // m = NewModel6() 71 | // m.ID = 0 72 | // err := m.Insert(ctx, sqlboiler, boil.Infer()) 73 | // CheckErr(err, b) 74 | // }) 75 | 76 | // for i := 0; i < b.N; i++ { 77 | // _, err := models.Models(qm.Where("id = 0")).Exec(sqlboiler) 78 | // CheckErr(err, b) 79 | // } 80 | // } 81 | 82 | // func SqlboilerReadSlice(b *B) { 83 | // var m *models.Model 84 | // WrapExecute(b, func() { 85 | // InitDB() 86 | // m = NewModel6() 87 | // for i := 0; i < 100; i++ { 88 | // m.ID = 0 89 | // err := m.Insert(ctx, sqlboiler, boil.Infer()) 90 | // CheckErr(err, b) 91 | // } 92 | // }) 93 | 94 | // for i := 0; i < b.N; i++ { 95 | // _, err := models.Models(qm.Where("id > 0"), qm.Limit(100)).All(ctx, sqlboiler) 96 | // CheckErr(err, b) 97 | // } 98 | // } 99 | -------------------------------------------------------------------------------- /benchs/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 | // WithFixture sets the foreign-key renaming option to the migration when upgrading 32 | // ent from v0.1.0 (issue-#285). Defaults to false. 33 | WithFixture = schema.WithFixture 34 | // WithForeignKeys enables creating foreign-key in schema DDL. This defaults to true. 35 | WithForeignKeys = schema.WithForeignKeys 36 | ) 37 | 38 | // Schema is the API for creating, migrating and dropping a schema. 39 | type Schema struct { 40 | drv dialect.Driver 41 | } 42 | 43 | // NewSchema creates a new schema client. 44 | func NewSchema(drv dialect.Driver) *Schema { return &Schema{drv: drv} } 45 | 46 | // Create creates all schema resources. 47 | func (s *Schema) Create(ctx context.Context, opts ...schema.MigrateOption) error { 48 | migrate, err := schema.NewMigrate(s.drv, opts...) 49 | if err != nil { 50 | return fmt.Errorf("ent/migrate: %w", err) 51 | } 52 | return migrate.Create(ctx, Tables...) 53 | } 54 | 55 | // WriteTo writes the schema changes to w instead of running them against the database. 56 | // 57 | // if err := client.Schema.WriteTo(context.Background(), os.Stdout); err != nil { 58 | // log.Fatal(err) 59 | // } 60 | // 61 | func (s *Schema) WriteTo(ctx context.Context, w io.Writer, opts ...schema.MigrateOption) error { 62 | drv := &schema.WriteDriver{ 63 | Writer: w, 64 | Driver: s.drv, 65 | } 66 | migrate, err := schema.NewMigrate(drv, opts...) 67 | if err != nil { 68 | return fmt.Errorf("ent/migrate: %w", err) 69 | } 70 | return migrate.Create(ctx, Tables...) 71 | } 72 | -------------------------------------------------------------------------------- /benchs/gorm.go: -------------------------------------------------------------------------------- 1 | package benchs 2 | 3 | import ( 4 | "gorm.io/driver/mysql" 5 | "gorm.io/gorm" 6 | "gorm.io/gorm/logger" 7 | ) 8 | 9 | var gormdb *gorm.DB 10 | 11 | func init() { 12 | st := NewSuite("gorm") 13 | st.InitF = func() { 14 | st.AddBenchmark("Insert", 200*OrmMulti, GormInsert) 15 | st.AddBenchmark("MultiInsert 100 row", 200*OrmMulti, GormInsertMulti) 16 | st.AddBenchmark("Update", 200*OrmMulti, GormUpdate) 17 | st.AddBenchmark("Read", 200*OrmMulti, GormRead) 18 | st.AddBenchmark("MultiRead limit 100", 200*OrmMulti, GormReadSlice) 19 | 20 | var err error 21 | gormdb, err = gorm.Open(mysql.New(mysql.Config{ 22 | DSN: ConvertSourceToDSN(), 23 | // PreferSimpleProtocol: true, // disables implicit prepared statement usage 24 | }), &gorm.Config{ 25 | SkipDefaultTransaction: true, 26 | PrepareStmt: false, 27 | Logger: logger.Default.LogMode(logger.Silent), 28 | }) 29 | CheckErr(err) 30 | } 31 | } 32 | 33 | func GormInsert(b *B) { 34 | var m *Model 35 | WrapExecute(b, func() { 36 | InitDB() 37 | m = NewModel() 38 | }) 39 | 40 | for i := 0; i < b.N; i++ { 41 | m.Id = 0 42 | err := gormdb.Create(m).Error 43 | CheckErr(err, b) 44 | } 45 | } 46 | 47 | func GormInsertMulti(b *B) { 48 | var ms []*Model 49 | WrapExecute(b, func() { 50 | InitDB() 51 | ms = make([]*Model, 0, 100) 52 | for i := 0; i < 100; i++ { 53 | ms = append(ms, NewModel()) 54 | } 55 | }) 56 | 57 | for i := 0; i < b.N; i++ { 58 | for _, m := range ms { 59 | m.Id = 0 60 | } 61 | err := gormdb.Create(&ms).Error 62 | CheckErr(err, b) 63 | } 64 | } 65 | 66 | func GormUpdate(b *B) { 67 | var m *Model 68 | WrapExecute(b, func() { 69 | InitDB() 70 | m = NewModel() 71 | err := gormdb.Create(m).Error 72 | CheckErr(err, b) 73 | }) 74 | 75 | for i := 0; i < b.N; i++ { 76 | err := gormdb.Model(m).Updates(m).Error 77 | CheckErr(err, b) 78 | } 79 | } 80 | 81 | func GormRead(b *B) { 82 | var m *Model 83 | WrapExecute(b, func() { 84 | InitDB() 85 | m = NewModel() 86 | err := gormdb.Create(m).Error 87 | CheckErr(err, b) 88 | }) 89 | 90 | for i := 0; i < b.N; i++ { 91 | err := gormdb.Take(m).Error 92 | CheckErr(err, b) 93 | } 94 | } 95 | 96 | func GormReadSlice(b *B) { 97 | var m *Model 98 | WrapExecute(b, func() { 99 | InitDB() 100 | m = NewModel() 101 | for i := 0; i < 100; i++ { 102 | m.Id = 0 103 | err := gormdb.Create(m).Error 104 | CheckErr(err, b) 105 | } 106 | }) 107 | 108 | for i := 0; i < b.N; i++ { 109 | var models []*Model 110 | err := gormdb.Where("id > ?", 0).Limit(100).Find(&models).Error 111 | CheckErr(err, b) 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /benchs/rel.go: -------------------------------------------------------------------------------- 1 | package benchs 2 | 3 | import ( 4 | "context" 5 | "log" 6 | 7 | "github.com/go-rel/mysql" 8 | relware "github.com/go-rel/rel" 9 | "github.com/go-rel/rel/where" 10 | ) 11 | 12 | var rel relware.Repository 13 | 14 | func init() { 15 | st := NewSuite("rel") 16 | st.InitF = func() { 17 | st.AddBenchmark("Insert", 200*OrmMulti, RelInsert) 18 | st.AddBenchmark("MultiInsert 100 row", 200*OrmMulti, RelInsertMulti) 19 | st.AddBenchmark("Update", 200*OrmMulti, RelUpdate) 20 | st.AddBenchmark("Read", 200*OrmMulti, RelRead) 21 | st.AddBenchmark("MultiRead limit 100", 200*OrmMulti, RelReadSlice) 22 | 23 | conn, err := mysql.Open(ConvertSourceToDSN()) 24 | if err != nil { 25 | log.Fatalf("failed opening connection to postgres: %v", err) 26 | } 27 | 28 | rel = relware.New(conn) 29 | err = rel.Ping(ctx) 30 | CheckErr(err) 31 | 32 | // Disable debug logging 33 | rel.Instrumentation(func(ctx context.Context, op string, message string, args ...any) func(err error) { 34 | return func(err error) { 35 | CheckErr(err) 36 | } 37 | }) 38 | } 39 | } 40 | 41 | func RelInsert(b *B) { 42 | var m *Model3 43 | WrapExecute(b, func() { 44 | InitDB() 45 | m = NewModel3() 46 | }) 47 | 48 | for i := 0; i < b.N; i++ { 49 | m.ID = 0 50 | err := rel.Insert(ctx, m) 51 | CheckErr(err, b) 52 | } 53 | } 54 | 55 | func RelInsertMulti(b *B) { 56 | var ms []*Model3 57 | WrapExecute(b, func() { 58 | InitDB() 59 | ms = make([]*Model3, 0, 100) 60 | for i := 0; i < 100; i++ { 61 | ms = append(ms, NewModel3()) 62 | } 63 | }) 64 | 65 | for i := 0; i < b.N; i++ { 66 | for _, m := range ms { 67 | m.ID = 0 68 | } 69 | err := rel.InsertAll(ctx, &ms) 70 | CheckErr(err, b) 71 | } 72 | } 73 | 74 | func RelUpdate(b *B) { 75 | var m *Model3 76 | WrapExecute(b, func() { 77 | InitDB() 78 | m = NewModel3() 79 | m.ID = 0 80 | err := rel.Insert(ctx, m) 81 | CheckErr(err, b) 82 | }) 83 | 84 | for i := 0; i < b.N; i++ { 85 | err := rel.Update(ctx, m) 86 | CheckErr(err, b) 87 | } 88 | } 89 | 90 | func RelRead(b *B) { 91 | var m *Model3 92 | WrapExecute(b, func() { 93 | InitDB() 94 | m = NewModel3() 95 | m.ID = 0 96 | err := rel.Insert(ctx, m) 97 | CheckErr(err, b) 98 | }) 99 | 100 | for i := 0; i < b.N; i++ { 101 | err := rel.Find(ctx, m) 102 | CheckErr(err, b) 103 | } 104 | } 105 | 106 | func RelReadSlice(b *B) { 107 | var m *Model3 108 | WrapExecute(b, func() { 109 | InitDB() 110 | m = NewModel3() 111 | for i := 0; i < 100; i++ { 112 | m.ID = 0 113 | err := rel.Insert(ctx, m) 114 | CheckErr(err, b) 115 | } 116 | }) 117 | 118 | for i := 0; i < b.N; i++ { 119 | var ms []Model3 120 | err := rel.FindAll(ctx, &ms, where.Gt("id", 0), relware.Limit(100)) 121 | CheckErr(err, b) 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /benchs/pg.go: -------------------------------------------------------------------------------- 1 | package benchs 2 | 3 | // import ( 4 | // "github.com/go-pg/pg/v10" 5 | // ) 6 | 7 | // var pgdb *pg.DB 8 | 9 | // func init() { 10 | // st := NewSuite("pg") 11 | // st.InitF = func() { 12 | // st.AddBenchmark("Insert", 200*OrmMulti, PgInsert) 13 | // st.AddBenchmark("MultiInsert 100 row", 200*OrmMulti, PgInsertMulti) 14 | // st.AddBenchmark("Update", 200*OrmMulti, PgUpdate) 15 | // st.AddBenchmark("Read", 200*OrmMulti, PgRead) 16 | // st.AddBenchmark("MultiRead limit 100", 200*OrmMulti, PgReadSlice) 17 | 18 | // source := SplitSource() 19 | // pgdb = pg.Connect(&pg.Options{ 20 | // Addr: source["host"] + ":5432", 21 | // User: source["user"], 22 | // Password: source["password"], 23 | // Database: source["dbname"], 24 | // }) 25 | 26 | // err := pgdb.Ping(ctx) 27 | // CheckErr(err) 28 | // } 29 | // } 30 | 31 | // func PgInsert(b *B) { 32 | // var m *Model 33 | 34 | // WrapExecute(b, func() { 35 | // InitDB() 36 | // m = NewModel() 37 | // }) 38 | 39 | // for i := 0; i < b.N; i++ { 40 | // m.Id = 0 41 | // _, err := pgdb.Model(m).Insert() 42 | // CheckErr(err, b) 43 | // } 44 | // } 45 | 46 | // func PgInsertMulti(b *B) { 47 | // var ms []*Model 48 | 49 | // WrapExecute(b, func() { 50 | // InitDB() 51 | // ms = make([]*Model, 0, 100) 52 | // for i := 0; i < 100; i++ { 53 | // ms = append(ms, NewModel()) 54 | // } 55 | // }) 56 | 57 | // for i := 0; i < b.N; i++ { 58 | // for _, m := range ms { 59 | // m.Id = 0 60 | // } 61 | 62 | // _, err := pgdb.Model(&ms).Insert() 63 | // CheckErr(err, b) 64 | // } 65 | // } 66 | 67 | // func PgUpdate(b *B) { 68 | // var m *Model 69 | 70 | // WrapExecute(b, func() { 71 | // InitDB() 72 | // m = NewModel() 73 | // _, err := pgdb.Model(m).Insert() 74 | // CheckErr(err, b) 75 | // }) 76 | 77 | // for i := 0; i < b.N; i++ { 78 | // _, err := pgdb.Model(m).WherePK().Update() 79 | // CheckErr(err, b) 80 | // } 81 | // } 82 | 83 | // func PgRead(b *B) { 84 | // var m *Model 85 | // WrapExecute(b, func() { 86 | // InitDB() 87 | // m = NewModel() 88 | // _, err := pgdb.Model(m).Insert() 89 | // CheckErr(err, b) 90 | // }) 91 | 92 | // for i := 0; i < b.N; i++ { 93 | // err := pgdb.Model(m).Select() 94 | // CheckErr(err, b) 95 | // } 96 | // } 97 | 98 | // func PgReadSlice(b *B) { 99 | // var m *Model 100 | // WrapExecute(b, func() { 101 | // InitDB() 102 | // m = NewModel() 103 | // for i := 0; i < 100; i++ { 104 | // m.Id = 0 105 | // _, err := pgdb.Model(m).Insert() 106 | // CheckErr(err, b) 107 | // } 108 | // }) 109 | 110 | // for i := 0; i < b.N; i++ { 111 | // var models []*Model 112 | // err := pgdb.Model(&models).Where("id > ?", 0).Limit(100).Select() 113 | // CheckErr(err, b) 114 | // } 115 | // } 116 | -------------------------------------------------------------------------------- /benchs/dbr.go: -------------------------------------------------------------------------------- 1 | package benchs 2 | 3 | // import ( 4 | // "fmt" 5 | 6 | // dbrware "github.com/gocraft/dbr/v2" 7 | // _ "github.com/jackc/pgx/v4/stdlib" 8 | // ) 9 | 10 | // var dbr *dbrware.Session 11 | // var columns = []string{"name", "title", "fax", "web", "age", "right", "counter"} 12 | 13 | // func init() { 14 | // st := NewSuite("dbr") 15 | // st.InitF = func() { 16 | // st.AddBenchmark("Insert", 200*OrmMulti, DbrInsert) 17 | // st.AddBenchmark("MultiInsert 100 row", 200*OrmMulti, DbrInsertMulti) 18 | // st.AddBenchmark("Update", 200*OrmMulti, DbrUpdate) 19 | // st.AddBenchmark("Read", 200*OrmMulti, DbrRead) 20 | // st.AddBenchmark("MultiRead limit 100", 200*OrmMulti, DbrReadSlice) 21 | 22 | // conn, err := dbrware.Open("postgres", OrmSource, nil) 23 | // CheckErr(err) 24 | 25 | // dbr = conn.NewSession(nil) 26 | // dbr.Begin() 27 | // } 28 | // } 29 | 30 | // func DbrInsert(b *B) { 31 | // var m *Model2 32 | // WrapExecute(b, func() { 33 | // InitDB() 34 | // m = NewModel2() 35 | // }) 36 | 37 | // for i := 0; i < b.N; i++ { 38 | // _, err := dbr.InsertInto("models").Columns(columns...).Record(m).Exec() 39 | // CheckErr(err, b) 40 | // } 41 | // } 42 | 43 | // func DbrInsertMulti(b *B) { 44 | // panic(fmt.Errorf("doesn't support bulk-insert")) 45 | // } 46 | 47 | // func DbrUpdate(b *B) { 48 | // var m *Model2 49 | // WrapExecute(b, func() { 50 | // InitDB() 51 | // m = NewModel2() 52 | // _, err := dbr.InsertInto("models").Columns(columns...).Record(m).Exec() 53 | // CheckErr(err, b) 54 | // }) 55 | 56 | // for i := 0; i < b.N; i++ { 57 | // _, err := dbr.Update("models").SetMap(map[string]interface{}{ 58 | // "name": m.Name, 59 | // "title": m.Title, 60 | // "fax": m.Fax, 61 | // "web": m.Web, 62 | // "age": m.Age, 63 | // "right": m.Right, 64 | // "counter": m.Counter, 65 | // }).Exec() 66 | // CheckErr(err, b) 67 | // } 68 | // } 69 | 70 | // func DbrRead(b *B) { 71 | // var m *Model2 72 | // WrapExecute(b, func() { 73 | // InitDB() 74 | // m = NewModel2() 75 | // _, err := dbr.InsertInto("models").Columns(columns...).Record(m).Exec() 76 | // CheckErr(err, b) 77 | // }) 78 | 79 | // for i := 0; i < b.N; i++ { 80 | // _, err := dbr.Select("*").From("models").Where("id = ?", m.ID).Load(m) 81 | // CheckErr(err, b) 82 | // } 83 | // } 84 | 85 | // func DbrReadSlice(b *B) { 86 | // var m *Model2 87 | // WrapExecute(b, func() { 88 | // InitDB() 89 | // m = NewModel2() 90 | // for i := 0; i < 100; i++ { 91 | // _, err := dbr.InsertInto("models").Columns(columns...).Record(m).Exec() 92 | // CheckErr(err, b) 93 | // } 94 | // }) 95 | 96 | // for i := 0; i < b.N; i++ { 97 | // var ms []*Model2 98 | // _, err := dbr.Select("*").From("models").Where("id > 0").Limit(100).Load(&ms) 99 | // CheckErr(err, b) 100 | // } 101 | // } 102 | -------------------------------------------------------------------------------- /benchs/gorm_prep.go: -------------------------------------------------------------------------------- 1 | package benchs 2 | 3 | // import ( 4 | // "gorm.io/driver/postgres" 5 | // "gorm.io/gorm" 6 | // "gorm.io/gorm/logger" 7 | // ) 8 | 9 | // var gormdbp *gorm.DB 10 | 11 | // func init() { 12 | // st := NewSuite("gorm_prep") 13 | // st.InitF = func() { 14 | // st.AddBenchmark("Insert", 200*OrmMulti, GormPrepInsert) 15 | // st.AddBenchmark("MultiInsert 100 row", 200*OrmMulti, GormPrepInsertMulti) 16 | // st.AddBenchmark("Update", 200*OrmMulti, GormPrepUpdate) 17 | // st.AddBenchmark("Read", 200*OrmMulti, GormPrepRead) 18 | // st.AddBenchmark("MultiRead limit 100", 200*OrmMulti, GormPrepReadSlice) 19 | 20 | // var err error 21 | // gormdbp, err = gorm.Open(postgres.New(postgres.Config{ 22 | // DSN: OrmSource, 23 | // }), &gorm.Config{ 24 | // SkipDefaultTransaction: true, 25 | // PrepareStmt: true, 26 | // Logger: logger.Default.LogMode(logger.Silent), 27 | // }) 28 | // CheckErr(err) 29 | // } 30 | // } 31 | 32 | // func GormPrepInsert(b *B) { 33 | // var m *Model 34 | // WrapExecute(b, func() { 35 | // InitDB() 36 | // m = NewModel() 37 | // }) 38 | 39 | // for i := 0; i < b.N; i++ { 40 | // m.Id = 0 41 | // err := gormdbp.Create(m).Error 42 | // CheckErr(err, b) 43 | // } 44 | // } 45 | 46 | // func GormPrepInsertMulti(b *B) { 47 | // var ms []*Model 48 | // WrapExecute(b, func() { 49 | // InitDB() 50 | // ms = make([]*Model, 0, 100) 51 | // for i := 0; i < 100; i++ { 52 | // ms = append(ms, NewModel()) 53 | // } 54 | // }) 55 | 56 | // for i := 0; i < b.N; i++ { 57 | // for _, m := range ms { 58 | // m.Id = 0 59 | // } 60 | // err := gormdbp.Create(&ms).Error 61 | // CheckErr(err, b) 62 | // } 63 | // } 64 | 65 | // func GormPrepUpdate(b *B) { 66 | // var m *Model 67 | // WrapExecute(b, func() { 68 | // InitDB() 69 | // m = NewModel() 70 | // err := gormdbp.Create(m).Error 71 | // CheckErr(err, b) 72 | // }) 73 | 74 | // for i := 0; i < b.N; i++ { 75 | // err := gormdbp.Model(m).Updates(m).Error 76 | // CheckErr(err, b) 77 | // } 78 | // } 79 | 80 | // func GormPrepRead(b *B) { 81 | // var m *Model 82 | // WrapExecute(b, func() { 83 | // InitDB() 84 | // m = NewModel() 85 | // err := gormdbp.Create(m).Error 86 | // CheckErr(err, b) 87 | // }) 88 | 89 | // for i := 0; i < b.N; i++ { 90 | // err := gormdbp.Take(m).Error 91 | // CheckErr(err, b) 92 | // } 93 | // } 94 | 95 | // func GormPrepReadSlice(b *B) { 96 | // var m *Model 97 | // WrapExecute(b, func() { 98 | // InitDB() 99 | // m = NewModel() 100 | // for i := 0; i < 100; i++ { 101 | // m.Id = 0 102 | // err := gormdbp.Create(m).Error 103 | // CheckErr(err, b) 104 | // } 105 | // }) 106 | 107 | // for i := 0; i < b.N; i++ { 108 | // var models []*Model 109 | // err := gormdbp.Where("id > ?", 0).Limit(100).Find(&models).Error 110 | // CheckErr(err, b) 111 | // } 112 | // } 113 | -------------------------------------------------------------------------------- /benchs/ent/model_delete.go: -------------------------------------------------------------------------------- 1 | // Code generated by entc, DO NOT EDIT. 2 | 3 | package ent 4 | 5 | import ( 6 | "context" 7 | "fmt" 8 | 9 | "entgo.io/ent/dialect/sql" 10 | "entgo.io/ent/dialect/sql/sqlgraph" 11 | "entgo.io/ent/schema/field" 12 | "github.com/efectn/go-orm-benchmarks/benchs/ent/model" 13 | "github.com/efectn/go-orm-benchmarks/benchs/ent/predicate" 14 | ) 15 | 16 | // ModelDelete is the builder for deleting a Model entity. 17 | type ModelDelete struct { 18 | config 19 | hooks []Hook 20 | mutation *ModelMutation 21 | } 22 | 23 | // Where appends a list predicates to the ModelDelete builder. 24 | func (md *ModelDelete) Where(ps ...predicate.Model) *ModelDelete { 25 | md.mutation.Where(ps...) 26 | return md 27 | } 28 | 29 | // Exec executes the deletion query and returns how many vertices were deleted. 30 | func (md *ModelDelete) Exec(ctx context.Context) (int, error) { 31 | var ( 32 | err error 33 | affected int 34 | ) 35 | if len(md.hooks) == 0 { 36 | affected, err = md.sqlExec(ctx) 37 | } else { 38 | var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { 39 | mutation, ok := m.(*ModelMutation) 40 | if !ok { 41 | return nil, fmt.Errorf("unexpected mutation type %T", m) 42 | } 43 | md.mutation = mutation 44 | affected, err = md.sqlExec(ctx) 45 | mutation.done = true 46 | return affected, err 47 | }) 48 | for i := len(md.hooks) - 1; i >= 0; i-- { 49 | if md.hooks[i] == nil { 50 | return 0, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)") 51 | } 52 | mut = md.hooks[i](mut) 53 | } 54 | if _, err := mut.Mutate(ctx, md.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 (md *ModelDelete) ExecX(ctx context.Context) int { 63 | n, err := md.Exec(ctx) 64 | if err != nil { 65 | panic(err) 66 | } 67 | return n 68 | } 69 | 70 | func (md *ModelDelete) sqlExec(ctx context.Context) (int, error) { 71 | _spec := &sqlgraph.DeleteSpec{ 72 | Node: &sqlgraph.NodeSpec{ 73 | Table: model.Table, 74 | ID: &sqlgraph.FieldSpec{ 75 | Type: field.TypeInt, 76 | Column: model.FieldID, 77 | }, 78 | }, 79 | } 80 | if ps := md.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, md.driver, _spec) 88 | } 89 | 90 | // ModelDeleteOne is the builder for deleting a single Model entity. 91 | type ModelDeleteOne struct { 92 | md *ModelDelete 93 | } 94 | 95 | // Exec executes the deletion query. 96 | func (mdo *ModelDeleteOne) Exec(ctx context.Context) error { 97 | n, err := mdo.md.Exec(ctx) 98 | switch { 99 | case err != nil: 100 | return err 101 | case n == 0: 102 | return &NotFoundError{model.Label} 103 | default: 104 | return nil 105 | } 106 | } 107 | 108 | // ExecX is like Exec, but panics if an error occurs. 109 | func (mdo *ModelDeleteOne) ExecX(ctx context.Context) { 110 | mdo.md.ExecX(ctx) 111 | } 112 | -------------------------------------------------------------------------------- /benchs/reform.go: -------------------------------------------------------------------------------- 1 | package benchs 2 | 3 | // import ( 4 | // "database/sql" 5 | 6 | // r "github.com/efectn/go-orm-benchmarks/benchs/reform" 7 | // _ "github.com/jackc/pgx/v4/stdlib" 8 | // "gopkg.in/reform.v1/dialects/postgresql" 9 | 10 | // reformware "gopkg.in/reform.v1" 11 | // ) 12 | 13 | // var reform *reformware.DB 14 | 15 | // func NewReformModel() *r.ReformModels { 16 | // m := new(r.ReformModels) 17 | // m.Name = "Orm Benchmark" 18 | // m.Title = "Just a Benchmark for fun" 19 | // m.Fax = "99909990" 20 | // m.Web = "http://blog.milkpod29.me" 21 | // m.Age = 100 22 | // m.Right = true 23 | // m.Counter = 1000 24 | 25 | // return m 26 | // } 27 | 28 | // func init() { 29 | // st := NewSuite("reform") 30 | // st.InitF = func() { 31 | // st.AddBenchmark("Insert", 200*OrmMulti, ReformInsert) 32 | // st.AddBenchmark("MultiInsert 100 row", 200*OrmMulti, ReformInsertMulti) 33 | // st.AddBenchmark("Update", 200*OrmMulti, ReformUpdate) 34 | // st.AddBenchmark("Read", 200*OrmMulti, ReformRead) 35 | // st.AddBenchmark("MultiRead limit 100", 200*OrmMulti, ReformReadSlice) 36 | 37 | // db, err := sql.Open("pgx", OrmSource) 38 | // CheckErr(err) 39 | 40 | // reform = reformware.NewDB(db, postgresql.Dialect, nil) 41 | // } 42 | // } 43 | 44 | // func ReformInsert(b *B) { 45 | // var m *r.ReformModels 46 | // WrapExecute(b, func() { 47 | // InitDB() 48 | // m = NewReformModel() 49 | // }) 50 | 51 | // for i := 0; i < b.N; i++ { 52 | // err := reform.Save(m) 53 | // CheckErr(err, b) 54 | // } 55 | // } 56 | 57 | // func ReformInsertMulti(b *B) { 58 | // var ms []reformware.Struct 59 | // WrapExecute(b, func() { 60 | // InitDB() 61 | // ms = make([]reformware.Struct, 0, 100) 62 | // for i := 0; i < 100; i++ { 63 | // ms = append(ms, NewReformModel()) 64 | // } 65 | // }) 66 | 67 | // for i := 0; i < b.N; i++ { 68 | // err := reform.InsertMulti(ms...) 69 | // CheckErr(err, b) 70 | // } 71 | // } 72 | 73 | // func ReformUpdate(b *B) { 74 | // var m *r.ReformModels 75 | // WrapExecute(b, func() { 76 | // InitDB() 77 | // m = NewReformModel() 78 | // err := reform.Save(m) 79 | // CheckErr(err, b) 80 | // }) 81 | 82 | // for i := 0; i < b.N; i++ { 83 | // err := reform.Update(m) 84 | // CheckErr(err, b) 85 | // } 86 | // } 87 | 88 | // func ReformRead(b *B) { 89 | // var m *r.ReformModels 90 | // WrapExecute(b, func() { 91 | // InitDB() 92 | // m = NewReformModel() 93 | // err := reform.Save(m) 94 | // CheckErr(err, b) 95 | // }) 96 | 97 | // for i := 0; i < b.N; i++ { 98 | // _, err := reform.FindByPrimaryKeyFrom(r.ReformModelsTable, m.ID) 99 | // CheckErr(err, b) 100 | // } 101 | // } 102 | 103 | // func ReformReadSlice(b *B) { 104 | // var m *r.ReformModels 105 | // WrapExecute(b, func() { 106 | // InitDB() 107 | // m = NewReformModel() 108 | // for i := 0; i < 100; i++ { 109 | // err := reform.Save(m) 110 | // CheckErr(err, b) 111 | // } 112 | // }) 113 | 114 | // for i := 0; i < b.N; i++ { 115 | // _, err := reform.SelectAllFrom(r.ReformModelsTable, "WHERE id > 0 LIMIT 100") 116 | // CheckErr(err, b) 117 | // } 118 | // } 119 | -------------------------------------------------------------------------------- /benchs/utils.go: -------------------------------------------------------------------------------- 1 | package benchs 2 | 3 | import ( 4 | "database/sql" 5 | "log" 6 | "strings" 7 | 8 | _ "github.com/jackc/pgx/v4/stdlib" 9 | ) 10 | 11 | var ( 12 | OrmMulti int 13 | OrmMaxIdle int 14 | OrmMaxConn int 15 | OrmSource string 16 | DebugMode bool 17 | ) 18 | 19 | // Convert ORMSource to DSN (dburl) 20 | func ConvertSourceToDSN() string { 21 | template := "postgres://$(user):$(password)@$(host):5432/$(dbname)" 22 | 23 | // Parse one-by-one instead of using REGEX because of performance issues 24 | for _, option := range strings.Split(OrmSource, " ") { 25 | k := strings.Split(option, "=")[0] 26 | v := strings.Split(option, "=")[1] 27 | 28 | if strings.Contains(template, "$("+k+")") { 29 | template = strings.ReplaceAll(template, "$("+k+")", v) 30 | } else { 31 | template += "?" + option 32 | } 33 | } 34 | 35 | // log.Println("ConvertSourceToDSN =>", ConvertSourceToDSN()) 36 | 37 | template = "root:abcd1234@/go-orm?parseTime=true&multiStatements=true" 38 | return template 39 | } 40 | 41 | func SplitSource() map[string]string { 42 | options := make(map[string]string) 43 | // Split one-by-one instead of using REGEX because of performance issues 44 | for _, option := range strings.Split(OrmSource, " ") { 45 | k := strings.Split(option, "=")[0] 46 | v := strings.Split(option, "=")[1] 47 | 48 | options[k] = v 49 | } 50 | 51 | return options 52 | } 53 | 54 | func CheckErr(err error, b ...*B) { 55 | if err != nil { 56 | log.Fatalf("[go-orm-benchmarks] ERR: %v", err) 57 | 58 | if len(b) > 0 { 59 | b[0].FailNow() 60 | } 61 | } 62 | } 63 | 64 | func WrapExecute(b *B, cbk func()) { 65 | b.StopTimer() 66 | defer b.StartTimer() 67 | cbk() 68 | b.ResetTimer() 69 | } 70 | 71 | func InitDB() { 72 | // sqls := [][]string{ 73 | // { 74 | // `DROP TABLE IF EXISTS models;`, 75 | // `CREATE TABLE models ( 76 | // id SERIAL NOT NULL, 77 | // name text NOT NULL, 78 | // title text NOT NULL, 79 | // fax text NOT NULL, 80 | // web text NOT NULL, 81 | // age integer NOT NULL, 82 | // "right" boolean NOT NULL, 83 | // counter bigint NOT NULL, 84 | // CONSTRAINT models_pkey PRIMARY KEY (id) 85 | // ) WITH (OIDS=FALSE);`, 86 | // }, 87 | // { 88 | // `DROP TABLE IF EXISTS model5;`, 89 | // `CREATE TABLE model5 ( 90 | // id SERIAL NOT NULL, 91 | // name text NOT NULL, 92 | // title text NOT NULL, 93 | // fax text NOT NULL, 94 | // web text NOT NULL, 95 | // age integer NOT NULL, 96 | // "right" boolean NOT NULL, 97 | // counter bigint NOT NULL 98 | // ) WITH (OIDS=FALSE);`, 99 | // }, 100 | // } 101 | 102 | // DB, err := sql.Open("pgx", OrmSource) 103 | DB, err := sql.Open("mysql", "root:abcd1234@/go-orm?parseTime=true") 104 | CheckErr(err) 105 | defer func() { 106 | err := DB.Close() 107 | CheckErr(err) 108 | }() 109 | 110 | err = DB.Ping() 111 | CheckErr(err) 112 | 113 | // db.Migrate[Model](ctx, DB) 114 | // db.Migrate[Model5](ctx, DB) 115 | // for _, sql := range sqls { 116 | // for _, line := range sql { 117 | // _, err = DB.Exec(line) 118 | // CheckErr(err) 119 | // } 120 | // } 121 | 122 | } 123 | -------------------------------------------------------------------------------- /benchs/upper.go: -------------------------------------------------------------------------------- 1 | package benchs 2 | 3 | // import ( 4 | // db "github.com/upper/db/v4" 5 | // "github.com/upper/db/v4/adapter/postgresql" 6 | // ) 7 | 8 | // var upper db.Session 9 | 10 | // func init() { 11 | // st := NewSuite("upper") 12 | // st.InitF = func() { 13 | // st.AddBenchmark("Insert", 200*OrmMulti, UpperInsert) 14 | // st.AddBenchmark("MultiInsert 100 row", 200*OrmMulti, UpperInsertMulti) 15 | // st.AddBenchmark("Update", 200*OrmMulti, UpperUpdate) 16 | // st.AddBenchmark("Read", 200*OrmMulti, UpperRead) 17 | // st.AddBenchmark("MultiRead limit 100", 200*OrmMulti, UpperReadSlice) 18 | 19 | // var err error 20 | 21 | // source := SplitSource() 22 | // upper, err = postgresql.Open(postgresql.ConnectionURL{ 23 | // Host: source["host"] + ":5432", 24 | // User: source["user"], 25 | // Password: source["password"], 26 | // Database: source["dbname"], 27 | // }) 28 | // CheckErr(err) 29 | 30 | // // Disable logger 31 | // db.LC().SetLogger(nil) 32 | 33 | // err = upper.Ping() 34 | // CheckErr(err) 35 | // } 36 | // } 37 | 38 | // func UpperInsert(b *B) { 39 | // var m *Model4 40 | // WrapExecute(b, func() { 41 | // InitDB() 42 | // m = NewModel4() 43 | // }) 44 | 45 | // for i := 1; i < b.N+1; i++ { 46 | // _, err := upper.Collection("models").Insert(m) 47 | // CheckErr(err, b) 48 | // } 49 | // } 50 | 51 | // func UpperInsertMulti(b *B) { 52 | // var m *Model4 53 | // WrapExecute(b, func() { 54 | // InitDB() 55 | // m = NewModel4() 56 | // }) 57 | 58 | // for i := 1; i < b.N+1; i++ { 59 | // batch := upper.SQL().InsertInto("models").Columns("name", "title", "fax", "web", "age", "right", "counter").Batch(100) 60 | 61 | // go func() { 62 | // for i := 0; i < 100; i++ { 63 | // batch.Values(m.Name, m.Title, m.Fax, m.Web, m.Age, m.Right, m.Counter) 64 | // } 65 | // batch.Done() 66 | // }() 67 | 68 | // err := batch.Wait() 69 | // CheckErr(err, b) 70 | // } 71 | // } 72 | 73 | // func UpperUpdate(b *B) { 74 | // var m *Model4 75 | // WrapExecute(b, func() { 76 | // InitDB() 77 | // m = NewModel4() 78 | 79 | // err := upper.Collection("models").InsertReturning(m) 80 | // CheckErr(err, b) 81 | // }) 82 | 83 | // for i := 0; i < b.N; i++ { 84 | // err := upper.Collection("models").UpdateReturning(m) 85 | // CheckErr(err, b) 86 | // } 87 | // } 88 | 89 | // func UpperRead(b *B) { 90 | // var m *Model4 91 | // WrapExecute(b, func() { 92 | // InitDB() 93 | // m = NewModel4() 94 | 95 | // err := upper.Collection("models").InsertReturning(m) 96 | // CheckErr(err, b) 97 | // }) 98 | 99 | // for i := 0; i < b.N; i++ { 100 | // err := upper.SQL().SelectFrom("models").Where("id = ?", m.ID).One(m) 101 | // CheckErr(err, b) 102 | // } 103 | // } 104 | 105 | // func UpperReadSlice(b *B) { 106 | // var m *Model4 107 | // WrapExecute(b, func() { 108 | // InitDB() 109 | // m = NewModel4() 110 | 111 | // err := upper.Collection("models").InsertReturning(m) 112 | // CheckErr(err, b) 113 | // }) 114 | 115 | // for i := 0; i < b.N; i++ { 116 | // var ms []*Model4 117 | // err := upper.SQL().SelectFrom("models").Where("id > ?", m.ID).Limit(100).All(&ms) 118 | // CheckErr(err, b) 119 | // } 120 | // } 121 | -------------------------------------------------------------------------------- /benchs/bun.go: -------------------------------------------------------------------------------- 1 | package benchs 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/uptrace/bun" 7 | ) 8 | 9 | // import ( 10 | // "context" 11 | // "database/sql" 12 | 13 | // "github.com/uptrace/bun" 14 | // "github.com/uptrace/bun/dialect/pgdialect" 15 | // 16 | // ) 17 | 18 | var ( 19 | ctx = context.Background() 20 | bundb *bun.DB 21 | ) 22 | 23 | // func init() { 24 | // st := NewSuite("bun") 25 | // st.InitF = func() { 26 | // st.AddBenchmark("Insert", 200*OrmMulti, BunInsert) 27 | // st.AddBenchmark("MultiInsert 100 row", 200*OrmMulti, BunInsertMulti) 28 | // // st.AddBenchmark("Update", 200*OrmMulti, BunUpdate) 29 | // st.AddBenchmark("Read", 200*OrmMulti, BunRead) 30 | // st.AddBenchmark("MultiRead limit 100", 200*OrmMulti, BunReadSlice) 31 | 32 | // // sqldb := sql.OpenDB(pgdriver.NewConnector(pgdriver.WithDSN(ConvertSourceToDSN()))) 33 | // sqldb, err := sql.Open(sqlDriver, ConvertSourceToDSN()) 34 | // if err != nil { 35 | // panic(err) 36 | // } 37 | // sqldb.SetMaxOpenConns(OrmMaxConn) 38 | // sqldb.SetMaxIdleConns(OrmMaxIdle) 39 | 40 | // bundb = bun.NewDB(sqldb, mysqldialect.New()) 41 | // } 42 | // } 43 | 44 | // func BunInsert(b *B) { 45 | // var m *Model 46 | // WrapExecute(b, func() { 47 | // InitDB() 48 | // m = NewModel() 49 | // }) 50 | 51 | // for i := 0; i < b.N; i++ { 52 | // m.Id = 0 53 | // _, err := bundb.NewInsert().Model(m).Exec(ctx) 54 | // CheckErr(err, b) 55 | // } 56 | // } 57 | 58 | // func BunInsertMulti(b *B) { 59 | // var ms []*Model 60 | // WrapExecute(b, func() { 61 | // InitDB() 62 | // ms = make([]*Model, 0, 100) 63 | // for i := 0; i < 100; i++ { 64 | // ms = append(ms, NewModel()) 65 | // } 66 | // }) 67 | 68 | // for i := 0; i < b.N; i++ { 69 | // for _, m := range ms { 70 | // m.Id = 0 71 | // } 72 | 73 | // _, err := bundb.NewInsert().Model(&ms).Exec(ctx) 74 | // CheckErr(err, b) 75 | // } 76 | // } 77 | 78 | // func BunUpdate(b *B) { 79 | // var m *Model 80 | // WrapExecute(b, func() { 81 | // InitDB() 82 | // m = NewModel() 83 | 84 | // _, err := bundb.NewInsert().Model(m).Exec(ctx) 85 | // CheckErr(err, b) 86 | // }) 87 | 88 | // for i := 0; i < b.N; i++ { 89 | // _, err := bundb.NewUpdate().Model(m).WherePK().Exec(ctx) 90 | // CheckErr(err, b) 91 | // } 92 | // } 93 | 94 | // func BunRead(b *B) { 95 | // var m *Model 96 | // WrapExecute(b, func() { 97 | // InitDB() 98 | // m = NewModel() 99 | 100 | // _, err := bundb.NewInsert().Model(m).Exec(ctx) 101 | // CheckErr(err, b) 102 | // }) 103 | 104 | // for i := 0; i < b.N; i++ { 105 | // err := bundb.NewSelect().Model(m).Scan(ctx) 106 | // CheckErr(err, b) 107 | // } 108 | // } 109 | 110 | // func BunReadSlice(b *B) { 111 | // var m *Model 112 | // WrapExecute(b, func() { 113 | // InitDB() 114 | // m = NewModel() 115 | // for i := 0; i < 100; i++ { 116 | // m.Id = 0 117 | // _, err := bundb.NewInsert().Model(m).Exec(ctx) 118 | // CheckErr(err, b) 119 | // } 120 | // }) 121 | 122 | // for i := 0; i < b.N; i++ { 123 | // var models []*Model 124 | // err := bundb.NewSelect(). 125 | // Model(&models). 126 | // Where("id > ?", 0). 127 | // Limit(100). 128 | // Scan(ctx) 129 | // CheckErr(err, b) 130 | // } 131 | // } 132 | -------------------------------------------------------------------------------- /benchs/zorm.go: -------------------------------------------------------------------------------- 1 | package benchs 2 | 3 | // import ( 4 | // "context" 5 | 6 | // "gitee.com/chunanyong/zorm" 7 | // // _ "github.com/lib/pq" 8 | // ) 9 | 10 | // var ( 11 | // zormCtx = context.Background() 12 | // readFinder = zorm.NewFinder().Append("SELECT * FROM models WHERE id = 1") 13 | // readSliceFinder = zorm.NewFinder().Append("SELECT * FROM models WHERE id > 0") 14 | // page = &zorm.Page{PageNo: 1, PageSize: 100} 15 | // ) 16 | 17 | // func init() { 18 | // st := NewSuite("zorm") 19 | // readFinder.InjectionCheck = false 20 | // readSliceFinder.InjectionCheck = false 21 | // readSliceFinder.SelectTotalCount = false 22 | // st.InitF = func() { 23 | // st.AddBenchmark("Insert", 200*OrmMulti, ZormInsert) 24 | // st.AddBenchmark("MultiInsert 100 row", 200*OrmMulti, ZormInsertMulti) 25 | // // st.AddBenchmark("Update", 200*OrmMulti, ZormUpdate) 26 | // st.AddBenchmark("Read", 200*OrmMulti, ZormRead) 27 | // st.AddBenchmark("MultiRead limit 100", 200*OrmMulti, ZormReadSlice) 28 | 29 | // dbDaoConfig := zorm.DataSourceConfig{ 30 | // DSN: ConvertSourceToDSN(), 31 | // DriverName: sqlDriver, 32 | // Dialect: sqlDriver, 33 | // MaxOpenConns: OrmMaxConn, 34 | // MaxIdleConns: OrmMaxIdle, 35 | // SlowSQLMillis: -1, 36 | // DisableTransaction: true, 37 | // } 38 | // _, err := zorm.NewDBDao(&dbDaoConfig) 39 | // CheckErr(err) 40 | // } 41 | // } 42 | 43 | // func ZormInsert(b *B) { 44 | // var m *Model7 45 | // WrapExecute(b, func() { 46 | // InitDB() 47 | // m = NewModel7() 48 | // }) 49 | 50 | // for i := 0; i < b.N; i++ { 51 | // m.ID = 0 52 | // _, err := zorm.Insert(zormCtx, m) 53 | // CheckErr(err, b) 54 | // } 55 | // } 56 | 57 | // func ZormInsertMulti(b *B) { 58 | // var ms []zorm.IEntityStruct 59 | // WrapExecute(b, func() { 60 | // InitDB() 61 | // ms = make([]zorm.IEntityStruct, 0, 100) 62 | // for i := 0; i < 100; i++ { 63 | // ms = append(ms, NewModel7()) 64 | // } 65 | // }) 66 | 67 | // for i := 0; i < b.N; i++ { 68 | // for _, m := range ms { 69 | // m7, _ := m.(*Model7) 70 | // m7.ID = 0 71 | // } 72 | // _, err := zorm.InsertSlice(zormCtx, ms) 73 | // CheckErr(err, b) 74 | // } 75 | // } 76 | 77 | // func ZormUpdate(b *B) { 78 | // var m *Model7 79 | // WrapExecute(b, func() { 80 | // InitDB() 81 | // m = NewModel7() 82 | // _, err := zorm.Insert(zormCtx, m) 83 | // CheckErr(err, b) 84 | // }) 85 | 86 | // for i := 0; i < b.N; i++ { 87 | // _, err := zorm.Update(zormCtx, m) 88 | // CheckErr(err, b) 89 | // } 90 | // } 91 | 92 | // func ZormRead(b *B) { 93 | // var m *Model7 94 | // WrapExecute(b, func() { 95 | // InitDB() 96 | // m = NewModel7() 97 | // _, err := zorm.Insert(zormCtx, m) 98 | // CheckErr(err, b) 99 | // }) 100 | 101 | // for i := 0; i < b.N; i++ { 102 | // _, err := zorm.QueryRow(zormCtx, readFinder, m) 103 | // CheckErr(err, b) 104 | // } 105 | // } 106 | 107 | // func ZormReadSlice(b *B) { 108 | // var m *Model7 109 | // WrapExecute(b, func() { 110 | // InitDB() 111 | // m = NewModel7() 112 | // for i := 0; i < 100; i++ { 113 | // m.ID = 0 114 | // _, err := zorm.Insert(zormCtx, m) 115 | // CheckErr(err, b) 116 | // } 117 | // }) 118 | 119 | // for i := 0; i < b.N; i++ { 120 | // var models []Model7 121 | // err := zorm.Query(zormCtx, readSliceFinder, &models, page) 122 | // CheckErr(err, b) 123 | // } 124 | // } 125 | -------------------------------------------------------------------------------- /benchs/pgx.go: -------------------------------------------------------------------------------- 1 | package benchs 2 | 3 | // import ( 4 | // "github.com/jackc/pgx/v4" 5 | // ) 6 | 7 | // var pgxdb *pgx.Conn 8 | 9 | // func init() { 10 | // st := NewSuite("pgx") 11 | // st.InitF = func() { 12 | // st.AddBenchmark("Insert", 200*OrmMulti, PgxInsert) 13 | // st.AddBenchmark("MultiInsert 100 row", 200*OrmMulti, PgxInsertMulti) 14 | // st.AddBenchmark("Update", 200*OrmMulti, PgxUpdate) 15 | // st.AddBenchmark("Read", 200*OrmMulti, PgxRead) 16 | // st.AddBenchmark("MultiRead limit 100", 200*OrmMulti, PgxReadSlice) 17 | 18 | // db, err := pgx.Connect(ctx, OrmSource) 19 | // CheckErr(err) 20 | 21 | // pgxdb = db 22 | // } 23 | // } 24 | 25 | // func PgxInsert(b *B) { 26 | // var m *Model 27 | // WrapExecute(b, func() { 28 | // InitDB() 29 | // m = NewModel() 30 | // }) 31 | 32 | // for i := 0; i < b.N; i++ { 33 | // _, err := pgxdb.Exec(ctx, sqlxInsertSQL, m.Name, m.Title, m.Fax, m.Web, m.Age, m.Right, m.Counter) 34 | // CheckErr(err, b) 35 | // } 36 | // } 37 | 38 | // func pgxInsert(m *Model) error { 39 | // _, err := pgxdb.Exec(ctx, sqlxInsertSQL, m.Name, m.Title, m.Fax, m.Web, m.Age, m.Right, m.Counter) 40 | // CheckErr(err) 41 | 42 | // return nil 43 | // } 44 | 45 | // func PgxInsertMulti(b *B) { 46 | // var rows = make([][]interface{}, 0) 47 | // WrapExecute(b, func() { 48 | // InitDB() 49 | // m := NewModel() 50 | // for i := 0; i < 100; i++ { 51 | // rows = append(rows, []interface{}{m.Name, m.Title, m.Fax, m.Web, m.Age, m.Right, m.Counter}) 52 | // } 53 | // }) 54 | 55 | // for i := 0; i < b.N; i++ { 56 | // _, err := pgxdb.CopyFrom(ctx, pgx.Identifier{"models"}, columns, pgx.CopyFromRows(rows)) 57 | // CheckErr(err, b) 58 | // } 59 | // } 60 | 61 | // func PgxUpdate(b *B) { 62 | // var m *Model 63 | // WrapExecute(b, func() { 64 | // InitDB() 65 | // m = NewModel() 66 | // m.Id = 1 67 | // err := pgxInsert(m) 68 | // CheckErr(err, b) 69 | // }) 70 | 71 | // for i := 0; i < b.N; i++ { 72 | // _, err := pgxdb.Exec(ctx, sqlxUpdateSQL, m.Name, m.Title, m.Fax, m.Web, m.Age, m.Right, m.Counter, m.Id) 73 | // CheckErr(err, b) 74 | // } 75 | // } 76 | 77 | // func PgxRead(b *B) { 78 | // var m *Model 79 | // WrapExecute(b, func() { 80 | // InitDB() 81 | // m = NewModel() 82 | // err := pgxInsert(m) 83 | // CheckErr(err, b) 84 | // }) 85 | 86 | // for i := 0; i < b.N; i++ { 87 | // var m Model 88 | // err := pgxdb.QueryRow(ctx, sqlxSelectSQL, 1).Scan( 89 | // &m.Id, 90 | // &m.Name, 91 | // &m.Title, 92 | // &m.Fax, 93 | // &m.Web, 94 | // &m.Age, 95 | // &m.Right, 96 | // &m.Counter, 97 | // ) 98 | // CheckErr(err, b) 99 | // } 100 | // } 101 | 102 | // func PgxReadSlice(b *B) { 103 | // var m *Model 104 | // WrapExecute(b, func() { 105 | // var err error 106 | // InitDB() 107 | // m = NewModel() 108 | // for i := 0; i < 100; i++ { 109 | // err = pgxInsert(m) 110 | // CheckErr(err, b) 111 | // } 112 | // CheckErr(err, b) 113 | // }) 114 | 115 | // for i := 0; i < b.N; i++ { 116 | // ms := make([]Model, 100) 117 | // rows, err := pgxdb.Query(ctx, sqlxSelectMultiSQL) 118 | // CheckErr(err, b) 119 | 120 | // for j := 0; rows.Next() && j < len(ms); j++ { 121 | // err = rows.Scan( 122 | // &ms[j].Id, 123 | // &ms[j].Name, 124 | // &ms[j].Title, 125 | // &ms[j].Fax, 126 | // &ms[j].Web, 127 | // &ms[j].Age, 128 | // &ms[j].Right, 129 | // &ms[j].Counter, 130 | // ) 131 | // CheckErr(err, b) 132 | // } 133 | // err = rows.Err() 134 | // CheckErr(err, b) 135 | // rows.Close() 136 | // } 137 | // } 138 | -------------------------------------------------------------------------------- /benchs/sqlx.go: -------------------------------------------------------------------------------- 1 | package benchs 2 | 3 | import ( 4 | "github.com/jmoiron/sqlx" 5 | _ "github.com/lib/pq" 6 | ) 7 | 8 | var sqlxdb *sqlx.DB 9 | 10 | const ( 11 | // sqlxInsertBaseSQL = `INSERT INTO models (name, title, fax, web, age, "right", counter) VALUES ` 12 | sqlxInsertBaseSQL = "INSERT INTO models (name, title, fax, web, age, `right`, counter) VALUES " 13 | // sqlxInsertValuesSQL = `($1, $2, $3, $4, $5, $6, $7)` 14 | sqlxInsertValuesSQL = `(?, ?, ?, ?, ?, ?, ?)` 15 | sqlxInsertSQL = sqlxInsertBaseSQL + sqlxInsertValuesSQL 16 | sqlxInsertNamesSQL = `(:name, :title, :fax, :web, :age, :right, :counter)` 17 | sqlxInsertMultiSQL = sqlxInsertBaseSQL + sqlxInsertNamesSQL 18 | // sqlxUpdateSQL = `UPDATE models SET name = $1, title = $2, fax = $3, web = $4, age = $5, "right" = $6, counter = $7 WHERE id = $8` 19 | sqlxUpdateSQL = "UPDATE models SET name = ?, title = ?, fax = ?, web = ?, age = ?, `right` = ?, counter = ? WHERE id = ?" 20 | // sqlxSelectSQL = `SELECT * FROM models WHERE id = $1` 21 | sqlxSelectSQL = `SELECT * FROM models WHERE id = ?` 22 | sqlxSelectMultiSQL = `SELECT * FROM models WHERE id > 0 LIMIT 100` 23 | ) 24 | 25 | func init() { 26 | st := NewSuite("sqlx") 27 | st.InitF = func() { 28 | st.AddBenchmark("Insert", 200*OrmMulti, SqlxInsert) 29 | st.AddBenchmark("MultiInsert 100 row", 200*OrmMulti, SqlxInsertMulti) 30 | st.AddBenchmark("Update", 200*OrmMulti, SqlxUpdate) 31 | st.AddBenchmark("Read", 200*OrmMulti, SqlxRead) 32 | st.AddBenchmark("MultiRead limit 100", 200*OrmMulti, SqlxReadSlice) 33 | 34 | db, err := sqlx.Connect("mysql", ConvertSourceToDSN()) 35 | CheckErr(err) 36 | 37 | sqlxdb = db 38 | } 39 | } 40 | 41 | func SqlxInsert(b *B) { 42 | var m *Model 43 | WrapExecute(b, func() { 44 | InitDB() 45 | m = NewModel() 46 | }) 47 | 48 | for i := 0; i < b.N; i++ { 49 | _, err := sqlxdb.Exec(sqlxInsertSQL, m.Name, m.Title, m.Fax, m.Web, m.Age, m.Right, m.Counter) 50 | CheckErr(err, b) 51 | } 52 | } 53 | 54 | func sqlxInsert(m *Model) error { 55 | _, err := sqlxdb.Exec(sqlxInsertSQL, m.Name, m.Title, m.Fax, m.Web, m.Age, m.Right, m.Counter) 56 | CheckErr(err) 57 | 58 | return nil 59 | } 60 | 61 | func SqlxInsertMulti(b *B) { 62 | var ms []*Model 63 | WrapExecute(b, func() { 64 | InitDB() 65 | ms = make([]*Model, 0, 100) 66 | for i := 0; i < 100; i++ { 67 | ms = append(ms, NewModel()) 68 | } 69 | }) 70 | 71 | for i := 0; i < b.N; i++ { 72 | _, err := sqlxdb.NamedExec(sqlxInsertMultiSQL, ms) 73 | CheckErr(err, b) 74 | } 75 | } 76 | 77 | func SqlxUpdate(b *B) { 78 | var m *Model 79 | WrapExecute(b, func() { 80 | InitDB() 81 | m = NewModel() 82 | err := sqlxInsert(m) 83 | CheckErr(err, b) 84 | }) 85 | 86 | for i := 0; i < b.N; i++ { 87 | _, err := sqlxdb.Exec(sqlxUpdateSQL, m.Name, m.Title, m.Fax, m.Web, m.Age, m.Right, m.Counter, m.Id) 88 | CheckErr(err, b) 89 | } 90 | } 91 | 92 | func SqlxRead(b *B) { 93 | var m *Model 94 | WrapExecute(b, func() { 95 | InitDB() 96 | m = NewModel() 97 | err := sqlxInsert(m) 98 | CheckErr(err, b) 99 | }) 100 | 101 | for i := 0; i < b.N; i++ { 102 | m := []Model{} 103 | err := sqlxdb.Select(&m, sqlxSelectSQL, 1) 104 | CheckErr(err, b) 105 | } 106 | } 107 | 108 | func SqlxReadSlice(b *B) { 109 | var m *Model 110 | WrapExecute(b, func() { 111 | var err error 112 | InitDB() 113 | m = NewModel() 114 | for i := 0; i < 100; i++ { 115 | err = sqlxInsert(m) 116 | CheckErr(err, b) 117 | } 118 | CheckErr(err, b) 119 | }) 120 | 121 | for i := 0; i < b.N; i++ { 122 | ms := make([]Model, 100) 123 | err := sqlxdb.Select(&ms, sqlxSelectMultiSQL) 124 | CheckErr(err, b) 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /benchs/pgx_pool.go: -------------------------------------------------------------------------------- 1 | package benchs 2 | 3 | // import ( 4 | // "github.com/jackc/pgx/v4" 5 | // "github.com/jackc/pgx/v4/pgxpool" 6 | // ) 7 | 8 | // var pgxpdb *pgxpool.Pool 9 | 10 | // func init() { 11 | // st := NewSuite("pgx_pool") 12 | // st.InitF = func() { 13 | // st.AddBenchmark("Insert", 200*OrmMulti, PgxPoolInsert) 14 | // st.AddBenchmark("MultiInsert 100 row", 200*OrmMulti, PgxPoolInsertMulti) 15 | // st.AddBenchmark("Update", 200*OrmMulti, PgxPoolUpdate) 16 | // st.AddBenchmark("Read", 200*OrmMulti, PgxPoolRead) 17 | // st.AddBenchmark("MultiRead limit 100", 200*OrmMulti, PgxPoolReadSlice) 18 | 19 | // db, err := pgxpool.Connect(ctx, OrmSource) 20 | // CheckErr(err) 21 | 22 | // pgxpdb = db 23 | // } 24 | // } 25 | 26 | // func PgxPoolInsert(b *B) { 27 | // var m *Model 28 | // WrapExecute(b, func() { 29 | // InitDB() 30 | // m = NewModel() 31 | // }) 32 | 33 | // for i := 0; i < b.N; i++ { 34 | // _, err := pgxpdb.Exec(ctx, sqlxInsertSQL, m.Name, m.Title, m.Fax, m.Web, m.Age, m.Right, m.Counter) 35 | // CheckErr(err, b) 36 | // } 37 | // } 38 | 39 | // func pgxPoolInsert(m *Model) error { 40 | // _, err := pgxpdb.Exec(ctx, sqlxInsertSQL, m.Name, m.Title, m.Fax, m.Web, m.Age, m.Right, m.Counter) 41 | // CheckErr(err) 42 | 43 | // return nil 44 | // } 45 | 46 | // func PgxPoolInsertMulti(b *B) { 47 | // var rows = make([][]interface{}, 0) 48 | // WrapExecute(b, func() { 49 | // InitDB() 50 | // m := NewModel() 51 | // for i := 0; i < 100; i++ { 52 | // rows = append(rows, []interface{}{m.Name, m.Title, m.Fax, m.Web, m.Age, m.Right, m.Counter}) 53 | // } 54 | // }) 55 | 56 | // for i := 0; i < b.N; i++ { 57 | // _, err := pgxpdb.CopyFrom(ctx, pgx.Identifier{"models"}, columns, pgx.CopyFromRows(rows)) 58 | // CheckErr(err, b) 59 | // } 60 | // } 61 | 62 | // func PgxPoolUpdate(b *B) { 63 | // var m *Model 64 | // WrapExecute(b, func() { 65 | // InitDB() 66 | // m = NewModel() 67 | // m.Id = 1 68 | // err := pgxPoolInsert(m) 69 | // CheckErr(err, b) 70 | // }) 71 | 72 | // for i := 0; i < b.N; i++ { 73 | // _, err := pgxpdb.Exec(ctx, sqlxUpdateSQL, m.Name, m.Title, m.Fax, m.Web, m.Age, m.Right, m.Counter, m.Id) 74 | // CheckErr(err, b) 75 | // } 76 | // } 77 | 78 | // func PgxPoolRead(b *B) { 79 | // var m *Model 80 | // WrapExecute(b, func() { 81 | // InitDB() 82 | // m = NewModel() 83 | // err := pgxPoolInsert(m) 84 | // CheckErr(err, b) 85 | // }) 86 | 87 | // for i := 0; i < b.N; i++ { 88 | // var m Model 89 | // err := pgxpdb.QueryRow(ctx, sqlxSelectSQL, 1).Scan( 90 | // &m.Id, 91 | // &m.Name, 92 | // &m.Title, 93 | // &m.Fax, 94 | // &m.Web, 95 | // &m.Age, 96 | // &m.Right, 97 | // &m.Counter, 98 | // ) 99 | // CheckErr(err, b) 100 | // } 101 | // } 102 | 103 | // func PgxPoolReadSlice(b *B) { 104 | // var m *Model 105 | // WrapExecute(b, func() { 106 | // var err error 107 | // InitDB() 108 | // m = NewModel() 109 | // for i := 0; i < 100; i++ { 110 | // err = pgxPoolInsert(m) 111 | // CheckErr(err, b) 112 | // } 113 | // CheckErr(err, b) 114 | // }) 115 | 116 | // for i := 0; i < b.N; i++ { 117 | // ms := make([]Model, 100) 118 | // rows, err := pgxpdb.Query(ctx, sqlxSelectMultiSQL) 119 | // CheckErr(err, b) 120 | 121 | // for j := 0; rows.Next() && j < len(ms); j++ { 122 | // err = rows.Scan( 123 | // &ms[j].Id, 124 | // &ms[j].Name, 125 | // &ms[j].Title, 126 | // &ms[j].Fax, 127 | // &ms[j].Web, 128 | // &ms[j].Age, 129 | // &ms[j].Right, 130 | // &ms[j].Counter, 131 | // ) 132 | // CheckErr(err, b) 133 | // } 134 | // err = rows.Err() 135 | // CheckErr(err, b) 136 | // rows.Close() 137 | // } 138 | // } 139 | -------------------------------------------------------------------------------- /benchs/sqlgen.go: -------------------------------------------------------------------------------- 1 | package benchs 2 | 3 | import ( 4 | "database/sql" 5 | 6 | "github.com/efectn/go-orm-benchmarks/db" 7 | ) 8 | 9 | var sqldb *sql.DB 10 | 11 | func init() { 12 | st := NewSuite("sqlgen") 13 | st.InitF = func() { 14 | st.AddBenchmark("Insert", 200*OrmMulti, SqlgenInsert) 15 | st.AddBenchmark("MultiInsert 100 row", 200*OrmMulti, SqlgenInsertMulti) 16 | // st.AddBenchmark("MultiInsert 100 row2", 200*OrmMulti, SqlgenInsertMulti2) 17 | st.AddBenchmark("Update", 200*OrmMulti, SqlgenUpdate) 18 | st.AddBenchmark("Read", 200*OrmMulti, SqlgenRead) 19 | st.AddBenchmark("MultiRead limit 100", 200*OrmMulti, SqlgenReadSlice) 20 | 21 | db, err := sql.Open(sqlDriver, "root:abcd1234@/go-orm?parseTime=true") 22 | CheckErr(err) 23 | 24 | sqldb = db 25 | } 26 | } 27 | 28 | func SqlgenInsert(b *B) { 29 | var m *Model 30 | WrapExecute(b, func() { 31 | InitDB() 32 | m = NewModel() 33 | }) 34 | 35 | for i := 0; i < b.N; i++ { 36 | _, err := db.InsertOne(ctx, sqldb, m) 37 | CheckErr(err, b) 38 | 39 | m = &Model{ 40 | Id: int(m.Id), 41 | Name: m.Name, 42 | Title: m.Title, 43 | Fax: m.Fax, 44 | Web: m.Web, 45 | Age: int(m.Age), 46 | Right: m.Right, 47 | Counter: m.Counter, 48 | } 49 | } 50 | } 51 | 52 | func SqlgenInsertMulti(b *B) { 53 | var ms []Model 54 | WrapExecute(b, func() { 55 | InitDB() 56 | ms = make([]Model, 0, 100) 57 | for i := 0; i < 100; i++ { 58 | ms = append(ms, *NewModel()) 59 | } 60 | }) 61 | 62 | for i := 0; i < b.N; i++ { 63 | for _, m := range ms { 64 | m.Id = 0 65 | } 66 | _, err := db.Insert(ctx, sqldb, ms) 67 | CheckErr(err, b) 68 | } 69 | } 70 | 71 | // func SqlgenInsertMulti(b *B) { 72 | // var ms []Model 73 | // WrapExecute(b, func() { 74 | // sqlutil.SetDialect("postgres") 75 | // InitDB() 76 | // ms = make([]Model, 0, 100) 77 | // for i := 0; i < 100; i++ { 78 | // ms = append(ms, *NewModel()) 79 | // } 80 | // }) 81 | 82 | // for i := 0; i < b.N; i++ { 83 | // _, err := sqlutil.InsertInto(ctx, sqldb, ms) 84 | // CheckErr(err, b) 85 | // } 86 | // } 87 | 88 | // func SqlgenInsertMulti2(b *B) { 89 | // var ms []Model 90 | // WrapExecute(b, func() { 91 | // sqlutil.SetDialect("postgres") 92 | // InitDB() 93 | // ms = make([]Model, 0, 100) 94 | // for i := 0; i < 100; i++ { 95 | // ms = append(ms, *NewModel()) 96 | // } 97 | // }) 98 | 99 | // for i := 0; i < b.N; i++ { 100 | // _, err := sqlutil.InsertInto(ctx, sqldb, ms) 101 | // CheckErr(err, b) 102 | // } 103 | // } 104 | 105 | func SqlgenUpdate(b *B) { 106 | var m *Model 107 | WrapExecute(b, func() { 108 | InitDB() 109 | m = NewModel() 110 | err := sqlgenInsert(m) 111 | CheckErr(err, b) 112 | }) 113 | 114 | for i := 0; i < b.N; i++ { 115 | _, err := db.UpdateByPK(ctx, sqldb, &Model{ 116 | Name: m.Name, 117 | Title: m.Title, 118 | Fax: m.Fax, 119 | Web: m.Web, 120 | Age: int(m.Age), 121 | Right: m.Right, 122 | Counter: m.Counter, 123 | Id: m.Id, 124 | }) 125 | CheckErr(err, b) 126 | } 127 | } 128 | 129 | func SqlgenRead(b *B) { 130 | var m *Model 131 | WrapExecute(b, func() { 132 | InitDB() 133 | m = NewModel() 134 | err := sqlgenInsert(m) 135 | CheckErr(err, b) 136 | }) 137 | 138 | m.Id = 1 139 | for i := 0; i < b.N; i++ { 140 | err := db.FindByPK(ctx, sqldb, m) 141 | CheckErr(err, b) 142 | } 143 | } 144 | 145 | func SqlgenReadSlice(b *B) { 146 | var m *Model 147 | WrapExecute(b, func() { 148 | var err error 149 | InitDB() 150 | m = NewModel() 151 | for i := 0; i < 100; i++ { 152 | err = sqlgenInsert(m) 153 | CheckErr(err, b) 154 | } 155 | CheckErr(err, b) 156 | }) 157 | 158 | for i := 0; i < b.N; i++ { 159 | models := make([]Model, 100) 160 | var m Model 161 | rows, err := sqldb.QueryContext(ctx, sqlxSelectMultiSQL) 162 | CheckErr(err, b) 163 | for j := 0; rows.Next() && j < len(models); j++ { 164 | err = rows.Scan(m.Addrs()...) 165 | CheckErr(err, b) 166 | models[j] = m 167 | } 168 | rows.Close() 169 | } 170 | } 171 | 172 | func sqlgenInsert(m *Model) error { 173 | _, err := db.InsertOne(ctx, sqldb, m) 174 | return err 175 | // return nil 176 | } 177 | -------------------------------------------------------------------------------- /benchs/ent.go: -------------------------------------------------------------------------------- 1 | package benchs 2 | 3 | import ( 4 | "database/sql" 5 | 6 | "entgo.io/ent/dialect" 7 | entsql "entgo.io/ent/dialect/sql" 8 | "github.com/efectn/go-orm-benchmarks/benchs/ent" 9 | "github.com/efectn/go-orm-benchmarks/benchs/ent/model" 10 | ) 11 | 12 | var client *ent.Client 13 | var dbEnt *sql.DB 14 | 15 | func initDBEnt() { 16 | // Run the auto migration. 17 | _, err := dbEnt.Exec("DROP TABLE IF EXISTS models") 18 | CheckErr(err) 19 | 20 | err = client.Schema.Create(ctx) 21 | CheckErr(err) 22 | } 23 | 24 | func init() { 25 | st := NewSuite("ent") 26 | st.InitF = func() { 27 | st.AddBenchmark("Insert", 200*OrmMulti, EntInsert) 28 | st.AddBenchmark("MultiInsert 100 row", 200*OrmMulti, EntInsertMulti) 29 | st.AddBenchmark("Update", 200*OrmMulti, EntUpdate) 30 | st.AddBenchmark("Read", 200*OrmMulti, EntRead) 31 | st.AddBenchmark("MultiRead limit 100", 200*OrmMulti, EntReadSlice) 32 | 33 | var err error 34 | dbEnt, err = sql.Open("mysql", ConvertSourceToDSN()) 35 | CheckErr(err) 36 | 37 | // Create an ent.Driver from `dbEnt`. 38 | drv := entsql.OpenDB(dialect.MySQL, dbEnt) 39 | 40 | // Assign to client 41 | client = ent.NewClient(ent.Driver(drv)) 42 | 43 | } 44 | } 45 | 46 | func EntInsert(b *B) { 47 | var m Model 48 | WrapExecute(b, func() { 49 | initDBEnt() 50 | m = NewModelAlt() 51 | }) 52 | 53 | for i := 0; i < b.N; i++ { 54 | _, err := client.Model.Create(). 55 | SetName(m.Name). 56 | SetTitle(m.Title). 57 | SetFax(m.Fax). 58 | SetWeb(m.Web). 59 | SetAge(m.Age). 60 | SetRight(m.Right). 61 | SetCounter(m.Counter). 62 | Save(ctx) 63 | 64 | CheckErr(err) 65 | } 66 | } 67 | 68 | func EntInsertMulti(b *B) { 69 | var ms []Model 70 | WrapExecute(b, func() { 71 | initDBEnt() 72 | ms = make([]Model, 0, 100) 73 | for i := 0; i < 100; i++ { 74 | ms = append(ms, NewModelAlt()) 75 | } 76 | }) 77 | 78 | bulk := make([]*ent.ModelCreate, len(ms)) 79 | for i, m := range ms { 80 | bulk[i] = client.Model.Create(). 81 | SetName(m.Name). 82 | SetTitle(m.Title). 83 | SetFax(m.Fax). 84 | SetWeb(m.Web). 85 | SetAge(m.Age). 86 | SetRight(m.Right). 87 | SetCounter(m.Counter) 88 | } 89 | 90 | for i := 0; i < b.N; i++ { 91 | _, err := client.Model.CreateBulk(bulk...).Save(ctx) 92 | CheckErr(err) 93 | 94 | } 95 | } 96 | 97 | func EntUpdate(b *B) { 98 | var m Model 99 | WrapExecute(b, func() { 100 | initDBEnt() 101 | m = NewModelAlt() 102 | _, err := client.Model.Create(). 103 | SetName(m.Name). 104 | SetTitle(m.Title). 105 | SetFax(m.Fax). 106 | SetWeb(m.Web). 107 | SetAge(m.Age). 108 | SetRight(m.Right). 109 | SetCounter(m.Counter). 110 | Save(ctx) 111 | 112 | CheckErr(err) 113 | }) 114 | 115 | for i := 0; i < b.N; i++ { 116 | _, err := client.Model.Update(). 117 | Where(model.IDEQ(m.Id)). 118 | SetName(m.Name). 119 | SetTitle(m.Title). 120 | SetFax(m.Fax). 121 | SetWeb(m.Web). 122 | SetAge(m.Age). 123 | SetRight(m.Right). 124 | SetCounter(m.Counter). 125 | Save(ctx) 126 | 127 | CheckErr(err) 128 | } 129 | } 130 | 131 | func EntRead(b *B) { 132 | var m Model 133 | WrapExecute(b, func() { 134 | initDBEnt() 135 | m = NewModelAlt() 136 | _, err := client.Model.Create(). 137 | SetName(m.Name). 138 | SetTitle(m.Title). 139 | SetFax(m.Fax). 140 | SetWeb(m.Web). 141 | SetAge(m.Age). 142 | SetRight(m.Right). 143 | SetCounter(m.Counter). 144 | Save(ctx) 145 | 146 | CheckErr(err) 147 | }) 148 | 149 | for i := 0; i < b.N; i++ { 150 | _, err := client.Model.Query().Where(model.IDEQ(1)).First(ctx) 151 | CheckErr(err) 152 | } 153 | } 154 | 155 | func EntReadSlice(b *B) { 156 | var m Model 157 | WrapExecute(b, func() { 158 | initDBEnt() 159 | m = NewModelAlt() 160 | for i := 0; i < 100; i++ { 161 | _, err := client.Model.Create(). 162 | SetName(m.Name). 163 | SetTitle(m.Title). 164 | SetFax(m.Fax). 165 | SetWeb(m.Web). 166 | SetAge(m.Age). 167 | SetRight(m.Right). 168 | SetCounter(m.Counter). 169 | Save(ctx) 170 | 171 | CheckErr(err) 172 | } 173 | }) 174 | 175 | for i := 0; i < b.N; i++ { 176 | _, err := client.Model.Query().Where(model.IDGT(0)).Unique(false).Limit(100).All(ctx) 177 | 178 | CheckErr(err) 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /benchs/sqlc/db/queries.sql.go: -------------------------------------------------------------------------------- 1 | // Code generated by sqlc. DO NOT EDIT. 2 | // versions: 3 | // sqlc v1.13.0 4 | // source: queries.sql 5 | package db 6 | 7 | import ( 8 | "context" 9 | ) 10 | 11 | // const createModel = `-- name: CreateModel :one 12 | // INSERT INTO models (NAME, title, fax, web, age, "right", counter) 13 | // VALUES ($1, $2, $3, $4, $5, $6, $7) 14 | // RETURNING id, name, title, fax, web, age, "right", counter 15 | // ` 16 | const createModel = "INSERT INTO models (NAME, title, fax, web, age, `right`, counter) VALUES (?, ?, ?, ?, ?, ?, ?)" 17 | 18 | type CreateModelParams struct { 19 | Name string 20 | Title string 21 | Fax string 22 | Web string 23 | Age int32 24 | Right bool 25 | Counter int64 26 | } 27 | 28 | // func (q *Queries) CreateModel(ctx context.Context, arg CreateModelParams) (Model, error) { 29 | // row := q.db.QueryRowContext(ctx, createModel, 30 | // arg.Name, 31 | // arg.Title, 32 | // arg.Fax, 33 | // arg.Web, 34 | // arg.Age, 35 | // arg.Right, 36 | // arg.Counter, 37 | // ) 38 | // var i Model 39 | // err := row.Scan( 40 | // &i.ID, 41 | // &i.Name, 42 | // &i.Title, 43 | // &i.Fax, 44 | // &i.Web, 45 | // &i.Age, 46 | // &i.Right, 47 | // &i.Counter, 48 | // ) 49 | // return i, err 50 | // } 51 | 52 | func (q *Queries) CreateModel(ctx context.Context, arg CreateModelParams) (Model, error) { 53 | result, err := q.db.ExecContext(ctx, createModel, 54 | arg.Name, 55 | arg.Title, 56 | arg.Fax, 57 | arg.Web, 58 | arg.Age, 59 | arg.Right, 60 | arg.Counter, 61 | ) 62 | if err != nil { 63 | return Model{}, err 64 | } 65 | i64, _ := result.LastInsertId() 66 | var i Model 67 | i.ID = int32(i64) 68 | i.Name = arg.Name 69 | i.Title = arg.Title 70 | i.Fax = arg.Fax 71 | i.Web = arg.Web 72 | i.Age = arg.Age 73 | i.Right = arg.Right 74 | i.Counter = arg.Counter 75 | return i, err 76 | } 77 | 78 | // const getModel = `-- name: GetModel :one 79 | // SELECT id, name, title, fax, web, age, "right", counter 80 | // FROM models 81 | // WHERE id = $1 82 | // ` 83 | const getModel = "SELECT id, name, title, fax, web, age, `right`, counter FROM models WHERE id = ?" 84 | 85 | func (q *Queries) GetModel(ctx context.Context, id int32) (Model, error) { 86 | row := q.db.QueryRowContext(ctx, getModel, id) 87 | var i Model 88 | err := row.Scan( 89 | &i.ID, 90 | &i.Name, 91 | &i.Title, 92 | &i.Fax, 93 | &i.Web, 94 | &i.Age, 95 | &i.Right, 96 | &i.Counter, 97 | ) 98 | return i, err 99 | } 100 | 101 | // const listModels = `-- name: ListModels :many 102 | // SELECT id, name, title, fax, web, age, "right", counter 103 | // FROM models 104 | // WHERE ID > $1 105 | // ORDER BY ID 106 | // LIMIT $2 107 | // ` 108 | const listModels = "SELECT id, name, title, fax, web, age, `right`, counter FROM models WHERE ID > ? ORDER BY ID LIMIT ?" 109 | 110 | type ListModelsParams struct { 111 | ID int32 112 | Limit int32 113 | } 114 | 115 | func (q *Queries) ListModels(ctx context.Context, arg ListModelsParams) ([]Model, error) { 116 | rows, err := q.db.QueryContext(ctx, listModels, arg.ID, arg.Limit) 117 | if err != nil { 118 | return nil, err 119 | } 120 | defer rows.Close() 121 | var items []Model 122 | for rows.Next() { 123 | var i Model 124 | if err := rows.Scan( 125 | &i.ID, 126 | &i.Name, 127 | &i.Title, 128 | &i.Fax, 129 | &i.Web, 130 | &i.Age, 131 | &i.Right, 132 | &i.Counter, 133 | ); err != nil { 134 | return nil, err 135 | } 136 | items = append(items, i) 137 | } 138 | if err := rows.Close(); err != nil { 139 | return nil, err 140 | } 141 | if err := rows.Err(); err != nil { 142 | return nil, err 143 | } 144 | return items, nil 145 | } 146 | 147 | // const updateModel = `-- name: UpdateModel :exec 148 | // UPDATE models 149 | // SET name = $1, 150 | // title = $2, 151 | // fax = $3, 152 | // web = $4, 153 | // age = $5, 154 | // "right" = $6, 155 | // counter = $7 156 | // WHERE id = $8 157 | // ` 158 | const updateModel = `-- name: UpdateModel :exec 159 | UPDATE models 160 | SET name = ?, 161 | title = ?, 162 | fax = ?, 163 | web = ?, 164 | age = ?, 165 | `+"`right`"+` = ?, 166 | counter = ? 167 | WHERE id = ? 168 | ` 169 | 170 | type UpdateModelParams struct { 171 | Name string 172 | Title string 173 | Fax string 174 | Web string 175 | Age int32 176 | Right bool 177 | Counter int64 178 | ID int32 179 | } 180 | 181 | func (q *Queries) UpdateModel(ctx context.Context, arg UpdateModelParams) error { 182 | _, err := q.db.ExecContext(ctx, updateModel, 183 | arg.Name, 184 | arg.Title, 185 | arg.Fax, 186 | arg.Web, 187 | arg.Age, 188 | arg.Right, 189 | arg.Counter, 190 | arg.ID, 191 | ) 192 | return err 193 | } 194 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/efectn/go-orm-benchmarks 2 | 3 | go 1.23 4 | 5 | toolchain go1.23.0 6 | 7 | require ( 8 | entgo.io/ent v0.12.4 9 | gitee.com/chunanyong/zorm v1.6.7 10 | github.com/friendsofgo/errors v0.9.2 11 | github.com/go-rel/mysql v0.11.0 12 | github.com/go-rel/rel v0.41.0 13 | github.com/go-sql-driver/mysql v1.8.1 14 | github.com/gobuffalo/pop/v6 v6.1.1 15 | github.com/jackc/pgx/v4 v4.18.1 16 | github.com/jmoiron/sqlx v1.3.5 17 | github.com/lib/pq v1.10.9 18 | github.com/samonzeweb/godb v1.0.15 19 | github.com/si3nloong/sqlgen v1.0.0-alpha.1.0.20231113031311-e30b0ab0301f 20 | github.com/uptrace/bun v1.1.16 21 | github.com/volatiletech/sqlboiler/v4 v4.14.1 22 | github.com/volatiletech/strmangle v0.0.4 23 | gopkg.in/reform.v1 v1.5.1 24 | gorm.io/driver/mysql v1.5.2 25 | gorm.io/gorm v1.25.5 26 | xorm.io/xorm v1.3.9 27 | ) 28 | 29 | replace github.com/si3nloong/sqlgen => /Users/sianloong/Developers/si3nloong/sqlgen 30 | 31 | require ( 32 | ariga.io/atlas v0.14.1-0.20230918065911-83ad451a4935 // indirect 33 | filippo.io/edwards25519 v1.1.0 // indirect 34 | github.com/Masterminds/semver/v3 v3.3.1 // indirect 35 | github.com/agext/levenshtein v1.2.1 // indirect 36 | github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect 37 | github.com/aymerick/douceur v0.2.0 // indirect 38 | github.com/denisenkom/go-mssqldb v0.12.3 // indirect 39 | github.com/fatih/color v1.13.0 // indirect 40 | github.com/fatih/structs v1.1.0 // indirect 41 | github.com/go-openapi/inflect v0.19.0 // indirect 42 | github.com/go-rel/sql v0.16.0 // indirect 43 | github.com/gobuffalo/envy v1.10.2 // indirect 44 | github.com/gobuffalo/fizz v1.14.4 // indirect 45 | github.com/gobuffalo/flect v1.0.0 // indirect 46 | github.com/gobuffalo/github_flavored_markdown v1.1.3 // indirect 47 | github.com/gobuffalo/helpers v0.6.7 // indirect 48 | github.com/gobuffalo/nulls v0.4.2 // indirect 49 | github.com/gobuffalo/plush/v4 v4.1.18 // indirect 50 | github.com/gobuffalo/tags/v3 v3.1.4 // indirect 51 | github.com/gobuffalo/validate/v3 v3.3.3 // indirect 52 | github.com/goccy/go-json v0.8.1 // indirect 53 | github.com/gofrs/uuid v4.3.1+incompatible // indirect 54 | github.com/golang/snappy v0.0.4 // indirect 55 | github.com/google/go-cmp v0.5.9 // indirect 56 | github.com/google/uuid v1.3.1 // indirect 57 | github.com/gorilla/css v1.0.0 // indirect 58 | github.com/hashicorp/hcl/v2 v2.13.0 // indirect 59 | github.com/jackc/chunkreader/v2 v2.0.1 // indirect 60 | github.com/jackc/pgconn v1.14.0 // indirect 61 | github.com/jackc/pgio v1.0.0 // indirect 62 | github.com/jackc/pgpassfile v1.0.0 // indirect 63 | github.com/jackc/pgproto3/v2 v2.3.2 // indirect 64 | github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect 65 | github.com/jackc/pgtype v1.14.0 // indirect 66 | github.com/jinzhu/inflection v1.0.0 // indirect 67 | github.com/jinzhu/now v1.1.5 // indirect 68 | github.com/joho/godotenv v1.4.0 // indirect 69 | github.com/json-iterator/go v1.1.12 // indirect 70 | github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect 71 | github.com/luna-duclos/instrumentedsql v1.1.3 // indirect 72 | github.com/mattn/go-colorable v0.1.13 // indirect 73 | github.com/mattn/go-isatty v0.0.20 // indirect 74 | github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect 75 | github.com/microcosm-cc/bluemonday v1.0.20 // indirect 76 | github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 // indirect 77 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 78 | github.com/modern-go/reflect2 v1.0.2 // indirect 79 | github.com/rogpeppe/go-internal v1.11.0 // indirect 80 | github.com/serenize/snaker v0.0.0-20201027110005-a7ad2135616e // indirect 81 | github.com/sergi/go-diff v1.2.0 // indirect 82 | github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d // indirect 83 | github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e // indirect 84 | github.com/spf13/cast v1.5.0 // indirect 85 | github.com/syndtr/goleveldb v1.0.0 // indirect 86 | github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect 87 | github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect 88 | github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect 89 | github.com/volatiletech/inflect v0.0.1 // indirect 90 | github.com/zclconf/go-cty v1.8.0 // indirect 91 | golang.org/x/crypto v0.29.0 // indirect 92 | golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 // indirect 93 | golang.org/x/mod v0.22.0 // indirect 94 | golang.org/x/net v0.31.0 // indirect 95 | golang.org/x/sync v0.9.0 // indirect 96 | golang.org/x/sys v0.27.0 // indirect 97 | golang.org/x/text v0.20.0 // indirect 98 | golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect 99 | gopkg.in/yaml.v2 v2.4.0 // indirect 100 | xorm.io/builder v0.3.11-0.20220531020008-1bd24a7dc978 // indirect 101 | ) 102 | -------------------------------------------------------------------------------- /benchs/models.go: -------------------------------------------------------------------------------- 1 | package benchs 2 | 3 | import ( 4 | "gitee.com/chunanyong/zorm" 5 | models "github.com/efectn/go-orm-benchmarks/benchs/sqlboiler" 6 | ) 7 | 8 | // Model for GORM, GORP, Beego, Bun, Pg, Raw, Sqlc, Ent 9 | type Model struct { 10 | Id int `orm:"auto" gorm:"primary_key" db:"id" bun:",pk,autoincrement" sql:",pk,auto_increment"` 11 | Name string 12 | Title string 13 | Fax string 14 | Web string 15 | Age int 16 | Right bool 17 | Counter int64 18 | } 19 | 20 | func (m Model) Table() string { 21 | return "models" 22 | } 23 | 24 | func NewModel() *Model { 25 | m := new(Model) 26 | m.Name = "Orm Benchmark" 27 | m.Title = "Just a Benchmark for fun" 28 | m.Fax = "99909990" 29 | m.Web = "http://blog.milkpod29.me" 30 | m.Age = 100 31 | m.Right = true 32 | m.Counter = 1000 33 | 34 | return m 35 | } 36 | 37 | func NewModelAlt() Model { 38 | m := Model{} 39 | m.Name = "Orm Benchmark" 40 | m.Title = "Just a Benchmark for fun" 41 | m.Fax = "99909990" 42 | m.Web = "http://blog.milkpod29.me" 43 | m.Age = 100 44 | m.Right = true 45 | m.Counter = 1000 46 | 47 | return m 48 | } 49 | 50 | // Model for Godb, Dbr 51 | type Model2 struct { 52 | ID int `db:"id,key,auto"` 53 | Name string `db:"name"` 54 | Title string `db:"title"` 55 | Fax string `db:"fax"` 56 | Web string `db:"web"` 57 | Age int `db:"age"` 58 | Right bool `db:"right"` 59 | Counter int64 `db:"counter"` 60 | } 61 | 62 | func (Model2) TableName() string { 63 | return "models" 64 | } 65 | 66 | func NewModel2() *Model2 { 67 | m := new(Model2) 68 | m.Name = "Orm Benchmark" 69 | m.Title = "Just a Benchmark for fun" 70 | m.Fax = "99909990" 71 | m.Web = "http://blog.milkpod29.me" 72 | m.Age = 100 73 | m.Right = true 74 | m.Counter = 1000 75 | 76 | return m 77 | } 78 | 79 | // Model for Pop, Rel 80 | type Model3 struct { 81 | ID int `db:"id"` 82 | Name string `db:"name"` 83 | Title string `db:"title"` 84 | Fax string `db:"fax"` 85 | Web string `db:"web"` 86 | Age int `db:"age"` 87 | Right bool `db:"right"` 88 | Counter int64 `db:"counter"` 89 | } 90 | 91 | func (Model3) Table() string { 92 | return "models" 93 | } 94 | 95 | func (Model3) TableName() string { 96 | return "models" 97 | } 98 | 99 | func NewModel3() *Model3 { 100 | m := new(Model3) 101 | m.Name = "Orm Benchmark" 102 | m.Title = "Just a Benchmark for fun" 103 | m.Fax = "99909990" 104 | m.Web = "http://blog.milkpod29.me" 105 | m.Age = 100 106 | m.Right = true 107 | m.Counter = 1000 108 | 109 | return m 110 | } 111 | 112 | // Model for Upper 113 | type Model4 struct { 114 | ID int `db:"id,omitempty"` 115 | Name string `db:"name"` 116 | Title string `db:"title"` 117 | Fax string `db:"fax"` 118 | Web string `db:"web"` 119 | Age int `db:"age"` 120 | Right bool `db:"right"` 121 | Counter int64 `db:"counter"` 122 | } 123 | 124 | func NewModel4() *Model4 { 125 | m := new(Model4) 126 | m.Name = "Orm Benchmark" 127 | m.Title = "Just a Benchmark for fun" 128 | m.Fax = "99909990" 129 | m.Web = "http://blog.milkpod29.me" 130 | m.Age = 100 131 | m.Right = true 132 | m.Counter = 1000 133 | 134 | return m 135 | } 136 | 137 | // Model for XORM 138 | type Model5 struct { 139 | Id int64 `xorm:"pk"` 140 | Name string 141 | Title string 142 | Fax string 143 | Web string 144 | Age int 145 | Right bool 146 | Counter int64 147 | } 148 | 149 | func NewModel5() *Model5 { 150 | m := new(Model5) 151 | m.Name = "Orm Benchmark" 152 | m.Title = "Just a Benchmark for fun" 153 | m.Fax = "99909990" 154 | m.Web = "http://blog.milkpod29.me" 155 | m.Age = 100 156 | m.Right = true 157 | m.Counter = 1000 158 | return m 159 | } 160 | 161 | // Model for Sqlboiler 162 | func NewModel6() *models.Model { 163 | m := new(models.Model) 164 | m.Name = "Orm Benchmark" 165 | m.Title = "Just a Benchmark for fun" 166 | m.Fax = "99909990" 167 | m.Web = "http://blog.milkpod29.me" 168 | m.Age = 100 169 | m.Right = true 170 | m.Counter = 1000 171 | 172 | return m 173 | } 174 | 175 | // Model for zorm 176 | type Model7 struct { 177 | zorm.EntityStruct 178 | ID int `column:"id"` 179 | Name string `column:"name"` 180 | Title string `column:"title"` 181 | Fax string `column:"fax"` 182 | Web string `column:"web"` 183 | Age int `column:"age"` 184 | Right bool `column:"\"right\""` 185 | Counter int64 `column:"counter"` 186 | } 187 | 188 | func (entity *Model7) GetTableName() string { 189 | return "models" 190 | } 191 | 192 | func (entity *Model7) GetPKColumnName() string { 193 | return "id" 194 | } 195 | 196 | func NewModel7() *Model7 { 197 | m := new(Model7) 198 | m.Name = "Orm Benchmark" 199 | m.Title = "Just a Benchmark for fun" 200 | m.Fax = "99909990" 201 | m.Web = "http://blog.milkpod29.me" 202 | m.Age = 100 203 | m.Right = true 204 | m.Counter = 1000 205 | 206 | return m 207 | } 208 | -------------------------------------------------------------------------------- /benchs/reform/reform_models_reform.go: -------------------------------------------------------------------------------- 1 | // Code generated by gopkg.in/reform.v1. DO NOT EDIT. 2 | 3 | package reform 4 | 5 | import ( 6 | "fmt" 7 | "strings" 8 | 9 | "gopkg.in/reform.v1" 10 | "gopkg.in/reform.v1/parse" 11 | ) 12 | 13 | type reformModelsTableType struct { 14 | s parse.StructInfo 15 | z []interface{} 16 | } 17 | 18 | // Schema returns a schema name in SQL database (""). 19 | func (v *reformModelsTableType) Schema() string { 20 | return v.s.SQLSchema 21 | } 22 | 23 | // Name returns a view or table name in SQL database ("models"). 24 | func (v *reformModelsTableType) Name() string { 25 | return v.s.SQLName 26 | } 27 | 28 | // Columns returns a new slice of column names for that view or table in SQL database. 29 | func (v *reformModelsTableType) Columns() []string { 30 | return []string{ 31 | "id", 32 | "name", 33 | "title", 34 | "fax", 35 | "web", 36 | "age", 37 | "right", 38 | "counter", 39 | } 40 | } 41 | 42 | // NewStruct makes a new struct for that view or table. 43 | func (v *reformModelsTableType) NewStruct() reform.Struct { 44 | return new(ReformModels) 45 | } 46 | 47 | // NewRecord makes a new record for that table. 48 | func (v *reformModelsTableType) NewRecord() reform.Record { 49 | return new(ReformModels) 50 | } 51 | 52 | // PKColumnIndex returns an index of primary key column for that table in SQL database. 53 | func (v *reformModelsTableType) PKColumnIndex() uint { 54 | return uint(v.s.PKFieldIndex) 55 | } 56 | 57 | // ReformModelsTable represents models view or table in SQL database. 58 | var ReformModelsTable = &reformModelsTableType{ 59 | s: parse.StructInfo{ 60 | Type: "ReformModels", 61 | SQLName: "models", 62 | Fields: []parse.FieldInfo{ 63 | {Name: "ID", Type: "int", Column: "id"}, 64 | {Name: "Name", Type: "string", Column: "name"}, 65 | {Name: "Title", Type: "string", Column: "title"}, 66 | {Name: "Fax", Type: "string", Column: "fax"}, 67 | {Name: "Web", Type: "string", Column: "web"}, 68 | {Name: "Age", Type: "int32", Column: "age"}, 69 | {Name: "Right", Type: "bool", Column: "right"}, 70 | {Name: "Counter", Type: "int64", Column: "counter"}, 71 | }, 72 | PKFieldIndex: 0, 73 | }, 74 | z: new(ReformModels).Values(), 75 | } 76 | 77 | // String returns a string representation of this struct or record. 78 | func (s ReformModels) String() string { 79 | res := make([]string, 8) 80 | res[0] = "ID: " + reform.Inspect(s.ID, true) 81 | res[1] = "Name: " + reform.Inspect(s.Name, true) 82 | res[2] = "Title: " + reform.Inspect(s.Title, true) 83 | res[3] = "Fax: " + reform.Inspect(s.Fax, true) 84 | res[4] = "Web: " + reform.Inspect(s.Web, true) 85 | res[5] = "Age: " + reform.Inspect(s.Age, true) 86 | res[6] = "Right: " + reform.Inspect(s.Right, true) 87 | res[7] = "Counter: " + reform.Inspect(s.Counter, true) 88 | return strings.Join(res, ", ") 89 | } 90 | 91 | // Values returns a slice of struct or record field values. 92 | // Returned interface{} values are never untyped nils. 93 | func (s *ReformModels) Values() []interface{} { 94 | return []interface{}{ 95 | s.ID, 96 | s.Name, 97 | s.Title, 98 | s.Fax, 99 | s.Web, 100 | s.Age, 101 | s.Right, 102 | s.Counter, 103 | } 104 | } 105 | 106 | // Pointers returns a slice of pointers to struct or record fields. 107 | // Returned interface{} values are never untyped nils. 108 | func (s *ReformModels) Pointers() []interface{} { 109 | return []interface{}{ 110 | &s.ID, 111 | &s.Name, 112 | &s.Title, 113 | &s.Fax, 114 | &s.Web, 115 | &s.Age, 116 | &s.Right, 117 | &s.Counter, 118 | } 119 | } 120 | 121 | // View returns View object for that struct. 122 | func (s *ReformModels) View() reform.View { 123 | return ReformModelsTable 124 | } 125 | 126 | // Table returns Table object for that record. 127 | func (s *ReformModels) Table() reform.Table { 128 | return ReformModelsTable 129 | } 130 | 131 | // PKValue returns a value of primary key for that record. 132 | // Returned interface{} value is never untyped nil. 133 | func (s *ReformModels) PKValue() interface{} { 134 | return s.ID 135 | } 136 | 137 | // PKPointer returns a pointer to primary key field for that record. 138 | // Returned interface{} value is never untyped nil. 139 | func (s *ReformModels) PKPointer() interface{} { 140 | return &s.ID 141 | } 142 | 143 | // HasPK returns true if record has non-zero primary key set, false otherwise. 144 | func (s *ReformModels) HasPK() bool { 145 | return s.ID != ReformModelsTable.z[ReformModelsTable.s.PKFieldIndex] 146 | } 147 | 148 | // SetPK sets record primary key, if possible. 149 | // 150 | // Deprecated: prefer direct field assignment where possible: s.ID = pk. 151 | func (s *ReformModels) SetPK(pk interface{}) { 152 | reform.SetPK(s, pk) 153 | } 154 | 155 | // check interfaces 156 | var ( 157 | _ reform.View = ReformModelsTable 158 | _ reform.Struct = (*ReformModels)(nil) 159 | _ reform.Table = ReformModelsTable 160 | _ reform.Record = (*ReformModels)(nil) 161 | _ fmt.Stringer = (*ReformModels)(nil) 162 | ) 163 | 164 | func init() { 165 | parse.AssertUpToDate(&ReformModelsTable.s, new(ReformModels)) 166 | } 167 | -------------------------------------------------------------------------------- /benchs/sqlc.go: -------------------------------------------------------------------------------- 1 | package benchs 2 | 3 | import ( 4 | "database/sql" 5 | "fmt" 6 | 7 | "github.com/efectn/go-orm-benchmarks/benchs/sqlc/db" 8 | ) 9 | 10 | var ( 11 | sqlcQueries *db.Queries 12 | ) 13 | 14 | func init() { 15 | st := NewSuite("sqlc") 16 | st.InitF = func() { 17 | st.AddBenchmark("Insert", 200*OrmMulti, SqlcInsert) 18 | st.AddBenchmark("MultiInsert 100 row", 200*OrmMulti, SqlcInsertMulti) 19 | st.AddBenchmark("Update", 200*OrmMulti, SqlcUpdate) 20 | st.AddBenchmark("Read", 200*OrmMulti, SqlcRead) 21 | st.AddBenchmark("MultiRead limit 100", 200*OrmMulti, SqlcReadSlice) 22 | 23 | dbConnection, _ := sql.Open(sqlDriver, ConvertSourceToDSN()) 24 | sqlcQueries = db.New(dbConnection) 25 | } 26 | } 27 | 28 | func SqlcInsert(b *B) { 29 | var m *Model 30 | 31 | WrapExecute(b, func() { 32 | InitDB() 33 | m = NewModel() 34 | }) 35 | 36 | for i := 0; i < b.N; i++ { 37 | m.Id = 0 38 | 39 | created, err := sqlcQueries.CreateModel(ctx, db.CreateModelParams{ 40 | Name: m.Name, 41 | Title: m.Title, 42 | Fax: m.Fax, 43 | Web: m.Web, 44 | Age: int32(m.Age), 45 | Right: m.Right, 46 | Counter: m.Counter, 47 | }) 48 | CheckErr(err, b) 49 | 50 | m = &Model{ 51 | Id: int(created.ID), 52 | Name: created.Name, 53 | Title: created.Title, 54 | Fax: created.Fax, 55 | Web: created.Web, 56 | Age: int(created.Age), 57 | Right: created.Right, 58 | Counter: created.Counter, 59 | } 60 | } 61 | } 62 | 63 | func SqlcInsertMulti(b *B) { 64 | panic(fmt.Errorf("doesn't support bulk-insert")) 65 | } 66 | 67 | func SqlcUpdate(b *B) { 68 | var m *Model 69 | 70 | WrapExecute(b, func() { 71 | InitDB() 72 | m = NewModel() 73 | created, err := sqlcQueries.CreateModel(ctx, db.CreateModelParams{ 74 | Name: m.Name, 75 | Title: m.Title, 76 | Fax: m.Fax, 77 | Web: m.Web, 78 | Age: int32(m.Age), 79 | Right: m.Right, 80 | Counter: m.Counter, 81 | }) 82 | CheckErr(err, b) 83 | 84 | m = &Model{ 85 | Id: int(created.ID), 86 | Name: created.Name, 87 | Title: created.Title, 88 | Fax: created.Fax, 89 | Web: created.Web, 90 | Age: int(created.Age), 91 | Right: created.Right, 92 | Counter: created.Counter, 93 | } 94 | }) 95 | 96 | for i := 0; i < b.N; i++ { 97 | err := sqlcQueries.UpdateModel(ctx, db.UpdateModelParams{ 98 | Name: m.Name, 99 | Title: m.Title, 100 | Fax: m.Fax, 101 | Web: m.Web, 102 | Age: int32(m.Age), 103 | Right: m.Right, 104 | Counter: m.Counter, 105 | ID: int32(m.Id), 106 | }) 107 | CheckErr(err, b) 108 | } 109 | } 110 | 111 | func SqlcRead(b *B) { 112 | var m *Model 113 | 114 | WrapExecute(b, func() { 115 | InitDB() 116 | m = NewModel() 117 | created, err := sqlcQueries.CreateModel(ctx, db.CreateModelParams{ 118 | Name: m.Name, 119 | Title: m.Title, 120 | Fax: m.Fax, 121 | Web: m.Web, 122 | Age: int32(m.Age), 123 | Right: m.Right, 124 | Counter: m.Counter, 125 | }) 126 | CheckErr(err, b) 127 | 128 | m = &Model{ 129 | Id: int(created.ID), 130 | Name: created.Name, 131 | Title: created.Title, 132 | Fax: created.Fax, 133 | Web: created.Web, 134 | Age: int(created.Age), 135 | Right: created.Right, 136 | Counter: created.Counter, 137 | } 138 | }) 139 | 140 | for i := 0; i < b.N; i++ { 141 | readed, err := sqlcQueries.GetModel(ctx, int32(m.Id)) 142 | CheckErr(err, b) 143 | 144 | m = &Model{ 145 | Id: int(readed.ID), 146 | Name: readed.Name, 147 | Title: readed.Title, 148 | Fax: readed.Fax, 149 | Web: readed.Web, 150 | Age: int(readed.Age), 151 | Right: readed.Right, 152 | Counter: readed.Counter, 153 | } 154 | } 155 | } 156 | 157 | func SqlcReadSlice(b *B) { 158 | var m *Model 159 | 160 | WrapExecute(b, func() { 161 | InitDB() 162 | m = NewModel() 163 | for i := 0; i < 100; i++ { 164 | m.Id = 0 165 | 166 | created, err := sqlcQueries.CreateModel(ctx, db.CreateModelParams{ 167 | Name: m.Name, 168 | Title: m.Title, 169 | Fax: m.Fax, 170 | Web: m.Web, 171 | Age: int32(m.Age), 172 | Right: m.Right, 173 | Counter: m.Counter, 174 | }) 175 | CheckErr(err, b) 176 | 177 | m = &Model{ 178 | Id: int(created.ID), 179 | Name: created.Name, 180 | Title: created.Title, 181 | Fax: created.Fax, 182 | Web: created.Web, 183 | Age: int(created.Age), 184 | Right: created.Right, 185 | Counter: created.Counter, 186 | } 187 | } 188 | }) 189 | 190 | for i := 0; i < b.N; i++ { 191 | sqlcModels, err := sqlcQueries.ListModels(ctx, db.ListModelsParams{ 192 | ID: 0, 193 | Limit: 100, 194 | }) 195 | CheckErr(err, b) 196 | 197 | models := make([]*Model, len(sqlcModels)) 198 | 199 | for i2 := range sqlcModels { 200 | models[i2] = &Model{ 201 | Name: sqlcModels[i2].Name, 202 | Title: sqlcModels[i2].Title, 203 | Fax: sqlcModels[i2].Fax, 204 | Web: sqlcModels[i2].Web, 205 | Age: int(sqlcModels[i2].Age), 206 | Right: sqlcModels[i2].Right, 207 | Counter: sqlcModels[i2].Counter, 208 | } 209 | } 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /benchs/raw.go: -------------------------------------------------------------------------------- 1 | package benchs 2 | 3 | import ( 4 | "database/sql" 5 | ) 6 | 7 | var raw *sql.DB 8 | 9 | const ( 10 | // rawInsertBaseSQL = `INSERT INTO models (name, title, fax, web, age, "right", counter) VALUES ` 11 | rawInsertBaseSQL = "INSERT INTO models (name, title, fax, web, age, `right`, counter) VALUES " 12 | // rawInsertValuesSQL = `($1, $2, $3, $4, $5, $6, $7)` 13 | rawInsertValuesSQL = `(?, ?, ?, ?, ?, ?, ?)` 14 | rawInsertSQL = rawInsertBaseSQL + rawInsertValuesSQL 15 | // rawUpdateSQL = `UPDATE models SET name = $1, title = $2, fax = $3, web = $4, age = $5, "right" = $6, counter = $7 WHERE id = $8` 16 | rawUpdateSQL = "UPDATE models SET name = ?, title = ?, fax = ?, web = ?, age = ?, `right` = ?, counter = ? WHERE id = ?" 17 | // rawSelectSQL = `SELECT id, name, title, fax, web, age, "right", counter FROM models WHERE id = $1` 18 | rawSelectSQL = "SELECT id, name, title, fax, web, age, `right`, counter FROM models WHERE id = ?" 19 | // rawSelectMultiSQL = `SELECT id, name, title, fax, web, age, "right", counter FROM models WHERE id > 0 LIMIT 100` 20 | rawSelectMultiSQL = "SELECT id, name, title, fax, web, age, `right`, counter FROM models WHERE id > 0 LIMIT 100" 21 | ) 22 | 23 | func init() { 24 | st := NewSuite("raw") 25 | st.InitF = func() { 26 | st.AddBenchmark("Insert", 200*OrmMulti, RawInsert) 27 | st.AddBenchmark("MultiInsert 100 row", 200*OrmMulti, RawInsertMulti) 28 | st.AddBenchmark("Update", 200*OrmMulti, RawUpdate) 29 | st.AddBenchmark("Read", 200*OrmMulti, RawRead) 30 | st.AddBenchmark("MultiRead limit 100", 200*OrmMulti, RawReadSlice) 31 | 32 | raw, _ = sql.Open("mysql", ConvertSourceToDSN()) 33 | } 34 | } 35 | 36 | func RawInsert(b *B) { 37 | var m *Model 38 | WrapExecute(b, func() { 39 | InitDB() 40 | m = NewModel() 41 | }) 42 | 43 | for i := 0; i < b.N; i++ { 44 | // pq dose not support the LastInsertId method. 45 | result, err := raw.Exec(rawInsertSQL, m.Name, m.Title, m.Fax, m.Web, m.Age, m.Right, m.Counter) 46 | CheckErr(err, b) 47 | id, _ := result.LastInsertId() 48 | m.Id = int(id) 49 | } 50 | } 51 | 52 | func rawInsert(m *Model) error { 53 | // pq dose not support the LastInsertId method. 54 | _, err := raw.Exec(rawInsertSQL, m.Name, m.Title, m.Fax, m.Web, m.Age, m.Right, m.Counter) 55 | CheckErr(err) 56 | 57 | return nil 58 | } 59 | 60 | func RawInsertMulti(b *B) { 61 | var ms []*Model 62 | WrapExecute(b, func() { 63 | InitDB() 64 | 65 | ms = make([]*Model, 0, 100) 66 | for i := 0; i < 100; i++ { 67 | ms = append(ms, NewModel()) 68 | } 69 | }) 70 | 71 | var valuesSQL string 72 | counter := 1 73 | for i := 0; i < 100; i++ { 74 | hoge := "" 75 | for j := 0; j < 7; j++ { 76 | // if j != 6 { 77 | // hoge += "$" + strconv.Itoa(counter) + "," 78 | // } else { 79 | // hoge += "$" + strconv.Itoa(counter) 80 | // } 81 | if j != 6 { 82 | hoge += "?," 83 | } else { 84 | hoge += "?" 85 | } 86 | counter++ 87 | 88 | } 89 | if i != 99 { 90 | valuesSQL += "(" + hoge + ")," 91 | } else { 92 | valuesSQL += "(" + hoge + ")" 93 | } 94 | } 95 | 96 | for i := 0; i < b.N; i++ { 97 | nFields := 7 98 | query := rawInsertBaseSQL + valuesSQL 99 | args := make([]interface{}, len(ms)*nFields) 100 | for j := range ms { 101 | offset := j * nFields 102 | args[offset+0] = ms[j].Name 103 | args[offset+1] = ms[j].Title 104 | args[offset+2] = ms[j].Fax 105 | args[offset+3] = ms[j].Web 106 | args[offset+4] = ms[j].Age 107 | args[offset+5] = ms[j].Right 108 | args[offset+6] = ms[j].Counter 109 | } 110 | // pq dose not support the LastInsertId method. 111 | _, err := raw.Exec(query, args...) 112 | CheckErr(err, b) 113 | } 114 | } 115 | 116 | func RawUpdate(b *B) { 117 | var m *Model 118 | WrapExecute(b, func() { 119 | InitDB() 120 | m = NewModel() 121 | err := rawInsert(m) 122 | CheckErr(err, b) 123 | }) 124 | 125 | for i := 0; i < b.N; i++ { 126 | _, err := raw.Exec(rawUpdateSQL, m.Name, m.Title, m.Fax, m.Web, m.Age, m.Right, m.Counter, m.Id) 127 | CheckErr(err, b) 128 | } 129 | } 130 | 131 | func RawRead(b *B) { 132 | var m *Model 133 | WrapExecute(b, func() { 134 | InitDB() 135 | m = NewModel() 136 | err := rawInsert(m) 137 | CheckErr(err, b) 138 | }) 139 | 140 | for i := 0; i < b.N; i++ { 141 | var mout Model 142 | err := raw.QueryRow(rawSelectSQL, 1).Scan( 143 | //err := stmt.QueryRow(m.Id).Scan( 144 | &mout.Id, 145 | &mout.Name, 146 | &mout.Title, 147 | &mout.Fax, 148 | &mout.Web, 149 | &mout.Age, 150 | &mout.Right, 151 | &mout.Counter, 152 | ) 153 | CheckErr(err, b) 154 | } 155 | } 156 | 157 | func RawReadSlice(b *B) { 158 | var m *Model 159 | WrapExecute(b, func() { 160 | var err error 161 | InitDB() 162 | m = NewModel() 163 | for i := 0; i < 100; i++ { 164 | err = rawInsert(m) 165 | CheckErr(err, b) 166 | } 167 | CheckErr(err, b) 168 | }) 169 | 170 | for i := 0; i < b.N; i++ { 171 | var j int 172 | models := make([]Model, 100) 173 | rows, err := raw.Query(rawSelectMultiSQL) 174 | CheckErr(err, b) 175 | for j = 0; rows.Next() && j < len(models); j++ { 176 | err = rows.Scan( 177 | &models[j].Id, 178 | &models[j].Name, 179 | &models[j].Title, 180 | &models[j].Fax, 181 | &models[j].Web, 182 | &models[j].Age, 183 | &models[j].Right, 184 | &models[j].Counter, 185 | ) 186 | CheckErr(err, b) 187 | } 188 | err = rows.Err() 189 | CheckErr(err, b) 190 | 191 | err = rows.Close() 192 | CheckErr(err, b) 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ## GITATTRIBUTES FOR WEB PROJECTS 2 | # 3 | # These settings are for any web project. 4 | # 5 | # Details per file setting: 6 | # text These files should be normalized (i.e. convert CRLF to LF). 7 | # binary These files are binary and should be left untouched. 8 | # 9 | # Note that binary is a macro for -text -diff. 10 | ###################################################################### 11 | 12 | # Auto detect 13 | ## Handle line endings automatically for files detected as 14 | ## text and leave all files detected as binary untouched. 15 | ## This will handle all files NOT defined below. 16 | * text=auto 17 | 18 | # Source code 19 | *.bash text eol=lf 20 | *.bat text eol=crlf 21 | *.cmd text eol=crlf 22 | *.coffee text 23 | *.css text 24 | *.htm text diff=html 25 | *.html text diff=html 26 | *.inc text 27 | *.ini text 28 | *.js text 29 | *.json text 30 | *.jsx text 31 | *.less text 32 | *.ls text 33 | *.map text -diff 34 | *.od text 35 | *.onlydata text 36 | *.php text diff=php 37 | *.pl text 38 | *.ps1 text eol=crlf 39 | *.py text diff=python 40 | *.rb text diff=ruby 41 | *.sass text 42 | *.scm text 43 | *.scss text diff=css 44 | *.sh text eol=lf 45 | *.sql text 46 | *.styl text 47 | *.tag text 48 | *.ts text 49 | *.tsx text 50 | *.xml text 51 | *.xhtml text diff=html 52 | 53 | # Docker 54 | Dockerfile text 55 | 56 | # Documentation 57 | *.ipynb text 58 | *.markdown text 59 | *.md text 60 | *.mdwn text 61 | *.mdown text 62 | *.mkd text 63 | *.mkdn text 64 | *.mdtxt text 65 | *.mdtext text 66 | *.txt text 67 | AUTHORS text 68 | CHANGELOG text 69 | CHANGES text 70 | CONTRIBUTING text 71 | COPYING text 72 | copyright text 73 | *COPYRIGHT* text 74 | INSTALL text 75 | license text 76 | LICENSE text 77 | NEWS text 78 | readme text 79 | *README* text 80 | TODO text 81 | 82 | # Templates 83 | *.dot text 84 | *.ejs text 85 | *.haml text 86 | *.handlebars text 87 | *.hbs text 88 | *.hbt text 89 | *.jade text 90 | *.latte text 91 | *.mustache text 92 | *.njk text 93 | *.phtml text 94 | *.tmpl text 95 | *.tpl text 96 | *.twig text 97 | *.vue text 98 | 99 | # Configs 100 | *.cnf text 101 | *.conf text 102 | *.config text 103 | .editorconfig text 104 | .env text 105 | .gitattributes text 106 | .gitconfig text 107 | .htaccess text 108 | *.lock text -diff 109 | package-lock.json text -diff 110 | *.toml text 111 | *.yaml text 112 | *.yml text 113 | browserslist text 114 | Makefile text 115 | makefile text 116 | 117 | # Heroku 118 | Procfile text 119 | 120 | # Graphics 121 | *.ai binary 122 | *.bmp binary 123 | *.eps binary 124 | *.gif binary 125 | *.gifv binary 126 | *.ico binary 127 | *.jng binary 128 | *.jp2 binary 129 | *.jpg binary 130 | *.jpeg binary 131 | *.jpx binary 132 | *.jxr binary 133 | *.pdf binary 134 | *.png binary 135 | *.psb binary 136 | *.psd binary 137 | # SVG treated as an asset (binary) by default. 138 | *.svg text 139 | # If you want to treat it as binary, 140 | # use the following line instead. 141 | # *.svg binary 142 | *.svgz binary 143 | *.tif binary 144 | *.tiff binary 145 | *.wbmp binary 146 | *.webp binary 147 | 148 | # Audio 149 | *.kar binary 150 | *.m4a binary 151 | *.mid binary 152 | *.midi binary 153 | *.mp3 binary 154 | *.ogg binary 155 | *.ra binary 156 | 157 | # Video 158 | *.3gpp binary 159 | *.3gp binary 160 | *.as binary 161 | *.asf binary 162 | *.asx binary 163 | *.fla binary 164 | *.flv binary 165 | *.m4v binary 166 | *.mng binary 167 | *.mov binary 168 | *.mp4 binary 169 | *.mpeg binary 170 | *.mpg binary 171 | *.ogv binary 172 | *.swc binary 173 | *.swf binary 174 | *.webm binary 175 | 176 | # Archives 177 | *.7z binary 178 | *.gz binary 179 | *.jar binary 180 | *.rar binary 181 | *.tar binary 182 | *.zip binary 183 | 184 | # Fonts 185 | *.ttf binary 186 | *.eot binary 187 | *.otf binary 188 | *.woff binary 189 | *.woff2 binary 190 | 191 | # Executables 192 | *.exe binary 193 | *.pyc binary 194 | 195 | # RC files (like .babelrc or .eslintrc) 196 | *.*rc text 197 | 198 | # Ignore files (like .npmignore or .gitignore) 199 | *.*ignore text 200 | 201 | *.go -text diff=golang -------------------------------------------------------------------------------- /benchs/ent/model.go: -------------------------------------------------------------------------------- 1 | // Code generated by entc, DO NOT EDIT. 2 | 3 | package ent 4 | 5 | import ( 6 | "fmt" 7 | "strings" 8 | 9 | "entgo.io/ent/dialect/sql" 10 | "github.com/efectn/go-orm-benchmarks/benchs/ent/model" 11 | ) 12 | 13 | // Model is the model entity for the Model schema. 14 | type Model 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 | // Title holds the value of the "title" field. 21 | Title string `json:"title,omitempty"` 22 | // Fax holds the value of the "fax" field. 23 | Fax string `json:"fax,omitempty"` 24 | // Web holds the value of the "web" field. 25 | Web string `json:"web,omitempty"` 26 | // Age holds the value of the "age" field. 27 | Age int `json:"age,omitempty"` 28 | // Right holds the value of the "right" field. 29 | Right bool `json:"right,omitempty"` 30 | // Counter holds the value of the "counter" field. 31 | Counter int64 `json:"counter,omitempty"` 32 | } 33 | 34 | // scanValues returns the types for scanning values from sql.Rows. 35 | func (*Model) scanValues(columns []string) ([]interface{}, error) { 36 | values := make([]interface{}, len(columns)) 37 | for i := range columns { 38 | switch columns[i] { 39 | case model.FieldRight: 40 | values[i] = new(sql.NullBool) 41 | case model.FieldID, model.FieldAge, model.FieldCounter: 42 | values[i] = new(sql.NullInt64) 43 | case model.FieldName, model.FieldTitle, model.FieldFax, model.FieldWeb: 44 | values[i] = new(sql.NullString) 45 | default: 46 | return nil, fmt.Errorf("unexpected column %q for type Model", columns[i]) 47 | } 48 | } 49 | return values, nil 50 | } 51 | 52 | // assignValues assigns the values that were returned from sql.Rows (after scanning) 53 | // to the Model fields. 54 | func (m *Model) assignValues(columns []string, values []interface{}) error { 55 | if m, n := len(values), len(columns); m < n { 56 | return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) 57 | } 58 | for i := range columns { 59 | switch columns[i] { 60 | case model.FieldID: 61 | value, ok := values[i].(*sql.NullInt64) 62 | if !ok { 63 | return fmt.Errorf("unexpected type %T for field id", value) 64 | } 65 | m.ID = int(value.Int64) 66 | case model.FieldName: 67 | if value, ok := values[i].(*sql.NullString); !ok { 68 | return fmt.Errorf("unexpected type %T for field name", values[i]) 69 | } else if value.Valid { 70 | m.Name = value.String 71 | } 72 | case model.FieldTitle: 73 | if value, ok := values[i].(*sql.NullString); !ok { 74 | return fmt.Errorf("unexpected type %T for field title", values[i]) 75 | } else if value.Valid { 76 | m.Title = value.String 77 | } 78 | case model.FieldFax: 79 | if value, ok := values[i].(*sql.NullString); !ok { 80 | return fmt.Errorf("unexpected type %T for field fax", values[i]) 81 | } else if value.Valid { 82 | m.Fax = value.String 83 | } 84 | case model.FieldWeb: 85 | if value, ok := values[i].(*sql.NullString); !ok { 86 | return fmt.Errorf("unexpected type %T for field web", values[i]) 87 | } else if value.Valid { 88 | m.Web = value.String 89 | } 90 | case model.FieldAge: 91 | if value, ok := values[i].(*sql.NullInt64); !ok { 92 | return fmt.Errorf("unexpected type %T for field age", values[i]) 93 | } else if value.Valid { 94 | m.Age = int(value.Int64) 95 | } 96 | case model.FieldRight: 97 | if value, ok := values[i].(*sql.NullBool); !ok { 98 | return fmt.Errorf("unexpected type %T for field right", values[i]) 99 | } else if value.Valid { 100 | m.Right = value.Bool 101 | } 102 | case model.FieldCounter: 103 | if value, ok := values[i].(*sql.NullInt64); !ok { 104 | return fmt.Errorf("unexpected type %T for field counter", values[i]) 105 | } else if value.Valid { 106 | m.Counter = value.Int64 107 | } 108 | } 109 | } 110 | return nil 111 | } 112 | 113 | // Update returns a builder for updating this Model. 114 | // Note that you need to call Model.Unwrap() before calling this method if this Model 115 | // was returned from a transaction, and the transaction was committed or rolled back. 116 | func (m *Model) Update() *ModelUpdateOne { 117 | return (&ModelClient{config: m.config}).UpdateOne(m) 118 | } 119 | 120 | // Unwrap unwraps the Model entity that was returned from a transaction after it was closed, 121 | // so that all future queries will be executed through the driver which created the transaction. 122 | func (m *Model) Unwrap() *Model { 123 | tx, ok := m.config.driver.(*txDriver) 124 | if !ok { 125 | panic("ent: Model is not a transactional entity") 126 | } 127 | m.config.driver = tx.drv 128 | return m 129 | } 130 | 131 | // String implements the fmt.Stringer. 132 | func (m *Model) String() string { 133 | var builder strings.Builder 134 | builder.WriteString("Model(") 135 | builder.WriteString(fmt.Sprintf("id=%v", m.ID)) 136 | builder.WriteString(", name=") 137 | builder.WriteString(m.Name) 138 | builder.WriteString(", title=") 139 | builder.WriteString(m.Title) 140 | builder.WriteString(", fax=") 141 | builder.WriteString(m.Fax) 142 | builder.WriteString(", web=") 143 | builder.WriteString(m.Web) 144 | builder.WriteString(", age=") 145 | builder.WriteString(fmt.Sprintf("%v", m.Age)) 146 | builder.WriteString(", right=") 147 | builder.WriteString(fmt.Sprintf("%v", m.Right)) 148 | builder.WriteString(", counter=") 149 | builder.WriteString(fmt.Sprintf("%v", m.Counter)) 150 | builder.WriteByte(')') 151 | return builder.String() 152 | } 153 | 154 | // Models is a parsable slice of Model. 155 | type Models []*Model 156 | 157 | func (m Models) config(cfg config) { 158 | for _i := range m { 159 | m[_i].config = cfg 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /benchs/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 | "github.com/efectn/go-orm-benchmarks/benchs/ent" 10 | ) 11 | 12 | // The ModelFunc type is an adapter to allow the use of ordinary 13 | // function as Model mutator. 14 | type ModelFunc func(context.Context, *ent.ModelMutation) (ent.Value, error) 15 | 16 | // Mutate calls f(ctx, m). 17 | func (f ModelFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { 18 | mv, ok := m.(*ent.ModelMutation) 19 | if !ok { 20 | return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.ModelMutation", 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 | -------------------------------------------------------------------------------- /benchs/benchmark_suite.go: -------------------------------------------------------------------------------- 1 | package benchs 2 | 3 | import ( 4 | "fmt" 5 | "runtime" 6 | "sort" 7 | "sync" 8 | "time" 9 | ) 10 | 11 | const sqlDriver = "mysql" 12 | 13 | type BenchmarkResult struct { 14 | N int 15 | T time.Duration 16 | MemAllocs uint64 17 | MemBytes uint64 18 | FailedMsg string 19 | } 20 | 21 | func (r BenchmarkResult) NsPerOp() int64 { 22 | if r.N <= 0 { 23 | return 0 24 | } 25 | return r.T.Nanoseconds() / int64(r.N) 26 | } 27 | 28 | func (r BenchmarkResult) AllocsPerOp() int64 { 29 | if r.N <= 0 { 30 | return 0 31 | } 32 | return int64(r.MemAllocs) / int64(r.N) 33 | } 34 | 35 | func (r BenchmarkResult) AllocedBytesPerOp() int64 { 36 | if r.N <= 0 { 37 | return 0 38 | } 39 | return int64(r.MemBytes) / int64(r.N) 40 | } 41 | 42 | func (r BenchmarkResult) String() string { 43 | if len(r.FailedMsg) > 0 { 44 | return " " + r.FailedMsg 45 | } 46 | 47 | nsop := r.NsPerOp() 48 | total := fmt.Sprintf(" %5.2fs", float64(r.T)/1e9) 49 | ns := fmt.Sprintf(" %10d ns/op", nsop) 50 | if r.N > 0 && nsop < 100 { 51 | 52 | if nsop < 10 { 53 | ns = fmt.Sprintf("%10.2f ns/op", float64(r.T.Nanoseconds())/float64(r.N)) 54 | } else { 55 | ns = fmt.Sprintf("%9.1f ns/op", float64(r.T.Nanoseconds())/float64(r.N)) 56 | } 57 | } 58 | return fmt.Sprintf("%s%s", total, ns) + fmt.Sprintf("%8d B/op %5d allocs/op", 59 | r.AllocedBytesPerOp(), r.AllocsPerOp()) 60 | } 61 | 62 | type common struct { 63 | mu sync.RWMutex 64 | failed bool 65 | 66 | start time.Time 67 | duration time.Duration 68 | signal chan interface{} 69 | } 70 | 71 | func (c *common) Fail() { 72 | c.mu.Lock() 73 | defer c.mu.Unlock() 74 | c.failed = true 75 | } 76 | func (c *common) FailNow() { 77 | c.Fail() 78 | runtime.Goexit() 79 | } 80 | 81 | var benchmarkLock sync.Mutex 82 | 83 | var memStats runtime.MemStats 84 | 85 | type B struct { 86 | common 87 | Brand string 88 | Name string 89 | N int 90 | F func(b *B) 91 | 92 | timerOn bool 93 | 94 | startAllocs uint64 95 | startBytes uint64 96 | 97 | netAllocs uint64 98 | netBytes uint64 99 | 100 | result *BenchmarkResult 101 | } 102 | 103 | func (b *B) StartTimer() { 104 | if !b.timerOn { 105 | runtime.ReadMemStats(&memStats) 106 | b.startAllocs = memStats.Mallocs 107 | b.startBytes = memStats.TotalAlloc 108 | b.start = time.Now() 109 | b.timerOn = true 110 | } 111 | } 112 | 113 | func (b *B) StopTimer() { 114 | if b.timerOn { 115 | b.duration += time.Since(b.start) 116 | runtime.ReadMemStats(&memStats) 117 | b.netAllocs += memStats.Mallocs - b.startAllocs 118 | b.netBytes += memStats.TotalAlloc - b.startBytes 119 | b.timerOn = false 120 | } 121 | } 122 | 123 | func (b *B) ResetTimer() { 124 | if b.timerOn { 125 | runtime.ReadMemStats(&memStats) 126 | b.startAllocs = memStats.Mallocs 127 | b.startBytes = memStats.TotalAlloc 128 | b.start = time.Now() 129 | } 130 | b.duration = 0 131 | b.netAllocs = 0 132 | b.netBytes = 0 133 | } 134 | 135 | func (b *B) launch() { 136 | benchmarkLock.Lock() 137 | 138 | defer func() { 139 | if err := recover(); err != nil { 140 | b.failed = true 141 | b.result = &BenchmarkResult{FailedMsg: fmt.Sprint(err)} 142 | } else { 143 | b.result = &BenchmarkResult{b.N, b.duration, b.netAllocs, b.netBytes, ""} 144 | } 145 | 146 | b.signal <- b 147 | benchmarkLock.Unlock() 148 | }() 149 | 150 | runtime.GC() 151 | b.ResetTimer() 152 | b.StartTimer() 153 | b.F(b) 154 | b.StopTimer() 155 | } 156 | 157 | func (b *B) run() { 158 | go b.launch() 159 | <-b.signal 160 | } 161 | 162 | type suite struct { 163 | Brand string 164 | InitF func() 165 | benchs []*B 166 | } 167 | 168 | func (st *suite) AddBenchmark(name string, n int, run func(b *B)) { 169 | st.benchs = append(st.benchs, &B{ 170 | common: common{ 171 | signal: make(chan interface{}), 172 | }, 173 | Name: name, 174 | Brand: st.Brand, 175 | N: n, 176 | F: run, 177 | }) 178 | if len(st.benchs) > benchmarksNums { 179 | benchmarksNums = len(st.benchs) 180 | } 181 | } 182 | 183 | func (st *suite) run() { 184 | for _, b := range st.benchs { 185 | b.run() 186 | if DebugMode { 187 | fmt.Printf("%25s: %6d ", b.Name, b.N) 188 | fmt.Println(b.result.String()) 189 | } 190 | } 191 | } 192 | 193 | var BrandNames []string 194 | var benchmarks = make(map[string]*suite) 195 | var benchmarksNums = 0 196 | 197 | func NewSuite(name string) *suite { 198 | s := new(suite) 199 | s.Brand = name 200 | benchmarks[name] = s 201 | BrandNames = append(BrandNames, name) 202 | return s 203 | } 204 | 205 | func RunBenchmark(name string) { 206 | if s, ok := benchmarks[name]; ok { 207 | s.InitF() 208 | if len(s.benchs) != benchmarksNums { 209 | CheckErr(fmt.Errorf("%s have not enough benchmarks", s.Brand)) 210 | } 211 | s.run() 212 | } else { 213 | CheckErr(fmt.Errorf("not found benchmark suite %s", s.Brand)) 214 | } 215 | } 216 | 217 | type BList []*B 218 | 219 | func (s BList) Len() int { return len(s) } 220 | func (s BList) Swap(i, j int) { s[i], s[j] = s[j], s[i] } 221 | func (s BList) Less(i, j int) bool { 222 | if s[i].failed { 223 | return false 224 | } 225 | if s[j].failed { 226 | return true 227 | } 228 | // return s[i].netBytes*s[i].netAllocs*uint64(s[i].result.N) < s[j].netBytes*s[j].netAllocs*uint64(s[j].result.N) 229 | return (s[i].duration) < (s[j].duration) 230 | } 231 | 232 | func MakeReport() (result string) { 233 | var first string 234 | 235 | for i := 0; i < benchmarksNums; i++ { 236 | 237 | var benchs BList 238 | 239 | for _, name := range BrandNames { 240 | 241 | if s, ok := benchmarks[name]; ok { 242 | 243 | if i >= len(s.benchs) { 244 | continue 245 | } 246 | 247 | b := s.benchs[i] 248 | 249 | if b.result == nil { 250 | continue 251 | } 252 | 253 | if len(first) == 0 { 254 | first = name 255 | } 256 | 257 | benchs = append(benchs, b) 258 | 259 | if name == first { 260 | result += fmt.Sprintf("%6d times - %s\n", b.N, b.Name) 261 | } 262 | } 263 | } 264 | 265 | sort.Sort(benchs) 266 | 267 | for _, b := range benchs { 268 | result += fmt.Sprintf("%10s: ", b.Brand) + b.result.String() + "\n" 269 | } 270 | 271 | if i < benchmarksNums-1 { 272 | result += "\n" 273 | } 274 | } 275 | return 276 | } 277 | -------------------------------------------------------------------------------- /db/operator.go: -------------------------------------------------------------------------------- 1 | // Code generated by sqlgen, version v1.0.0-alpha.5; DO NOT EDIT. 2 | 3 | package db 4 | 5 | import ( 6 | "github.com/si3nloong/sqlgen/sequel" 7 | ) 8 | 9 | func And(stmts ...sequel.WhereClause) sequel.WhereClause { 10 | return func(stmt sequel.StmtBuilder) { 11 | stmt.WriteByte('(') 12 | for i := range stmts { 13 | if i > 0 { 14 | stmt.WriteString(" AND ") 15 | } 16 | stmts[i](stmt) 17 | } 18 | stmt.WriteByte(')') 19 | } 20 | } 21 | 22 | func Or(stmts ...sequel.WhereClause) sequel.WhereClause { 23 | return func(stmt sequel.StmtBuilder) { 24 | stmt.WriteByte('(') 25 | for i := range stmts { 26 | if i > 0 { 27 | stmt.WriteString(" OR ") 28 | } 29 | stmts[i](stmt) 30 | } 31 | stmt.WriteByte(')') 32 | } 33 | } 34 | 35 | func Equal[T comparable](f sequel.ColumnValuer[T], value T) sequel.WhereClause { 36 | return func(stmt sequel.StmtBuilder) { 37 | switch vi := f.(type) { 38 | case sequel.SQLColumnValuer[T]: 39 | stmt.WriteString(f.ColumnName() + " = " + vi.SQLValue(stmt.Var(f.Convert(value)))) 40 | default: 41 | stmt.WriteString(f.ColumnName() + " = " + stmt.Var(f.Convert(value))) 42 | } 43 | } 44 | } 45 | 46 | func NotEqual[T comparable](f sequel.ColumnValuer[T], value T) sequel.WhereClause { 47 | return func(stmt sequel.StmtBuilder) { 48 | switch vi := f.(type) { 49 | case sequel.SQLColumnValuer[T]: 50 | stmt.WriteString(f.ColumnName() + " <> " + vi.SQLValue(stmt.Var(f.Convert(value)))) 51 | default: 52 | stmt.WriteString(f.ColumnName() + " <> " + stmt.Var(f.Convert(value))) 53 | } 54 | } 55 | } 56 | 57 | func In[T any](f sequel.ColumnValuer[T], values ...T) sequel.WhereClause { 58 | return func(stmt sequel.StmtBuilder) { 59 | args := make([]any, len(values)) 60 | for idx := range values { 61 | args[idx] = f.Convert(values[idx]) 62 | } 63 | stmt.WriteString(f.ColumnName() + " IN " + stmt.Vars(args)) 64 | } 65 | } 66 | 67 | func NotIn[T any](f sequel.ColumnValuer[T], values ...T) sequel.WhereClause { 68 | return func(stmt sequel.StmtBuilder) { 69 | args := make([]any, len(values)) 70 | for idx := range values { 71 | args[idx] = f.Convert(values[idx]) 72 | } 73 | stmt.WriteString(f.ColumnName() + " NOT IN " + stmt.Vars(args)) 74 | } 75 | } 76 | 77 | func GreaterThan[T comparable](f sequel.ColumnValuer[T], value T) sequel.WhereClause { 78 | return func(stmt sequel.StmtBuilder) { 79 | switch vi := f.(type) { 80 | case sequel.SQLColumnValuer[T]: 81 | stmt.WriteString(f.ColumnName() + " > " + vi.SQLValue(stmt.Var(f.Convert(value)))) 82 | default: 83 | stmt.WriteString(f.ColumnName() + " > " + stmt.Var(f.Convert(value))) 84 | } 85 | } 86 | } 87 | 88 | func GreaterThanOrEqual[T comparable](f sequel.ColumnValuer[T], value T) sequel.WhereClause { 89 | return func(stmt sequel.StmtBuilder) { 90 | switch vi := f.(type) { 91 | case sequel.SQLColumnValuer[T]: 92 | stmt.WriteString(f.ColumnName() + " >= " + vi.SQLValue(stmt.Var(f.Convert(value)))) 93 | default: 94 | stmt.WriteString(f.ColumnName() + " >= " + stmt.Var(f.Convert(value))) 95 | } 96 | } 97 | } 98 | 99 | func LessThan[T comparable](f sequel.ColumnValuer[T], value T) sequel.WhereClause { 100 | return func(stmt sequel.StmtBuilder) { 101 | switch vi := f.(type) { 102 | case sequel.SQLColumnValuer[T]: 103 | stmt.WriteString(f.ColumnName() + " < " + vi.SQLValue(stmt.Var(f.Convert(value)))) 104 | default: 105 | stmt.WriteString(f.ColumnName() + " < " + stmt.Var(f.Convert(value))) 106 | } 107 | } 108 | } 109 | 110 | func LessThanOrEqual[T comparable](f sequel.ColumnValuer[T], value T) sequel.WhereClause { 111 | return func(stmt sequel.StmtBuilder) { 112 | switch vi := f.(type) { 113 | case sequel.SQLColumnValuer[T]: 114 | stmt.WriteString(f.ColumnName() + " <= " + vi.SQLValue(stmt.Var(f.Convert(value)))) 115 | default: 116 | stmt.WriteString(f.ColumnName() + " <= " + stmt.Var(f.Convert(value))) 117 | } 118 | } 119 | } 120 | 121 | func Like[T comparable](f sequel.ColumnValuer[T], value T) sequel.WhereClause { 122 | return func(stmt sequel.StmtBuilder) { 123 | switch vi := f.(type) { 124 | case sequel.SQLColumnValuer[T]: 125 | stmt.WriteString(f.ColumnName() + " LIKE " + vi.SQLValue(stmt.Var(f.Convert(value)))) 126 | default: 127 | stmt.WriteString(f.ColumnName() + " LIKE " + stmt.Var(f.Convert(value))) 128 | } 129 | } 130 | } 131 | 132 | func NotLike[T comparable](f sequel.ColumnValuer[T], value T) sequel.WhereClause { 133 | return func(stmt sequel.StmtBuilder) { 134 | switch vi := f.(type) { 135 | case sequel.SQLColumnValuer[T]: 136 | stmt.WriteString(f.ColumnName() + " NOT LIKE " + vi.SQLValue(stmt.Var(f.Convert(value)))) 137 | default: 138 | stmt.WriteString(f.ColumnName() + " NOT LIKE " + stmt.Var(f.Convert(value))) 139 | } 140 | } 141 | } 142 | 143 | func IsNull[T any](f sequel.ColumnValuer[T]) sequel.WhereClause { 144 | return func(stmt sequel.StmtBuilder) { 145 | stmt.WriteString(f.ColumnName() + " IS NULL") 146 | } 147 | } 148 | 149 | func IsNotNull[T any](f sequel.ColumnValuer[T]) sequel.WhereClause { 150 | return func(stmt sequel.StmtBuilder) { 151 | stmt.WriteString(f.ColumnName() + " IS NOT NULL") 152 | } 153 | } 154 | 155 | func Between[T comparable](f sequel.ColumnValuer[T], from, to T) sequel.WhereClause { 156 | return func(stmt sequel.StmtBuilder) { 157 | switch vi := f.(type) { 158 | case sequel.SQLColumnValuer[T]: 159 | stmt.WriteString(f.ColumnName() + " BETWEEN " + vi.SQLValue(stmt.Var(from)) + " AND " + vi.SQLValue(stmt.Var(to))) 160 | default: 161 | stmt.WriteString(f.ColumnName() + " BETWEEN " + stmt.Var(from) + " AND " + stmt.Var(to)) 162 | } 163 | } 164 | } 165 | 166 | func Set[T any](f sequel.ColumnValuer[T], value ...T) sequel.SetClause { 167 | return func(stmt sequel.StmtBuilder) { 168 | defaultValue := f.Value() 169 | if len(value) > 0 { 170 | defaultValue = f.Convert(value[0]) 171 | } 172 | switch vi := f.(type) { 173 | case sequel.SQLColumnValuer[T]: 174 | stmt.WriteString(f.ColumnName() + " = " + vi.SQLValue(stmt.Var(defaultValue))) 175 | default: 176 | stmt.WriteString(f.ColumnName() + " = " + stmt.Var(defaultValue)) 177 | } 178 | } 179 | } 180 | 181 | func Asc[T any](f sequel.ColumnValuer[T]) sequel.OrderByClause { 182 | return sequel.OrderByColumn(f.ColumnName(), true) 183 | } 184 | 185 | func Desc[T any](f sequel.ColumnValuer[T]) sequel.OrderByClause { 186 | return sequel.OrderByColumn(f.ColumnName(), false) 187 | } 188 | -------------------------------------------------------------------------------- /benchs/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 | // Model is the client for interacting with the Model builders. 16 | Model *ModelClient 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 Commit 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(ctx 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 Rollback 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(ctx 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.Model = NewModelClient(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: Model.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 | -------------------------------------------------------------------------------- /benchs/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 | "github.com/efectn/go-orm-benchmarks/benchs/ent/migrate" 11 | 12 | "github.com/efectn/go-orm-benchmarks/benchs/ent/model" 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 | // Model is the client for interacting with the Model builders. 24 | Model *ModelClient 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.Model = NewModelClient(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 | Model: NewModelClient(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 | ctx: ctx, 91 | config: cfg, 92 | Model: NewModelClient(cfg), 93 | }, nil 94 | } 95 | 96 | // Debug returns a new debug-client. It's used to get verbose logging on specific operations. 97 | // 98 | // client.Debug(). 99 | // Model. 100 | // Query(). 101 | // Count(ctx) 102 | // 103 | func (c *Client) Debug() *Client { 104 | if c.debug { 105 | return c 106 | } 107 | cfg := c.config 108 | cfg.driver = dialect.Debug(c.driver, c.log) 109 | client := &Client{config: cfg} 110 | client.init() 111 | return client 112 | } 113 | 114 | // Close closes the database connection and prevents new queries from starting. 115 | func (c *Client) Close() error { 116 | return c.driver.Close() 117 | } 118 | 119 | // Use adds the mutation hooks to all the entity clients. 120 | // In order to add hooks to a specific client, call: `client.Node.Use(...)`. 121 | func (c *Client) Use(hooks ...Hook) { 122 | c.Model.Use(hooks...) 123 | } 124 | 125 | // ModelClient is a client for the Model schema. 126 | type ModelClient struct { 127 | config 128 | } 129 | 130 | // NewModelClient returns a client for the Model from the given config. 131 | func NewModelClient(c config) *ModelClient { 132 | return &ModelClient{config: c} 133 | } 134 | 135 | // Use adds a list of mutation hooks to the hooks stack. 136 | // A call to `Use(f, g, h)` equals to `model.Hooks(f(g(h())))`. 137 | func (c *ModelClient) Use(hooks ...Hook) { 138 | c.hooks.Model = append(c.hooks.Model, hooks...) 139 | } 140 | 141 | // Create returns a create builder for Model. 142 | func (c *ModelClient) Create() *ModelCreate { 143 | mutation := newModelMutation(c.config, OpCreate) 144 | return &ModelCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} 145 | } 146 | 147 | // CreateBulk returns a builder for creating a bulk of Model entities. 148 | func (c *ModelClient) CreateBulk(builders ...*ModelCreate) *ModelCreateBulk { 149 | return &ModelCreateBulk{config: c.config, builders: builders} 150 | } 151 | 152 | // Update returns an update builder for Model. 153 | func (c *ModelClient) Update() *ModelUpdate { 154 | mutation := newModelMutation(c.config, OpUpdate) 155 | return &ModelUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} 156 | } 157 | 158 | // UpdateOne returns an update builder for the given entity. 159 | func (c *ModelClient) UpdateOne(m *Model) *ModelUpdateOne { 160 | mutation := newModelMutation(c.config, OpUpdateOne, withModel(m)) 161 | return &ModelUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} 162 | } 163 | 164 | // UpdateOneID returns an update builder for the given id. 165 | func (c *ModelClient) UpdateOneID(id int) *ModelUpdateOne { 166 | mutation := newModelMutation(c.config, OpUpdateOne, withModelID(id)) 167 | return &ModelUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} 168 | } 169 | 170 | // Delete returns a delete builder for Model. 171 | func (c *ModelClient) Delete() *ModelDelete { 172 | mutation := newModelMutation(c.config, OpDelete) 173 | return &ModelDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} 174 | } 175 | 176 | // DeleteOne returns a delete builder for the given entity. 177 | func (c *ModelClient) DeleteOne(m *Model) *ModelDeleteOne { 178 | return c.DeleteOneID(m.ID) 179 | } 180 | 181 | // DeleteOneID returns a delete builder for the given id. 182 | func (c *ModelClient) DeleteOneID(id int) *ModelDeleteOne { 183 | builder := c.Delete().Where(model.ID(id)) 184 | builder.mutation.id = &id 185 | builder.mutation.op = OpDeleteOne 186 | return &ModelDeleteOne{builder} 187 | } 188 | 189 | // Query returns a query builder for Model. 190 | func (c *ModelClient) Query() *ModelQuery { 191 | return &ModelQuery{ 192 | config: c.config, 193 | } 194 | } 195 | 196 | // Get returns a Model entity by its id. 197 | func (c *ModelClient) Get(ctx context.Context, id int) (*Model, error) { 198 | return c.Query().Where(model.ID(id)).Only(ctx) 199 | } 200 | 201 | // GetX is like Get, but panics if an error occurs. 202 | func (c *ModelClient) GetX(ctx context.Context, id int) *Model { 203 | obj, err := c.Get(ctx, id) 204 | if err != nil { 205 | panic(err) 206 | } 207 | return obj 208 | } 209 | 210 | // Hooks returns the client hooks. 211 | func (c *ModelClient) Hooks() []Hook { 212 | return c.hooks.Model 213 | } 214 | -------------------------------------------------------------------------------- /benchs/ent/ent.go: -------------------------------------------------------------------------------- 1 | // Code generated by entc, DO NOT EDIT. 2 | 3 | package ent 4 | 5 | import ( 6 | "errors" 7 | "fmt" 8 | 9 | "entgo.io/ent" 10 | "entgo.io/ent/dialect/sql" 11 | "github.com/efectn/go-orm-benchmarks/benchs/ent/model" 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 | model.Table: model.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 or edge 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 | -------------------------------------------------------------------------------- /benchs/ent/model_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 | "entgo.io/ent/dialect/sql/sqlgraph" 11 | "entgo.io/ent/schema/field" 12 | "github.com/efectn/go-orm-benchmarks/benchs/ent/model" 13 | ) 14 | 15 | // ModelCreate is the builder for creating a Model entity. 16 | type ModelCreate struct { 17 | config 18 | mutation *ModelMutation 19 | hooks []Hook 20 | } 21 | 22 | // SetName sets the "name" field. 23 | func (mc *ModelCreate) SetName(s string) *ModelCreate { 24 | mc.mutation.SetName(s) 25 | return mc 26 | } 27 | 28 | // SetTitle sets the "title" field. 29 | func (mc *ModelCreate) SetTitle(s string) *ModelCreate { 30 | mc.mutation.SetTitle(s) 31 | return mc 32 | } 33 | 34 | // SetFax sets the "fax" field. 35 | func (mc *ModelCreate) SetFax(s string) *ModelCreate { 36 | mc.mutation.SetFax(s) 37 | return mc 38 | } 39 | 40 | // SetWeb sets the "web" field. 41 | func (mc *ModelCreate) SetWeb(s string) *ModelCreate { 42 | mc.mutation.SetWeb(s) 43 | return mc 44 | } 45 | 46 | // SetAge sets the "age" field. 47 | func (mc *ModelCreate) SetAge(i int) *ModelCreate { 48 | mc.mutation.SetAge(i) 49 | return mc 50 | } 51 | 52 | // SetRight sets the "right" field. 53 | func (mc *ModelCreate) SetRight(b bool) *ModelCreate { 54 | mc.mutation.SetRight(b) 55 | return mc 56 | } 57 | 58 | // SetCounter sets the "counter" field. 59 | func (mc *ModelCreate) SetCounter(i int64) *ModelCreate { 60 | mc.mutation.SetCounter(i) 61 | return mc 62 | } 63 | 64 | // Mutation returns the ModelMutation object of the builder. 65 | func (mc *ModelCreate) Mutation() *ModelMutation { 66 | return mc.mutation 67 | } 68 | 69 | // Save creates the Model in the database. 70 | func (mc *ModelCreate) Save(ctx context.Context) (*Model, error) { 71 | var ( 72 | err error 73 | node *Model 74 | ) 75 | if len(mc.hooks) == 0 { 76 | if err = mc.check(); err != nil { 77 | return nil, err 78 | } 79 | node, err = mc.sqlSave(ctx) 80 | } else { 81 | var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { 82 | mutation, ok := m.(*ModelMutation) 83 | if !ok { 84 | return nil, fmt.Errorf("unexpected mutation type %T", m) 85 | } 86 | if err = mc.check(); err != nil { 87 | return nil, err 88 | } 89 | mc.mutation = mutation 90 | if node, err = mc.sqlSave(ctx); err != nil { 91 | return nil, err 92 | } 93 | mutation.id = &node.ID 94 | mutation.done = true 95 | return node, err 96 | }) 97 | for i := len(mc.hooks) - 1; i >= 0; i-- { 98 | if mc.hooks[i] == nil { 99 | return nil, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)") 100 | } 101 | mut = mc.hooks[i](mut) 102 | } 103 | if _, err := mut.Mutate(ctx, mc.mutation); err != nil { 104 | return nil, err 105 | } 106 | } 107 | return node, err 108 | } 109 | 110 | // SaveX calls Save and panics if Save returns an error. 111 | func (mc *ModelCreate) SaveX(ctx context.Context) *Model { 112 | v, err := mc.Save(ctx) 113 | if err != nil { 114 | panic(err) 115 | } 116 | return v 117 | } 118 | 119 | // Exec executes the query. 120 | func (mc *ModelCreate) Exec(ctx context.Context) error { 121 | _, err := mc.Save(ctx) 122 | return err 123 | } 124 | 125 | // ExecX is like Exec, but panics if an error occurs. 126 | func (mc *ModelCreate) ExecX(ctx context.Context) { 127 | if err := mc.Exec(ctx); err != nil { 128 | panic(err) 129 | } 130 | } 131 | 132 | // check runs all checks and user-defined validators on the builder. 133 | func (mc *ModelCreate) check() error { 134 | if _, ok := mc.mutation.Name(); !ok { 135 | return &ValidationError{Name: "name", err: errors.New(`ent: missing required field "Model.name"`)} 136 | } 137 | if _, ok := mc.mutation.Title(); !ok { 138 | return &ValidationError{Name: "title", err: errors.New(`ent: missing required field "Model.title"`)} 139 | } 140 | if _, ok := mc.mutation.Fax(); !ok { 141 | return &ValidationError{Name: "fax", err: errors.New(`ent: missing required field "Model.fax"`)} 142 | } 143 | if _, ok := mc.mutation.Web(); !ok { 144 | return &ValidationError{Name: "web", err: errors.New(`ent: missing required field "Model.web"`)} 145 | } 146 | if _, ok := mc.mutation.Age(); !ok { 147 | return &ValidationError{Name: "age", err: errors.New(`ent: missing required field "Model.age"`)} 148 | } 149 | if _, ok := mc.mutation.Right(); !ok { 150 | return &ValidationError{Name: "right", err: errors.New(`ent: missing required field "Model.right"`)} 151 | } 152 | if _, ok := mc.mutation.Counter(); !ok { 153 | return &ValidationError{Name: "counter", err: errors.New(`ent: missing required field "Model.counter"`)} 154 | } 155 | return nil 156 | } 157 | 158 | func (mc *ModelCreate) sqlSave(ctx context.Context) (*Model, error) { 159 | _node, _spec := mc.createSpec() 160 | if err := sqlgraph.CreateNode(ctx, mc.driver, _spec); err != nil { 161 | if sqlgraph.IsConstraintError(err) { 162 | err = &ConstraintError{err.Error(), err} 163 | } 164 | return nil, err 165 | } 166 | id := _spec.ID.Value.(int64) 167 | _node.ID = int(id) 168 | return _node, nil 169 | } 170 | 171 | func (mc *ModelCreate) createSpec() (*Model, *sqlgraph.CreateSpec) { 172 | var ( 173 | _node = &Model{config: mc.config} 174 | _spec = &sqlgraph.CreateSpec{ 175 | Table: model.Table, 176 | ID: &sqlgraph.FieldSpec{ 177 | Type: field.TypeInt, 178 | Column: model.FieldID, 179 | }, 180 | } 181 | ) 182 | if value, ok := mc.mutation.Name(); ok { 183 | _spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{ 184 | Type: field.TypeString, 185 | Value: value, 186 | Column: model.FieldName, 187 | }) 188 | _node.Name = value 189 | } 190 | if value, ok := mc.mutation.Title(); ok { 191 | _spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{ 192 | Type: field.TypeString, 193 | Value: value, 194 | Column: model.FieldTitle, 195 | }) 196 | _node.Title = value 197 | } 198 | if value, ok := mc.mutation.Fax(); ok { 199 | _spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{ 200 | Type: field.TypeString, 201 | Value: value, 202 | Column: model.FieldFax, 203 | }) 204 | _node.Fax = value 205 | } 206 | if value, ok := mc.mutation.Web(); ok { 207 | _spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{ 208 | Type: field.TypeString, 209 | Value: value, 210 | Column: model.FieldWeb, 211 | }) 212 | _node.Web = value 213 | } 214 | if value, ok := mc.mutation.Age(); ok { 215 | _spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{ 216 | Type: field.TypeInt, 217 | Value: value, 218 | Column: model.FieldAge, 219 | }) 220 | _node.Age = value 221 | } 222 | if value, ok := mc.mutation.Right(); ok { 223 | _spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{ 224 | Type: field.TypeBool, 225 | Value: value, 226 | Column: model.FieldRight, 227 | }) 228 | _node.Right = value 229 | } 230 | if value, ok := mc.mutation.Counter(); ok { 231 | _spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{ 232 | Type: field.TypeInt64, 233 | Value: value, 234 | Column: model.FieldCounter, 235 | }) 236 | _node.Counter = value 237 | } 238 | return _node, _spec 239 | } 240 | 241 | // ModelCreateBulk is the builder for creating many Model entities in bulk. 242 | type ModelCreateBulk struct { 243 | config 244 | builders []*ModelCreate 245 | } 246 | 247 | // Save creates the Model entities in the database. 248 | func (mcb *ModelCreateBulk) Save(ctx context.Context) ([]*Model, error) { 249 | specs := make([]*sqlgraph.CreateSpec, len(mcb.builders)) 250 | nodes := make([]*Model, len(mcb.builders)) 251 | mutators := make([]Mutator, len(mcb.builders)) 252 | for i := range mcb.builders { 253 | func(i int, root context.Context) { 254 | builder := mcb.builders[i] 255 | var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { 256 | mutation, ok := m.(*ModelMutation) 257 | if !ok { 258 | return nil, fmt.Errorf("unexpected mutation type %T", m) 259 | } 260 | if err := builder.check(); err != nil { 261 | return nil, err 262 | } 263 | builder.mutation = mutation 264 | nodes[i], specs[i] = builder.createSpec() 265 | var err error 266 | if i < len(mutators)-1 { 267 | _, err = mutators[i+1].Mutate(root, mcb.builders[i+1].mutation) 268 | } else { 269 | spec := &sqlgraph.BatchCreateSpec{Nodes: specs} 270 | // Invoke the actual operation on the latest mutation in the chain. 271 | if err = sqlgraph.BatchCreate(ctx, mcb.driver, spec); err != nil { 272 | if sqlgraph.IsConstraintError(err) { 273 | err = &ConstraintError{err.Error(), err} 274 | } 275 | } 276 | } 277 | if err != nil { 278 | return nil, err 279 | } 280 | mutation.id = &nodes[i].ID 281 | mutation.done = true 282 | if specs[i].ID.Value != nil { 283 | id := specs[i].ID.Value.(int64) 284 | nodes[i].ID = int(id) 285 | } 286 | return nodes[i], nil 287 | }) 288 | for i := len(builder.hooks) - 1; i >= 0; i-- { 289 | mut = builder.hooks[i](mut) 290 | } 291 | mutators[i] = mut 292 | }(i, ctx) 293 | } 294 | if len(mutators) > 0 { 295 | if _, err := mutators[0].Mutate(ctx, mcb.builders[0].mutation); err != nil { 296 | return nil, err 297 | } 298 | } 299 | return nodes, nil 300 | } 301 | 302 | // SaveX is like Save, but panics if an error occurs. 303 | func (mcb *ModelCreateBulk) SaveX(ctx context.Context) []*Model { 304 | v, err := mcb.Save(ctx) 305 | if err != nil { 306 | panic(err) 307 | } 308 | return v 309 | } 310 | 311 | // Exec executes the query. 312 | func (mcb *ModelCreateBulk) Exec(ctx context.Context) error { 313 | _, err := mcb.Save(ctx) 314 | return err 315 | } 316 | 317 | // ExecX is like Exec, but panics if an error occurs. 318 | func (mcb *ModelCreateBulk) ExecX(ctx context.Context) { 319 | if err := mcb.Exec(ctx); err != nil { 320 | panic(err) 321 | } 322 | } 323 | -------------------------------------------------------------------------------- /benchs/generated.go: -------------------------------------------------------------------------------- 1 | // Code generated by sqlgen, version v1.0.0-alpha.5; DO NOT EDIT. 2 | 3 | package benchs 4 | 5 | import ( 6 | "database/sql/driver" 7 | 8 | "github.com/si3nloong/sqlgen/sequel" 9 | "github.com/si3nloong/sqlgen/sequel/types" 10 | ) 11 | 12 | func (Model) TableName() string { 13 | return "`model`" 14 | } 15 | func (Model) HasPK() {} 16 | func (Model) IsAutoIncr() {} 17 | func (v *Model) ScanAutoIncr(val int64) error { 18 | v.Id = int(val) 19 | return nil 20 | } 21 | func (v Model) PK() (string, int, any) { 22 | return "`id`", 0, (int64)(v.Id) 23 | } 24 | func (Model) Columns() []string { 25 | return []string{"`id`", "`name`", "`title`", "`fax`", "`web`", "`age`", "`right`", "`counter`"} 26 | } 27 | func (v Model) Values() []any { 28 | return []any{(int64)(v.Id), (string)(v.Name), (string)(v.Title), (string)(v.Fax), (string)(v.Web), (int64)(v.Age), (bool)(v.Right), (int64)(v.Counter)} 29 | } 30 | func (v *Model) Addrs() []any { 31 | return []any{types.Integer(&v.Id), types.String(&v.Name), types.String(&v.Title), types.String(&v.Fax), types.String(&v.Web), types.Integer(&v.Age), types.Bool(&v.Right), types.Integer(&v.Counter)} 32 | } 33 | func (Model) InsertPlaceholders(row int) string { 34 | return "(?,?,?,?,?,?,?)" 35 | } 36 | func (v Model) InsertOneStmt() (string, []any) { 37 | return "INSERT INTO `model` (`name`,`title`,`fax`,`web`,`age`,`right`,`counter`) VALUES (?,?,?,?,?,?,?);", []any{(string)(v.Name), (string)(v.Title), (string)(v.Fax), (string)(v.Web), (int64)(v.Age), (bool)(v.Right), (int64)(v.Counter)} 38 | } 39 | func (v Model) FindOneByPKStmt() (string, []any) { 40 | return "SELECT `id`,`name`,`title`,`fax`,`web`,`age`,`right`,`counter` FROM `model` WHERE `id` = ? LIMIT 1;", []any{(int64)(v.Id)} 41 | } 42 | func (v Model) UpdateOneByPKStmt() (string, []any) { 43 | return "UPDATE `model` SET `name` = ?,`title` = ?,`fax` = ?,`web` = ?,`age` = ?,`right` = ?,`counter` = ? WHERE `id` = ?;", []any{(string)(v.Name), (string)(v.Title), (string)(v.Fax), (string)(v.Web), (int64)(v.Age), (bool)(v.Right), (int64)(v.Counter), (int64)(v.Id)} 44 | } 45 | func (v Model) GetId() sequel.ColumnValuer[int] { 46 | return sequel.Column("`id`", v.Id, func(val int) driver.Value { return (int64)(val) }) 47 | } 48 | func (v Model) GetName() sequel.ColumnValuer[string] { 49 | return sequel.Column("`name`", v.Name, func(val string) driver.Value { return (string)(val) }) 50 | } 51 | func (v Model) GetTitle() sequel.ColumnValuer[string] { 52 | return sequel.Column("`title`", v.Title, func(val string) driver.Value { return (string)(val) }) 53 | } 54 | func (v Model) GetFax() sequel.ColumnValuer[string] { 55 | return sequel.Column("`fax`", v.Fax, func(val string) driver.Value { return (string)(val) }) 56 | } 57 | func (v Model) GetWeb() sequel.ColumnValuer[string] { 58 | return sequel.Column("`web`", v.Web, func(val string) driver.Value { return (string)(val) }) 59 | } 60 | func (v Model) GetAge() sequel.ColumnValuer[int] { 61 | return sequel.Column("`age`", v.Age, func(val int) driver.Value { return (int64)(val) }) 62 | } 63 | func (v Model) GetRight() sequel.ColumnValuer[bool] { 64 | return sequel.Column("`right`", v.Right, func(val bool) driver.Value { return (bool)(val) }) 65 | } 66 | func (v Model) GetCounter() sequel.ColumnValuer[int64] { 67 | return sequel.Column("`counter`", v.Counter, func(val int64) driver.Value { return (int64)(val) }) 68 | } 69 | 70 | func (Model2) Columns() []string { 71 | return []string{"`id`", "`name`", "`title`", "`fax`", "`web`", "`age`", "`right`", "`counter`"} 72 | } 73 | func (v Model2) Values() []any { 74 | return []any{(int64)(v.ID), (string)(v.Name), (string)(v.Title), (string)(v.Fax), (string)(v.Web), (int64)(v.Age), (bool)(v.Right), (int64)(v.Counter)} 75 | } 76 | func (v *Model2) Addrs() []any { 77 | return []any{types.Integer(&v.ID), types.String(&v.Name), types.String(&v.Title), types.String(&v.Fax), types.String(&v.Web), types.Integer(&v.Age), types.Bool(&v.Right), types.Integer(&v.Counter)} 78 | } 79 | func (Model2) InsertPlaceholders(row int) string { 80 | return "(?,?,?,?,?,?,?,?)" 81 | } 82 | func (v Model2) InsertOneStmt() (string, []any) { 83 | return "INSERT INTO " + v.TableName() + " (`id`,`name`,`title`,`fax`,`web`,`age`,`right`,`counter`) VALUES (?,?,?,?,?,?,?,?);", v.Values() 84 | } 85 | func (v Model2) GetID() sequel.ColumnValuer[int] { 86 | return sequel.Column("`id`", v.ID, func(val int) driver.Value { return (int64)(val) }) 87 | } 88 | func (v Model2) GetName() sequel.ColumnValuer[string] { 89 | return sequel.Column("`name`", v.Name, func(val string) driver.Value { return (string)(val) }) 90 | } 91 | func (v Model2) GetTitle() sequel.ColumnValuer[string] { 92 | return sequel.Column("`title`", v.Title, func(val string) driver.Value { return (string)(val) }) 93 | } 94 | func (v Model2) GetFax() sequel.ColumnValuer[string] { 95 | return sequel.Column("`fax`", v.Fax, func(val string) driver.Value { return (string)(val) }) 96 | } 97 | func (v Model2) GetWeb() sequel.ColumnValuer[string] { 98 | return sequel.Column("`web`", v.Web, func(val string) driver.Value { return (string)(val) }) 99 | } 100 | func (v Model2) GetAge() sequel.ColumnValuer[int] { 101 | return sequel.Column("`age`", v.Age, func(val int) driver.Value { return (int64)(val) }) 102 | } 103 | func (v Model2) GetRight() sequel.ColumnValuer[bool] { 104 | return sequel.Column("`right`", v.Right, func(val bool) driver.Value { return (bool)(val) }) 105 | } 106 | func (v Model2) GetCounter() sequel.ColumnValuer[int64] { 107 | return sequel.Column("`counter`", v.Counter, func(val int64) driver.Value { return (int64)(val) }) 108 | } 109 | 110 | func (Model3) Columns() []string { 111 | return []string{"`id`", "`name`", "`title`", "`fax`", "`web`", "`age`", "`right`", "`counter`"} 112 | } 113 | func (v Model3) Values() []any { 114 | return []any{(int64)(v.ID), (string)(v.Name), (string)(v.Title), (string)(v.Fax), (string)(v.Web), (int64)(v.Age), (bool)(v.Right), (int64)(v.Counter)} 115 | } 116 | func (v *Model3) Addrs() []any { 117 | return []any{types.Integer(&v.ID), types.String(&v.Name), types.String(&v.Title), types.String(&v.Fax), types.String(&v.Web), types.Integer(&v.Age), types.Bool(&v.Right), types.Integer(&v.Counter)} 118 | } 119 | func (Model3) InsertPlaceholders(row int) string { 120 | return "(?,?,?,?,?,?,?,?)" 121 | } 122 | func (v Model3) InsertOneStmt() (string, []any) { 123 | return "INSERT INTO " + v.TableName() + " (`id`,`name`,`title`,`fax`,`web`,`age`,`right`,`counter`) VALUES (?,?,?,?,?,?,?,?);", v.Values() 124 | } 125 | func (v Model3) GetID() sequel.ColumnValuer[int] { 126 | return sequel.Column("`id`", v.ID, func(val int) driver.Value { return (int64)(val) }) 127 | } 128 | func (v Model3) GetName() sequel.ColumnValuer[string] { 129 | return sequel.Column("`name`", v.Name, func(val string) driver.Value { return (string)(val) }) 130 | } 131 | func (v Model3) GetTitle() sequel.ColumnValuer[string] { 132 | return sequel.Column("`title`", v.Title, func(val string) driver.Value { return (string)(val) }) 133 | } 134 | func (v Model3) GetFax() sequel.ColumnValuer[string] { 135 | return sequel.Column("`fax`", v.Fax, func(val string) driver.Value { return (string)(val) }) 136 | } 137 | func (v Model3) GetWeb() sequel.ColumnValuer[string] { 138 | return sequel.Column("`web`", v.Web, func(val string) driver.Value { return (string)(val) }) 139 | } 140 | func (v Model3) GetAge() sequel.ColumnValuer[int] { 141 | return sequel.Column("`age`", v.Age, func(val int) driver.Value { return (int64)(val) }) 142 | } 143 | func (v Model3) GetRight() sequel.ColumnValuer[bool] { 144 | return sequel.Column("`right`", v.Right, func(val bool) driver.Value { return (bool)(val) }) 145 | } 146 | func (v Model3) GetCounter() sequel.ColumnValuer[int64] { 147 | return sequel.Column("`counter`", v.Counter, func(val int64) driver.Value { return (int64)(val) }) 148 | } 149 | 150 | func (Model4) TableName() string { 151 | return "`model_4`" 152 | } 153 | func (Model4) Columns() []string { 154 | return []string{"`id`", "`name`", "`title`", "`fax`", "`web`", "`age`", "`right`", "`counter`"} 155 | } 156 | func (v Model4) Values() []any { 157 | return []any{(int64)(v.ID), (string)(v.Name), (string)(v.Title), (string)(v.Fax), (string)(v.Web), (int64)(v.Age), (bool)(v.Right), (int64)(v.Counter)} 158 | } 159 | func (v *Model4) Addrs() []any { 160 | return []any{types.Integer(&v.ID), types.String(&v.Name), types.String(&v.Title), types.String(&v.Fax), types.String(&v.Web), types.Integer(&v.Age), types.Bool(&v.Right), types.Integer(&v.Counter)} 161 | } 162 | func (Model4) InsertPlaceholders(row int) string { 163 | return "(?,?,?,?,?,?,?,?)" 164 | } 165 | func (v Model4) InsertOneStmt() (string, []any) { 166 | return "INSERT INTO `model_4` (`id`,`name`,`title`,`fax`,`web`,`age`,`right`,`counter`) VALUES (?,?,?,?,?,?,?,?);", v.Values() 167 | } 168 | func (v Model4) GetID() sequel.ColumnValuer[int] { 169 | return sequel.Column("`id`", v.ID, func(val int) driver.Value { return (int64)(val) }) 170 | } 171 | func (v Model4) GetName() sequel.ColumnValuer[string] { 172 | return sequel.Column("`name`", v.Name, func(val string) driver.Value { return (string)(val) }) 173 | } 174 | func (v Model4) GetTitle() sequel.ColumnValuer[string] { 175 | return sequel.Column("`title`", v.Title, func(val string) driver.Value { return (string)(val) }) 176 | } 177 | func (v Model4) GetFax() sequel.ColumnValuer[string] { 178 | return sequel.Column("`fax`", v.Fax, func(val string) driver.Value { return (string)(val) }) 179 | } 180 | func (v Model4) GetWeb() sequel.ColumnValuer[string] { 181 | return sequel.Column("`web`", v.Web, func(val string) driver.Value { return (string)(val) }) 182 | } 183 | func (v Model4) GetAge() sequel.ColumnValuer[int] { 184 | return sequel.Column("`age`", v.Age, func(val int) driver.Value { return (int64)(val) }) 185 | } 186 | func (v Model4) GetRight() sequel.ColumnValuer[bool] { 187 | return sequel.Column("`right`", v.Right, func(val bool) driver.Value { return (bool)(val) }) 188 | } 189 | func (v Model4) GetCounter() sequel.ColumnValuer[int64] { 190 | return sequel.Column("`counter`", v.Counter, func(val int64) driver.Value { return (int64)(val) }) 191 | } 192 | 193 | func (Model5) TableName() string { 194 | return "`model_5`" 195 | } 196 | func (Model5) Columns() []string { 197 | return []string{"`id`", "`name`", "`title`", "`fax`", "`web`", "`age`", "`right`", "`counter`"} 198 | } 199 | func (v Model5) Values() []any { 200 | return []any{(int64)(v.Id), (string)(v.Name), (string)(v.Title), (string)(v.Fax), (string)(v.Web), (int64)(v.Age), (bool)(v.Right), (int64)(v.Counter)} 201 | } 202 | func (v *Model5) Addrs() []any { 203 | return []any{types.Integer(&v.Id), types.String(&v.Name), types.String(&v.Title), types.String(&v.Fax), types.String(&v.Web), types.Integer(&v.Age), types.Bool(&v.Right), types.Integer(&v.Counter)} 204 | } 205 | func (Model5) InsertPlaceholders(row int) string { 206 | return "(?,?,?,?,?,?,?,?)" 207 | } 208 | func (v Model5) InsertOneStmt() (string, []any) { 209 | return "INSERT INTO `model_5` (`id`,`name`,`title`,`fax`,`web`,`age`,`right`,`counter`) VALUES (?,?,?,?,?,?,?,?);", v.Values() 210 | } 211 | func (v Model5) GetId() sequel.ColumnValuer[int64] { 212 | return sequel.Column("`id`", v.Id, func(val int64) driver.Value { return (int64)(val) }) 213 | } 214 | func (v Model5) GetName() sequel.ColumnValuer[string] { 215 | return sequel.Column("`name`", v.Name, func(val string) driver.Value { return (string)(val) }) 216 | } 217 | func (v Model5) GetTitle() sequel.ColumnValuer[string] { 218 | return sequel.Column("`title`", v.Title, func(val string) driver.Value { return (string)(val) }) 219 | } 220 | func (v Model5) GetFax() sequel.ColumnValuer[string] { 221 | return sequel.Column("`fax`", v.Fax, func(val string) driver.Value { return (string)(val) }) 222 | } 223 | func (v Model5) GetWeb() sequel.ColumnValuer[string] { 224 | return sequel.Column("`web`", v.Web, func(val string) driver.Value { return (string)(val) }) 225 | } 226 | func (v Model5) GetAge() sequel.ColumnValuer[int] { 227 | return sequel.Column("`age`", v.Age, func(val int) driver.Value { return (int64)(val) }) 228 | } 229 | func (v Model5) GetRight() sequel.ColumnValuer[bool] { 230 | return sequel.Column("`right`", v.Right, func(val bool) driver.Value { return (bool)(val) }) 231 | } 232 | func (v Model5) GetCounter() sequel.ColumnValuer[int64] { 233 | return sequel.Column("`counter`", v.Counter, func(val int64) driver.Value { return (int64)(val) }) 234 | } 235 | 236 | func (Model7) TableName() string { 237 | return "`model_7`" 238 | } 239 | func (Model7) Columns() []string { 240 | return []string{"`id`", "`name`", "`title`", "`fax`", "`web`", "`age`", "`right`", "`counter`"} 241 | } 242 | func (v Model7) Values() []any { 243 | return []any{(int64)(v.ID), (string)(v.Name), (string)(v.Title), (string)(v.Fax), (string)(v.Web), (int64)(v.Age), (bool)(v.Right), (int64)(v.Counter)} 244 | } 245 | func (v *Model7) Addrs() []any { 246 | return []any{types.Integer(&v.ID), types.String(&v.Name), types.String(&v.Title), types.String(&v.Fax), types.String(&v.Web), types.Integer(&v.Age), types.Bool(&v.Right), types.Integer(&v.Counter)} 247 | } 248 | func (Model7) InsertPlaceholders(row int) string { 249 | return "(?,?,?,?,?,?,?,?)" 250 | } 251 | func (v Model7) InsertOneStmt() (string, []any) { 252 | return "INSERT INTO `model_7` (`id`,`name`,`title`,`fax`,`web`,`age`,`right`,`counter`) VALUES (?,?,?,?,?,?,?,?);", v.Values() 253 | } 254 | func (v Model7) GetID() sequel.ColumnValuer[int] { 255 | return sequel.Column("`id`", v.ID, func(val int) driver.Value { return (int64)(val) }) 256 | } 257 | func (v Model7) GetName() sequel.ColumnValuer[string] { 258 | return sequel.Column("`name`", v.Name, func(val string) driver.Value { return (string)(val) }) 259 | } 260 | func (v Model7) GetTitle() sequel.ColumnValuer[string] { 261 | return sequel.Column("`title`", v.Title, func(val string) driver.Value { return (string)(val) }) 262 | } 263 | func (v Model7) GetFax() sequel.ColumnValuer[string] { 264 | return sequel.Column("`fax`", v.Fax, func(val string) driver.Value { return (string)(val) }) 265 | } 266 | func (v Model7) GetWeb() sequel.ColumnValuer[string] { 267 | return sequel.Column("`web`", v.Web, func(val string) driver.Value { return (string)(val) }) 268 | } 269 | func (v Model7) GetAge() sequel.ColumnValuer[int] { 270 | return sequel.Column("`age`", v.Age, func(val int) driver.Value { return (int64)(val) }) 271 | } 272 | func (v Model7) GetRight() sequel.ColumnValuer[bool] { 273 | return sequel.Column("`right`", v.Right, func(val bool) driver.Value { return (bool)(val) }) 274 | } 275 | func (v Model7) GetCounter() sequel.ColumnValuer[int64] { 276 | return sequel.Column("`counter`", v.Counter, func(val int64) driver.Value { return (int64)(val) }) 277 | } 278 | -------------------------------------------------------------------------------- /benchs/ent/model_update.go: -------------------------------------------------------------------------------- 1 | // Code generated by entc, DO NOT EDIT. 2 | 3 | package ent 4 | 5 | import ( 6 | "context" 7 | "errors" 8 | "fmt" 9 | 10 | "entgo.io/ent/dialect/sql" 11 | "entgo.io/ent/dialect/sql/sqlgraph" 12 | "entgo.io/ent/schema/field" 13 | "github.com/efectn/go-orm-benchmarks/benchs/ent/model" 14 | "github.com/efectn/go-orm-benchmarks/benchs/ent/predicate" 15 | ) 16 | 17 | // ModelUpdate is the builder for updating Model entities. 18 | type ModelUpdate struct { 19 | config 20 | hooks []Hook 21 | mutation *ModelMutation 22 | } 23 | 24 | // Where appends a list predicates to the ModelUpdate builder. 25 | func (mu *ModelUpdate) Where(ps ...predicate.Model) *ModelUpdate { 26 | mu.mutation.Where(ps...) 27 | return mu 28 | } 29 | 30 | // SetName sets the "name" field. 31 | func (mu *ModelUpdate) SetName(s string) *ModelUpdate { 32 | mu.mutation.SetName(s) 33 | return mu 34 | } 35 | 36 | // SetTitle sets the "title" field. 37 | func (mu *ModelUpdate) SetTitle(s string) *ModelUpdate { 38 | mu.mutation.SetTitle(s) 39 | return mu 40 | } 41 | 42 | // SetFax sets the "fax" field. 43 | func (mu *ModelUpdate) SetFax(s string) *ModelUpdate { 44 | mu.mutation.SetFax(s) 45 | return mu 46 | } 47 | 48 | // SetWeb sets the "web" field. 49 | func (mu *ModelUpdate) SetWeb(s string) *ModelUpdate { 50 | mu.mutation.SetWeb(s) 51 | return mu 52 | } 53 | 54 | // SetAge sets the "age" field. 55 | func (mu *ModelUpdate) SetAge(i int) *ModelUpdate { 56 | mu.mutation.ResetAge() 57 | mu.mutation.SetAge(i) 58 | return mu 59 | } 60 | 61 | // AddAge adds i to the "age" field. 62 | func (mu *ModelUpdate) AddAge(i int) *ModelUpdate { 63 | mu.mutation.AddAge(i) 64 | return mu 65 | } 66 | 67 | // SetRight sets the "right" field. 68 | func (mu *ModelUpdate) SetRight(b bool) *ModelUpdate { 69 | mu.mutation.SetRight(b) 70 | return mu 71 | } 72 | 73 | // SetCounter sets the "counter" field. 74 | func (mu *ModelUpdate) SetCounter(i int64) *ModelUpdate { 75 | mu.mutation.ResetCounter() 76 | mu.mutation.SetCounter(i) 77 | return mu 78 | } 79 | 80 | // AddCounter adds i to the "counter" field. 81 | func (mu *ModelUpdate) AddCounter(i int64) *ModelUpdate { 82 | mu.mutation.AddCounter(i) 83 | return mu 84 | } 85 | 86 | // Mutation returns the ModelMutation object of the builder. 87 | func (mu *ModelUpdate) Mutation() *ModelMutation { 88 | return mu.mutation 89 | } 90 | 91 | // Save executes the query and returns the number of nodes affected by the update operation. 92 | func (mu *ModelUpdate) Save(ctx context.Context) (int, error) { 93 | var ( 94 | err error 95 | affected int 96 | ) 97 | if len(mu.hooks) == 0 { 98 | affected, err = mu.sqlSave(ctx) 99 | } else { 100 | var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { 101 | mutation, ok := m.(*ModelMutation) 102 | if !ok { 103 | return nil, fmt.Errorf("unexpected mutation type %T", m) 104 | } 105 | mu.mutation = mutation 106 | affected, err = mu.sqlSave(ctx) 107 | mutation.done = true 108 | return affected, err 109 | }) 110 | for i := len(mu.hooks) - 1; i >= 0; i-- { 111 | if mu.hooks[i] == nil { 112 | return 0, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)") 113 | } 114 | mut = mu.hooks[i](mut) 115 | } 116 | if _, err := mut.Mutate(ctx, mu.mutation); err != nil { 117 | return 0, err 118 | } 119 | } 120 | return affected, err 121 | } 122 | 123 | // SaveX is like Save, but panics if an error occurs. 124 | func (mu *ModelUpdate) SaveX(ctx context.Context) int { 125 | affected, err := mu.Save(ctx) 126 | if err != nil { 127 | panic(err) 128 | } 129 | return affected 130 | } 131 | 132 | // Exec executes the query. 133 | func (mu *ModelUpdate) Exec(ctx context.Context) error { 134 | _, err := mu.Save(ctx) 135 | return err 136 | } 137 | 138 | // ExecX is like Exec, but panics if an error occurs. 139 | func (mu *ModelUpdate) ExecX(ctx context.Context) { 140 | if err := mu.Exec(ctx); err != nil { 141 | panic(err) 142 | } 143 | } 144 | 145 | func (mu *ModelUpdate) sqlSave(ctx context.Context) (n int, err error) { 146 | _spec := &sqlgraph.UpdateSpec{ 147 | Node: &sqlgraph.NodeSpec{ 148 | Table: model.Table, 149 | Columns: model.Columns, 150 | ID: &sqlgraph.FieldSpec{ 151 | Type: field.TypeInt, 152 | Column: model.FieldID, 153 | }, 154 | }, 155 | } 156 | if ps := mu.mutation.predicates; len(ps) > 0 { 157 | _spec.Predicate = func(selector *sql.Selector) { 158 | for i := range ps { 159 | ps[i](selector) 160 | } 161 | } 162 | } 163 | if value, ok := mu.mutation.Name(); ok { 164 | _spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{ 165 | Type: field.TypeString, 166 | Value: value, 167 | Column: model.FieldName, 168 | }) 169 | } 170 | if value, ok := mu.mutation.Title(); ok { 171 | _spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{ 172 | Type: field.TypeString, 173 | Value: value, 174 | Column: model.FieldTitle, 175 | }) 176 | } 177 | if value, ok := mu.mutation.Fax(); ok { 178 | _spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{ 179 | Type: field.TypeString, 180 | Value: value, 181 | Column: model.FieldFax, 182 | }) 183 | } 184 | if value, ok := mu.mutation.Web(); ok { 185 | _spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{ 186 | Type: field.TypeString, 187 | Value: value, 188 | Column: model.FieldWeb, 189 | }) 190 | } 191 | if value, ok := mu.mutation.Age(); ok { 192 | _spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{ 193 | Type: field.TypeInt, 194 | Value: value, 195 | Column: model.FieldAge, 196 | }) 197 | } 198 | if value, ok := mu.mutation.AddedAge(); ok { 199 | _spec.Fields.Add = append(_spec.Fields.Add, &sqlgraph.FieldSpec{ 200 | Type: field.TypeInt, 201 | Value: value, 202 | Column: model.FieldAge, 203 | }) 204 | } 205 | if value, ok := mu.mutation.Right(); ok { 206 | _spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{ 207 | Type: field.TypeBool, 208 | Value: value, 209 | Column: model.FieldRight, 210 | }) 211 | } 212 | if value, ok := mu.mutation.Counter(); ok { 213 | _spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{ 214 | Type: field.TypeInt64, 215 | Value: value, 216 | Column: model.FieldCounter, 217 | }) 218 | } 219 | if value, ok := mu.mutation.AddedCounter(); ok { 220 | _spec.Fields.Add = append(_spec.Fields.Add, &sqlgraph.FieldSpec{ 221 | Type: field.TypeInt64, 222 | Value: value, 223 | Column: model.FieldCounter, 224 | }) 225 | } 226 | if n, err = sqlgraph.UpdateNodes(ctx, mu.driver, _spec); err != nil { 227 | if _, ok := err.(*sqlgraph.NotFoundError); ok { 228 | err = &NotFoundError{model.Label} 229 | } else if sqlgraph.IsConstraintError(err) { 230 | err = &ConstraintError{err.Error(), err} 231 | } 232 | return 0, err 233 | } 234 | return n, nil 235 | } 236 | 237 | // ModelUpdateOne is the builder for updating a single Model entity. 238 | type ModelUpdateOne struct { 239 | config 240 | fields []string 241 | hooks []Hook 242 | mutation *ModelMutation 243 | } 244 | 245 | // SetName sets the "name" field. 246 | func (muo *ModelUpdateOne) SetName(s string) *ModelUpdateOne { 247 | muo.mutation.SetName(s) 248 | return muo 249 | } 250 | 251 | // SetTitle sets the "title" field. 252 | func (muo *ModelUpdateOne) SetTitle(s string) *ModelUpdateOne { 253 | muo.mutation.SetTitle(s) 254 | return muo 255 | } 256 | 257 | // SetFax sets the "fax" field. 258 | func (muo *ModelUpdateOne) SetFax(s string) *ModelUpdateOne { 259 | muo.mutation.SetFax(s) 260 | return muo 261 | } 262 | 263 | // SetWeb sets the "web" field. 264 | func (muo *ModelUpdateOne) SetWeb(s string) *ModelUpdateOne { 265 | muo.mutation.SetWeb(s) 266 | return muo 267 | } 268 | 269 | // SetAge sets the "age" field. 270 | func (muo *ModelUpdateOne) SetAge(i int) *ModelUpdateOne { 271 | muo.mutation.ResetAge() 272 | muo.mutation.SetAge(i) 273 | return muo 274 | } 275 | 276 | // AddAge adds i to the "age" field. 277 | func (muo *ModelUpdateOne) AddAge(i int) *ModelUpdateOne { 278 | muo.mutation.AddAge(i) 279 | return muo 280 | } 281 | 282 | // SetRight sets the "right" field. 283 | func (muo *ModelUpdateOne) SetRight(b bool) *ModelUpdateOne { 284 | muo.mutation.SetRight(b) 285 | return muo 286 | } 287 | 288 | // SetCounter sets the "counter" field. 289 | func (muo *ModelUpdateOne) SetCounter(i int64) *ModelUpdateOne { 290 | muo.mutation.ResetCounter() 291 | muo.mutation.SetCounter(i) 292 | return muo 293 | } 294 | 295 | // AddCounter adds i to the "counter" field. 296 | func (muo *ModelUpdateOne) AddCounter(i int64) *ModelUpdateOne { 297 | muo.mutation.AddCounter(i) 298 | return muo 299 | } 300 | 301 | // Mutation returns the ModelMutation object of the builder. 302 | func (muo *ModelUpdateOne) Mutation() *ModelMutation { 303 | return muo.mutation 304 | } 305 | 306 | // Select allows selecting one or more fields (columns) of the returned entity. 307 | // The default is selecting all fields defined in the entity schema. 308 | func (muo *ModelUpdateOne) Select(field string, fields ...string) *ModelUpdateOne { 309 | muo.fields = append([]string{field}, fields...) 310 | return muo 311 | } 312 | 313 | // Save executes the query and returns the updated Model entity. 314 | func (muo *ModelUpdateOne) Save(ctx context.Context) (*Model, error) { 315 | var ( 316 | err error 317 | node *Model 318 | ) 319 | if len(muo.hooks) == 0 { 320 | node, err = muo.sqlSave(ctx) 321 | } else { 322 | var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { 323 | mutation, ok := m.(*ModelMutation) 324 | if !ok { 325 | return nil, fmt.Errorf("unexpected mutation type %T", m) 326 | } 327 | muo.mutation = mutation 328 | node, err = muo.sqlSave(ctx) 329 | mutation.done = true 330 | return node, err 331 | }) 332 | for i := len(muo.hooks) - 1; i >= 0; i-- { 333 | if muo.hooks[i] == nil { 334 | return nil, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)") 335 | } 336 | mut = muo.hooks[i](mut) 337 | } 338 | if _, err := mut.Mutate(ctx, muo.mutation); err != nil { 339 | return nil, err 340 | } 341 | } 342 | return node, err 343 | } 344 | 345 | // SaveX is like Save, but panics if an error occurs. 346 | func (muo *ModelUpdateOne) SaveX(ctx context.Context) *Model { 347 | node, err := muo.Save(ctx) 348 | if err != nil { 349 | panic(err) 350 | } 351 | return node 352 | } 353 | 354 | // Exec executes the query on the entity. 355 | func (muo *ModelUpdateOne) Exec(ctx context.Context) error { 356 | _, err := muo.Save(ctx) 357 | return err 358 | } 359 | 360 | // ExecX is like Exec, but panics if an error occurs. 361 | func (muo *ModelUpdateOne) ExecX(ctx context.Context) { 362 | if err := muo.Exec(ctx); err != nil { 363 | panic(err) 364 | } 365 | } 366 | 367 | func (muo *ModelUpdateOne) sqlSave(ctx context.Context) (_node *Model, err error) { 368 | _spec := &sqlgraph.UpdateSpec{ 369 | Node: &sqlgraph.NodeSpec{ 370 | Table: model.Table, 371 | Columns: model.Columns, 372 | ID: &sqlgraph.FieldSpec{ 373 | Type: field.TypeInt, 374 | Column: model.FieldID, 375 | }, 376 | }, 377 | } 378 | id, ok := muo.mutation.ID() 379 | if !ok { 380 | return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "Model.id" for update`)} 381 | } 382 | _spec.Node.ID.Value = id 383 | if fields := muo.fields; len(fields) > 0 { 384 | _spec.Node.Columns = make([]string, 0, len(fields)) 385 | _spec.Node.Columns = append(_spec.Node.Columns, model.FieldID) 386 | for _, f := range fields { 387 | if !model.ValidColumn(f) { 388 | return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} 389 | } 390 | if f != model.FieldID { 391 | _spec.Node.Columns = append(_spec.Node.Columns, f) 392 | } 393 | } 394 | } 395 | if ps := muo.mutation.predicates; len(ps) > 0 { 396 | _spec.Predicate = func(selector *sql.Selector) { 397 | for i := range ps { 398 | ps[i](selector) 399 | } 400 | } 401 | } 402 | if value, ok := muo.mutation.Name(); ok { 403 | _spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{ 404 | Type: field.TypeString, 405 | Value: value, 406 | Column: model.FieldName, 407 | }) 408 | } 409 | if value, ok := muo.mutation.Title(); ok { 410 | _spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{ 411 | Type: field.TypeString, 412 | Value: value, 413 | Column: model.FieldTitle, 414 | }) 415 | } 416 | if value, ok := muo.mutation.Fax(); ok { 417 | _spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{ 418 | Type: field.TypeString, 419 | Value: value, 420 | Column: model.FieldFax, 421 | }) 422 | } 423 | if value, ok := muo.mutation.Web(); ok { 424 | _spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{ 425 | Type: field.TypeString, 426 | Value: value, 427 | Column: model.FieldWeb, 428 | }) 429 | } 430 | if value, ok := muo.mutation.Age(); ok { 431 | _spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{ 432 | Type: field.TypeInt, 433 | Value: value, 434 | Column: model.FieldAge, 435 | }) 436 | } 437 | if value, ok := muo.mutation.AddedAge(); ok { 438 | _spec.Fields.Add = append(_spec.Fields.Add, &sqlgraph.FieldSpec{ 439 | Type: field.TypeInt, 440 | Value: value, 441 | Column: model.FieldAge, 442 | }) 443 | } 444 | if value, ok := muo.mutation.Right(); ok { 445 | _spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{ 446 | Type: field.TypeBool, 447 | Value: value, 448 | Column: model.FieldRight, 449 | }) 450 | } 451 | if value, ok := muo.mutation.Counter(); ok { 452 | _spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{ 453 | Type: field.TypeInt64, 454 | Value: value, 455 | Column: model.FieldCounter, 456 | }) 457 | } 458 | if value, ok := muo.mutation.AddedCounter(); ok { 459 | _spec.Fields.Add = append(_spec.Fields.Add, &sqlgraph.FieldSpec{ 460 | Type: field.TypeInt64, 461 | Value: value, 462 | Column: model.FieldCounter, 463 | }) 464 | } 465 | _node = &Model{config: muo.config} 466 | _spec.Assign = _node.assignValues 467 | _spec.ScanValues = _node.scanValues 468 | if err = sqlgraph.UpdateNode(ctx, muo.driver, _spec); err != nil { 469 | if _, ok := err.(*sqlgraph.NotFoundError); ok { 470 | err = &NotFoundError{model.Label} 471 | } else if sqlgraph.IsConstraintError(err) { 472 | err = &ConstraintError{err.Error(), err} 473 | } 474 | return nil, err 475 | } 476 | return _node, nil 477 | } 478 | --------------------------------------------------------------------------------