diff --git a/README.md b/README.md index 66a3761..600fa7f 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ All package run in no-cache mode. - [pgx](https://github.com/jackc/pgx) - [zorm](https://gitee.com/chunanyong/zorm) - [gen](https://gorm.io/gen/index.html) +- [queryx](https://github.com/swiftcarrot/queryx) See [`go.mod`](go.mod) for their latest versions. diff --git a/bench/models.go b/bench/models.go index b490759..11bd89d 100644 --- a/bench/models.go +++ b/bench/models.go @@ -215,6 +215,40 @@ func NewModel7() *Model7 { return m } +// Model for queryx +type Model8 struct { + zorm.EntityStruct + ID int `column:"id"` + Name string `column:"name"` + Title string `column:"title"` + Fax string `column:"fax"` + Web string `column:"web"` + Age int `column:"age"` + Right bool `column:"\"right\""` + Counter int64 `column:"counter"` +} + +func (entity *Model8) GetTableName() string { + return "models" +} + +func (entity *Model8) GetPKColumnName() string { + return "id" +} + +func NewModel8() *Model8 { + m := new(Model8) + m.Name = "Orm Benchmark" + m.Title = "Just a Benchmark for fun" + m.Fax = "99909990" + m.Web = "http://blog.milkpod29.me" + m.Age = 100 + m.Right = true + m.Counter = 1000 + + return m +} + func NewReformModel() *r.ReformModels { m := new(r.ReformModels) m.Name = "Orm Benchmark" diff --git a/bench/queryx.go b/bench/queryx.go new file mode 100644 index 0000000..82369dd --- /dev/null +++ b/bench/queryx.go @@ -0,0 +1,137 @@ +package bench + +import ( + "github.com/efectn/go-orm-benchmarks/bench/queryx/db/queryx" + "github.com/efectn/go-orm-benchmarks/helper" + "testing" + + "github.com/efectn/go-orm-benchmarks/bench/queryx/db" + _ "github.com/lib/pq" +) + +const ( + queryxSelectMultiSQL = `SELECT * FROM models WHERE id > 0` +) + +type Queryx struct { + helper.ORMInterface + client *db.QXClient +} + +func CreateQueryx() helper.ORMInterface { + return &Queryx{} +} + +func (q *Queryx) Name() string { + return "queryx" +} + +func (q *Queryx) Init() error { + client, err := db.NewClient() + if err != nil { + return err + } + q.client = client + return err +} + +func (q *Queryx) Close() error { + return nil +} + +func (q *Queryx) Insert(b *testing.B) { + m := NewModel8() + + b.ReportAllocs() + b.ResetTimer() + + for i := 0; i < b.N; i++ { + m.ID = 0 + _, err := q.client.QueryModel().Create(q.client.ChangeModel().SetName(m.Name). + SetTitle(m.Title).SetFax(m.Fax).SetWeb(m.Web).SetAge(int64(m.Age)).SetCounter(int32(m.Counter)).SetRight(true)) + if err != nil { + helper.SetError(b, q.Name(), "Insert", err.Error()) + } + } +} + +func (q *Queryx) InsertMulti(b *testing.B) { + m := NewModel8() + ms := make([]*queryx.ModelChange, 0, 100) + for i := 0; i < 100; i++ { + ms = append(ms, q.client.ChangeModel().SetName(m.Name). + SetTitle(m.Title).SetFax(m.Fax).SetWeb(m.Web).SetAge(int64(m.Age)).SetCounter(int32(m.Counter)).SetRight(true)) + } + + b.ReportAllocs() + b.ResetTimer() + + for i := 0; i < b.N; i++ { + _, err := q.client.QueryModel().InsertAll(ms) + if err != nil { + helper.SetError(b, q.Name(), "InsertMulti", err.Error()) + } + } +} + +func (q *Queryx) Update(b *testing.B) { + m := NewModel8() + change := q.client.ChangeModel().SetName(m.Name).SetRight(true). + SetTitle(m.Title).SetFax(m.Fax).SetWeb(m.Web).SetAge(int64(m.Age)).SetCounter(int32(m.Counter)) + m8, err := q.client.QueryModel().Create(change) + if err != nil { + helper.SetError(b, q.Name(), "Update", err.Error()) + } + + b.ReportAllocs() + b.ResetTimer() + + for i := 0; i < b.N; i++ { + _, err := q.client.QueryModel().Where(q.client.ModelID.EQ(m8.ID)).UpdateAll(change) + if err != nil { + helper.SetError(b, q.Name(), "Update", err.Error()) + } + } +} + +func (q *Queryx) Read(b *testing.B) { + m := NewModel8() + change := q.client.ChangeModel().SetName(m.Name).SetRight(true). + SetTitle(m.Title).SetFax(m.Fax).SetWeb(m.Web).SetAge(int64(m.Age)).SetCounter(int32(m.Counter)) + _, err := q.client.QueryModel().Create(change) + if err != nil { + helper.SetError(b, q.Name(), "Read", err.Error()) + } + + b.ReportAllocs() + b.ResetTimer() + + for i := 0; i < b.N; i++ { + _, err := q.client.QueryModel().FindBy(q.client.ModelName.EQ(m.Name)) + if err != nil { + helper.SetError(b, q.Name(), "Read", err.Error()) + } + } +} + +func (q *Queryx) ReadSlice(b *testing.B) { + m := NewModel8() + change := q.client.ChangeModel().SetName(m.Name).SetRight(true). + SetTitle(m.Title).SetFax(m.Fax).SetWeb(m.Web).SetAge(int64(m.Age)).SetCounter(int32(m.Counter)) + for i := 0; i < 100; i++ { + _, err := q.client.QueryModel().Create(change) + if err != nil { + helper.SetError(b, q.Name(), "ReadSlice", err.Error()) + } + } + + b.ReportAllocs() + b.ResetTimer() + + for i := 0; i < b.N; i++ { + _, err := q.client.QueryModel().FindBySQL(queryxSelectMultiSQL) + if err != nil { + helper.SetError(b, q.Name(), "ReadSlice", err.Error()) + } + } +} diff --git a/bench/queryx/db/errors.go b/bench/queryx/db/errors.go new file mode 100644 index 0000000..a74d06f --- /dev/null +++ b/bench/queryx/db/errors.go @@ -0,0 +1,7 @@ +// Code generated by queryx, DO NOT EDIT. + +package db + +import "errors" + +var ErrInsertAllEmptyChanges = errors.New("queryx: insert all with empty changes") diff --git a/bench/queryx/db/model.go b/bench/queryx/db/model.go new file mode 100644 index 0000000..6dda02a --- /dev/null +++ b/bench/queryx/db/model.go @@ -0,0 +1,91 @@ +// Code generated by queryx, DO NOT EDIT. + +package db + +import ( + "fmt" + "strings" + + "github.com/efectn/go-orm-benchmarks/bench/queryx/db/queryx" +) + +type Model struct { + ID int64 `json:"id" db:"id"` + Name queryx.String `json:"name" db:"name"` + Title queryx.String `json:"title" db:"title"` + Fax queryx.String `json:"fax" db:"fax"` + Web queryx.String `json:"web" db:"web"` + Age queryx.BigInt `json:"age" db:"age"` + Right queryx.Boolean `json:"right" db:"right"` + Counter queryx.Integer `json:"counter" db:"counter"` + + schema *queryx.Schema + queries Queries +} + +// String implements the stringer interface. +func (m *Model) String() string { + var sb strings.Builder + sb.WriteString("(Model ") + sb.WriteString(fmt.Sprintf("id: %v", m.ID)) + sb.WriteString(", ") + sb.WriteString(fmt.Sprintf("name: %s", m.Name)) + sb.WriteString(", ") + sb.WriteString(fmt.Sprintf("title: %s", m.Title)) + sb.WriteString(", ") + sb.WriteString(fmt.Sprintf("fax: %s", m.Fax)) + sb.WriteString(", ") + sb.WriteString(fmt.Sprintf("web: %s", m.Web)) + sb.WriteString(", ") + sb.WriteString(fmt.Sprintf("age: %s", m.Age)) + sb.WriteString(", ") + sb.WriteString(fmt.Sprintf("right: %s", m.Right)) + sb.WriteString(", ") + sb.WriteString(fmt.Sprintf("counter: %s", m.Counter)) + sb.WriteString(")") + return sb.String() +} + +func (m *Model) applyChange(change *queryx.ModelChange) error { + if change == nil { + return nil + } + if change.ID.Set { + m.ID = change.ID.Val + } + if change.Name.Set { + m.Name = change.Name + } + if change.Title.Set { + m.Title = change.Title + } + if change.Fax.Set { + m.Fax = change.Fax + } + if change.Web.Set { + m.Web = change.Web + } + if change.Age.Set { + m.Age = change.Age + } + if change.Right.Set { + m.Right = change.Right + } + if change.Counter.Set { + m.Counter = change.Counter + } + return nil +} +func (m *Model) Update(change *queryx.ModelChange) error { + _, err := m.queries.QueryModel().Where(m.schema.And(m.schema.ModelID.EQ(m.ID))).UpdateAll(change) + if err != nil { + return err + } + + return m.applyChange(change) +} + +func (m *Model) Delete() error { + _, err := m.queries.QueryModel().Delete(m.ID) + return err +} diff --git a/bench/queryx/db/model_query.go b/bench/queryx/db/model_query.go new file mode 100644 index 0000000..b174962 --- /dev/null +++ b/bench/queryx/db/model_query.go @@ -0,0 +1,300 @@ +// Code generated by queryx, DO NOT EDIT. + +package db + +import ( + "database/sql" + "errors" + "fmt" + + "github.com/efectn/go-orm-benchmarks/bench/queryx/db/queryx" +) + +type ModelQuery struct { + adapter *queryx.Adapter + schema *queryx.Schema + queries Queries + selectStatement *queryx.SelectStatement + preload map[string]bool + err error +} + +func NewModelQuery(adapter *queryx.Adapter, schema *queryx.Schema, queries Queries) *ModelQuery { + s := queryx.NewSelect().Select("models.*").From("models") + return &ModelQuery{ + adapter: adapter, + schema: schema, + queries: queries, + selectStatement: s, + preload: make(map[string]bool), + } +} + +func (q *ModelQuery) Create(change *queryx.ModelChange) (*Model, error) { + if q.err != nil { + return nil, q.err + } + + record := &Model{ + schema: q.schema, + queries: q.queries, + } + columns, values := change.Changes() + query, args := queryx.NewInsert(). + Into("models"). + Columns(columns...). + Values(values...). + Returning("*").ToSQL() + fmt.Println(query) + err := q.adapter.QueryOne(query, args...).Scan(record) + if err != nil { + return nil, err + } + + return record, nil +} + +func (q *ModelQuery) InsertAll(changes []*queryx.ModelChange) (int64, error) { + if q.err != nil { + return 0, q.err + } + + if len(changes) == 0 { + return 0, ErrInsertAllEmptyChanges + } + + s := queryx.NewInsert().Into("models") + for i, change := range changes { + columns, values := change.Changes() + if i == 0 { + s.Columns(columns...) + } + s.Values(values...) + } + + query, args := s.ToSQL() + return q.adapter.Exec(query, args...) +} + +func (q *ModelQuery) Delete(id int64) (int64, error) { + query, args := queryx.NewDelete().From("models").Where(q.schema.ModelID.EQ(id)).ToSQL() + result, err := q.adapter.Exec(query, args...) + if err != nil { + return 0, err + } + return result, err +} + +func (q *ModelQuery) Find(id int64) (*Model, error) { + res, err := q.Where(q.schema.ModelID.EQ(id)).First() + if err != nil { + return nil, err + } + if res == nil { + return nil, sql.ErrNoRows + } + res.schema = q.schema + res.queries = q.queries + return res, err +} + +func (q *ModelQuery) FindBy(where *queryx.Clause) (*Model, error) { + return q.Where(where).First() +} + +func (q *ModelQuery) FindBySQL(query string, args ...interface{}) ([]*Model, error) { + var modelList []Model + models := make([]*Model, 0) + err := q.adapter.Query(query, args...).Scan(&modelList) + if err != nil { + return nil, err + } + for i := 0; i < len(modelList); i++ { + models = append(models, &modelList[i]) + } + return models, nil +} + +func (q *ModelQuery) Where(clauses ...*queryx.Clause) *ModelQuery { + q.selectStatement.Where(clauses...) + return q +} + +func (q *ModelQuery) Select(selection ...string) *ModelQuery { + q.selectStatement.Select(selection...) + return q +} + +func (q *ModelQuery) Limit(limit int) *ModelQuery { + q.selectStatement.Limit(limit) + return q +} + +func (q *ModelQuery) Offset(offset int) *ModelQuery { + q.selectStatement.Offset(offset) + return q +} + +func (q *ModelQuery) Group(group string) *ModelQuery { + q.selectStatement.GroupBy(group) + return q +} + +func (q *ModelQuery) Having(having string) *ModelQuery { + q.selectStatement.Having(having) + return q +} + +func (q *ModelQuery) Joins(joins string) *ModelQuery { + q.selectStatement.Join(joins) + return q +} + +func (q *ModelQuery) Order(order ...string) *ModelQuery { + q.selectStatement.Order(order...) + return q +} + +func (q *ModelQuery) All() ([]*Model, error) { + if q.err != nil { + return nil, q.err + } + var rows []Model + models := make([]*Model, 0) + query, args := q.selectStatement.ToSQL() + err := q.adapter.Query(query, args...).Scan(&rows) + if err != nil { + return nil, err + } + + if len(rows) == 0 { + return models, nil + } + + for i := range rows { + rows[i].schema = q.schema + rows[i].queries = q.queries + models = append(models, &rows[i]) + } + + return models, nil +} + +func (q *ModelQuery) First() (*Model, error) { + q.Limit(1) + results, err := q.All() + if err != nil { + return nil, err + } + if len(results) > 0 { + return results[0], nil + } + + return nil, nil +} + +func (q *ModelQuery) Count() (int64, error) { + var res struct { + Count int64 `db:"count"` + } + q.selectStatement.Select("count(*)") + query, args := q.selectStatement.ToSQL() + if err := q.adapter.QueryOne(query, args...).Scan(&res); err != nil { + return 0, err + } + + return res.Count, nil +} + +func (q *ModelQuery) Avg(v string) (float64, error) { + var res struct { + Avg sql.NullFloat64 `db:"avg"` + } + q.selectStatement.Select(fmt.Sprintf("avg(%+v)", v)) + query, args := q.selectStatement.ToSQL() + if err := q.adapter.QueryOne(query, args...).Scan(&res); err != nil { + return 0, err + } + + return res.Avg.Float64, nil +} + +func (q *ModelQuery) Sum(v string) (float64, error) { + var res struct { + Sum sql.NullFloat64 `db:"sum"` + } + q.selectStatement.Select(fmt.Sprintf("sum(%+v)", v)) + query, args := q.selectStatement.ToSQL() + if err := q.adapter.QueryOne(query, args...).Scan(&res); err != nil { + return 0, err + } + + return res.Sum.Float64, nil +} + +func (q *ModelQuery) Max(v string) (float64, error) { + var res struct { + Max sql.NullFloat64 `db:"max"` + } + q.selectStatement.Select(fmt.Sprintf("max(%+v)", v)) + query, args := q.selectStatement.ToSQL() + if err := q.adapter.QueryOne(query, args...).Scan(&res); err != nil { + return 0, err + } + + return res.Max.Float64, nil +} + +func (q *ModelQuery) Min(v string) (float64, error) { + var res struct { + Min sql.NullFloat64 `db:"min"` + } + q.selectStatement.Select(fmt.Sprintf("min(%+v)", v)) + query, args := q.selectStatement.ToSQL() + if err := q.adapter.QueryOne(query, args...).Scan(&res); err != nil { + return 0, err + } + + return res.Min.Float64, nil +} + +func (q *ModelQuery) Exists() (bool, error) { + q.selectStatement.Select("1 AS one").Limit(1) + // select 1 as one from users limit 1 + query, args := q.selectStatement.ToSQL() + var res struct { + One int64 `db:"one"` + } + if err := q.adapter.QueryOne(query, args...).Scan(&res); err != nil { + if errors.Is(err, sql.ErrNoRows) { + return false, nil + } + return false, err + } + + return res.One == 1, nil +} + +func (q *ModelQuery) UpdateAll(change *queryx.ModelChange) (int64, error) { + if q.err != nil { + return 0, q.err + } + columns, values := change.Changes() + query, args := q.selectStatement.Update().Columns(columns...).Values(values...).ToSQL() + fmt.Println(query) + fmt.Println(args) + result, err := q.adapter.Exec(query, args...) + if err != nil { + return 0, err + } + return result, err +} + +func (q *ModelQuery) DeleteAll() (int64, error) { + query, args := q.selectStatement.Delete().ToSQL() + result, err := q.adapter.Exec(query, args...) + if err != nil { + return 0, err + } + return result, err +} diff --git a/bench/queryx/db/queryx.go b/bench/queryx/db/queryx.go new file mode 100644 index 0000000..2b29607 --- /dev/null +++ b/bench/queryx/db/queryx.go @@ -0,0 +1,120 @@ +// Code generated by queryx, DO NOT EDIT. + +package db + +import ( + "context" + "database/sql" + "fmt" + "log" + "os" + + "github.com/efectn/go-orm-benchmarks/bench/queryx/db/queryx" +) + +type Queries interface { + QueryModel() *ModelQuery +} + +type QXClient struct { + db *sql.DB + config *queryx.Config + logger queryx.Logger + *queryx.Adapter + *queryx.Schema +} + +func NewClient() (*QXClient, error) { + env := os.Getenv("QUERYX_ENV") + if env == "" { + env = "development" + } + return NewClientWithEnv(env) +} + +func NewClientWithEnv(env string) (*QXClient, error) { + config := queryx.NewConfig(env) + if config == nil { + return nil, fmt.Errorf("client config is missing for %s", env) + } + db, err := sql.Open("postgres", config.URL) + if err != nil { + return nil, err + } + + client := &QXClient{ + db: db, + config: config, + Adapter: queryx.NewAdapter(db), + Schema: queryx.NewSchema(), + } + client.setDefaultLogger() + + return client, nil +} + +func (c *QXClient) SetLogger(logger queryx.Logger) { + c.logger = logger +} + +func (c *QXClient) setDefaultLogger() { + c.logger = log.New(os.Stderr, "sql ", log.Llongfile|log.LstdFlags) +} + +func (c *QXClient) QueryModel() *ModelQuery { + return NewModelQuery(c.Adapter, c.Schema, c) +} + +func (c *QXClient) Transaction(f func(t *Tx) error) error { + tx, err := c.Tx() + if err != nil { + return err + } + defer tx.Rollback() + if err = f(tx); err != nil { + return err + } + return tx.Commit() +} + +func (c *QXClient) Raw(fragment string, args ...interface{}) *queryx.Clause { + return queryx.NewClause(fragment, args) +} + +type Tx struct { + *queryx.Schema + *queryx.Adapter + tx *sql.Tx + client *QXClient +} + +func (c *QXClient) Tx() (*Tx, error) { + ctx := context.Background() + tx, err := c.db.BeginTx(ctx, nil) + if err != nil { + return nil, err + } + + return &Tx{ + tx: tx, + Schema: c.Schema, + Adapter: queryx.NewAdapter(tx), + client: c, + }, nil +} + +func (tx *Tx) Commit() error { + return tx.tx.Commit() +} + +func (tx *Tx) Rollback() error { + return tx.tx.Rollback() +} + +func (tx *Tx) QueryModel() *ModelQuery { + return NewModelQuery(tx.Adapter, tx.Schema, tx) +} + +func (tx *Tx) Raw(fragment string, args ...interface{}) *queryx.Clause { + return queryx.NewClause(fragment, args) +} diff --git a/bench/queryx/db/queryx/adapter.go b/bench/queryx/db/queryx/adapter.go new file mode 100644 index 0000000..55eac2a --- /dev/null +++ b/bench/queryx/db/queryx/adapter.go @@ -0,0 +1,48 @@ +// Code generated by queryx, DO NOT EDIT. + +package queryx + +import ( + "regexp" + "strconv" + "strings" + + _ "github.com/lib/pq" +) + +func (a *Adapter) Exec(query string, args ...interface{}) (int64, error) { + matched1, err := regexp.MatchString(`.* IN (.*?)`, query) + if err != nil { + return 0, err + } + matched2, err := regexp.MatchString(`.* in (.*?)`, query) + if err != nil { + return 0, err + } + if matched1 || matched2 { + query, args, err = In(query, args...) + if err != nil { + return 0, err + } + } + query, args = rebind(query, args) + result, err := a.db.Exec(query, args...) + if err != nil { + return 0, err + } + return result.RowsAffected() +} + +func rebind(query string, args []interface{}) (string, []interface{}) { + rqb := make([]byte, 0, len(query)+10) + var i, j int + for i = strings.Index(query, "?"); i != -1; i = strings.Index(query, "?") { + rqb = append(rqb, query[:i]...) + rqb = append(rqb, '$') + j++ + rqb = strconv.AppendInt(rqb, int64(j), 10) + query = query[i+1:] + } + query = string(append(rqb, query...)) + return query, args +} diff --git a/bench/queryx/db/queryx/adapter_test.go b/bench/queryx/db/queryx/adapter_test.go new file mode 100644 index 0000000..07fea4b --- /dev/null +++ b/bench/queryx/db/queryx/adapter_test.go @@ -0,0 +1,15 @@ +// Code generated by queryx, DO NOT EDIT. + +package queryx + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestRebind(t *testing.T) { + sql, args := rebind("a ? b ?", []interface{}{1, 2}) + require.Equal(t, "a $1 b $2", sql) + require.Equal(t, []interface{}{1, 2}, args) +} diff --git a/bench/queryx/db/queryx/bigint.go b/bench/queryx/db/queryx/bigint.go new file mode 100644 index 0000000..1d58235 --- /dev/null +++ b/bench/queryx/db/queryx/bigint.go @@ -0,0 +1,75 @@ +// Code generated by queryx, DO NOT EDIT. + +package queryx + +import ( + "database/sql" + "database/sql/driver" + "encoding/json" + "strconv" +) + +type BigInt struct { + Val int64 + Valid bool + Set bool +} + +func NewBigInt(v int64) BigInt { + return BigInt{Val: v, Valid: true, Set: true} +} + +func NewNullableBigInt(v *int64) BigInt { + if v != nil { + return NewBigInt(*v) + } + return BigInt{Set: true} +} + +// Scan implements the Scanner interface. +func (b *BigInt) Scan(value interface{}) error { + n := sql.NullInt64{} + err := n.Scan(value) + if err != nil { + return err + } + b.Val, b.Valid = n.Int64, n.Valid + return nil +} + +// Value implements the driver Valuer interface. +func (b BigInt) Value() (driver.Value, error) { + if !b.Valid { + return nil, nil + } + return b.Val, nil +} + +// MarshalJSON implements the json.Marshaler interface. +func (b BigInt) MarshalJSON() ([]byte, error) { + if !b.Valid { + return json.Marshal(nil) + } + return json.Marshal(b.Val) +} + +// UnmarshalJSON implements the json.Unmarshaler interface. +func (b *BigInt) UnmarshalJSON(data []byte) error { + b.Set = true + if string(data) == "null" { + return nil + } + b.Valid = true + if err := json.Unmarshal(data, &b.Val); err != nil { + return err + } + return nil +} + +// String implements the stringer interface. +func (b BigInt) String() string { + if !b.Valid { + return "null" + } + return strconv.FormatInt(b.Val, 10) +} diff --git a/bench/queryx/db/queryx/bigint_column.go b/bench/queryx/db/queryx/bigint_column.go new file mode 100644 index 0000000..8682bd2 --- /dev/null +++ b/bench/queryx/db/queryx/bigint_column.go @@ -0,0 +1,93 @@ +// Code generated by queryx, DO NOT EDIT. + +package queryx + +import "fmt" + +type BigIntColumn struct { + Name string + Table *Table +} + +func (t *Table) NewBigIntColumn(name string) *BigIntColumn { + return &BigIntColumn{ + Table: t, + Name: name, + } +} + +func (c *BigIntColumn) EQ(v int64) *Clause { + return &Clause{ + fragment: fmt.Sprintf("%s.%s = ?", c.Table.Name, c.Name), + args: []interface{}{v}, + } +} + +func (c *BigIntColumn) NE(v int64) *Clause { + return &Clause{ + fragment: fmt.Sprintf("%s.%s <> ?", c.Table.Name, c.Name), + args: []interface{}{v}, + } +} + +func (c *BigIntColumn) LT(v int64) *Clause { + return &Clause{ + fragment: fmt.Sprintf("%s.%s < ?", c.Table.Name, c.Name), + args: []interface{}{v}, + } +} + +func (c *BigIntColumn) GT(v int64) *Clause { + return &Clause{ + fragment: fmt.Sprintf("%s.%s > ?", c.Table.Name, c.Name), + args: []interface{}{v}, + } +} + +func (c *BigIntColumn) LE(v int64) *Clause { + return &Clause{ + fragment: fmt.Sprintf("%s.%s <= ?", c.Table.Name, c.Name), + args: []interface{}{v}, + } +} + +func (c *BigIntColumn) GE(v int64) *Clause { + return &Clause{ + fragment: fmt.Sprintf("%s.%s >= ?", c.Table.Name, c.Name), + args: []interface{}{v}, + } +} + +func (c *BigIntColumn) In(v []int64) *Clause { + if len(v) == 0 { + return &Clause{ + fragment: "1=0", + } + } + return &Clause{ + fragment: fmt.Sprintf("%s.%s IN (?)", c.Table.Name, c.Name), + args: []interface{}{v}, + } +} + +func (c *BigIntColumn) InRange(start int64, end int64) *Clause { + return &Clause{ + fragment: fmt.Sprintf("%s.%s >= ? and %s.%s< ?", c.Table.Name, c.Name, c.Table.Name, c.Name), + args: []interface{}{start, end}, + } +} + +func (c *BigIntColumn) Between(start int64, end int64) *Clause { + return &Clause{ + fragment: fmt.Sprintf("%s.%s >= ? and %s.%s<= ?", c.Table.Name, c.Name, c.Table.Name, c.Name), + args: []interface{}{start, end}, + } +} + +func (c *BigIntColumn) Asc() string { + return fmt.Sprintf("%s.%s ASC", c.Table.Name, c.Name) +} + +func (c *BigIntColumn) Desc() string { + return fmt.Sprintf("%s.%s DESC", c.Table.Name, c.Name) +} diff --git a/bench/queryx/db/queryx/bigint_test.go b/bench/queryx/db/queryx/bigint_test.go new file mode 100644 index 0000000..1500f35 --- /dev/null +++ b/bench/queryx/db/queryx/bigint_test.go @@ -0,0 +1,40 @@ +// Code generated by queryx, DO NOT EDIT. + +package queryx + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestNewBigInt(t *testing.T) { + b1 := NewBigInt(2) + require.Equal(t, int64(2), b1.Val) + require.True(t, b1.Valid) + + b2 := NewNullableBigInt(nil) + require.False(t, b2.Valid) +} + +func TestBigIntJSON(t *testing.T) { + type Foo struct { + X BigInt `json:"x"` + Y BigInt `json:"y"` + } + x := NewBigInt(2) + y := NewNullableBigInt(nil) + s := `{"x":2,"y":null}` + + f1 := Foo{X: x, Y: y} + b, err := json.Marshal(f1) + require.NoError(t, err) + require.Equal(t, s, string(b)) + + var f2 Foo + err = json.Unmarshal([]byte(s), &f2) + require.NoError(t, err) + require.Equal(t, x, f2.X) + require.Equal(t, y, f2.Y) +} diff --git a/bench/queryx/db/queryx/bind.go b/bench/queryx/db/queryx/bind.go new file mode 100644 index 0000000..d248e8d --- /dev/null +++ b/bench/queryx/db/queryx/bind.go @@ -0,0 +1,167 @@ +// Code generated by queryx, DO NOT EDIT. + +package queryx + +import ( + "database/sql/driver" + "fmt" + "reflect" + "strings" +) + +// In expands slice values in args, returning the modified query string +// and a new arg list that can be executed by a database. The `query` should +// use the `?` bindVar. The return value uses the `?` bindVar. +func In(query string, args ...interface{}) (string, []interface{}, error) { + // argMeta stores reflect.Value and length for slices and + // the value itself for non-slice arguments + type argMeta struct { + v reflect.Value + i interface{} + length int + } + + var flatArgsCount int + var anySlices bool + + var stackMeta [32]argMeta + + var meta []argMeta + if len(args) <= len(stackMeta) { + meta = stackMeta[:len(args)] + } else { + meta = make([]argMeta, len(args)) + } + + for i, arg := range args { + if a, ok := arg.(driver.Valuer); ok { + var err error + arg, err = a.Value() + if err != nil { + return "", nil, err + } + } + + if v, ok := asSliceForIn(arg); ok { + meta[i].length = v.Len() + meta[i].v = v + + anySlices = true + flatArgsCount += meta[i].length + + if meta[i].length == 0 { + return "", nil, fmt.Errorf("empty slice passed to 'in' query") + } + } else { + meta[i].i = arg + flatArgsCount++ + } + } + + // don't do any parsing if there aren't any slices; note that this means + // some errors that we might have caught below will not be returned. + if !anySlices { + return query, args, nil + } + + newArgs := make([]interface{}, 0, flatArgsCount) + + var buf strings.Builder + buf.Grow(len(query) + len(", ?")*flatArgsCount) + + var arg, offset int + + for i := strings.IndexByte(query[offset:], '?'); i != -1; i = strings.IndexByte(query[offset:], '?') { + if arg >= len(meta) { + // if an argument wasn't passed, lets return an error; this is + // not actually how database/sql Exec/Query works, but since we are + // creating an argument list programmatically, we want to be able + // to catch these programmer errors earlier. + return "", nil, fmt.Errorf("number of bindVars exceeds arguments") + } + + argMeta := meta[arg] + arg++ + + // not a slice, continue. + // our questionmark will either be written before the next expansion + // of a slice or after the loop when writing the rest of the query + if argMeta.length == 0 { + offset = offset + i + 1 + newArgs = append(newArgs, argMeta.i) + continue + } + + // write everything up to and including our ? character + buf.WriteString(query[:offset+i+1]) + + for si := 1; si < argMeta.length; si++ { + buf.WriteString(", ?") + } + + newArgs = appendReflectSlice(newArgs, argMeta.v, argMeta.length) + + // slice the query and reset the offset. this avoids some bookkeeping for + // the write after the loop + query = query[offset+i+1:] + offset = 0 + } + + buf.WriteString(query) + + if arg < len(meta) { + return "", nil, fmt.Errorf("number of bindVars less than number arguments") + } + + return buf.String(), newArgs, nil +} +func asSliceForIn(i interface{}) (v reflect.Value, ok bool) { + if i == nil { + return reflect.Value{}, false + } + + v = reflect.ValueOf(i) + t := Deref(v.Type()) + + // Only expand slices + if t.Kind() != reflect.Slice { + return reflect.Value{}, false + } + + // []byte is a driver.Value type so it should not be expanded + if t == reflect.TypeOf([]byte{}) { + return reflect.Value{}, false + + } + + return v, true +} + +// Deref is Indirect for reflect.Types +func Deref(t reflect.Type) reflect.Type { + if t.Kind() == reflect.Ptr { + t = t.Elem() + } + return t +} + +func appendReflectSlice(args []interface{}, v reflect.Value, vlen int) []interface{} { + switch val := v.Interface().(type) { + case []interface{}: + args = append(args, val...) + case []int: + for i := range val { + args = append(args, val[i]) + } + case []string: + for i := range val { + args = append(args, val[i]) + } + default: + for si := 0; si < vlen; si++ { + args = append(args, v.Index(si).Interface()) + } + } + + return args +} diff --git a/bench/queryx/db/queryx/boolean.go b/bench/queryx/db/queryx/boolean.go new file mode 100644 index 0000000..388d2e9 --- /dev/null +++ b/bench/queryx/db/queryx/boolean.go @@ -0,0 +1,77 @@ +// Code generated by queryx, DO NOT EDIT. + +package queryx + +import ( + "database/sql" + "database/sql/driver" + "encoding/json" +) + +type Boolean struct { + Val bool + Valid bool + Set bool +} + +func NewBoolean(v bool) Boolean { + return Boolean{Val: v, Valid: true, Set: true} +} + +func NewNullableBoolean(v *bool) Boolean { + if v != nil { + return NewBoolean(*v) + } + return Boolean{Set: true} +} + +// Scan implements the Scanner interface. +func (b *Boolean) Scan(value interface{}) error { + n := sql.NullBool{} + err := n.Scan(value) + if err != nil { + return err + } + b.Val, b.Valid = n.Bool, n.Valid + return nil +} + +// Value implements the driver Valuer interface. +func (b Boolean) Value() (driver.Value, error) { + if !b.Valid { + return nil, nil + } + return b.Val, nil +} + +// MarshalJSON implements the json.Marshaler interface. +func (b Boolean) MarshalJSON() ([]byte, error) { + if !b.Valid { + return json.Marshal(nil) + } + return json.Marshal(b.Val) +} + +// UnmarshalJSON implements the json.Unmarshaler interface. +func (b *Boolean) UnmarshalJSON(data []byte) error { + b.Set = true + if string(data) == "null" { + return nil + } + b.Valid = true + if err := json.Unmarshal(data, &b.Val); err != nil { + return err + } + return nil +} + +// String implements the stringer interface. +func (b Boolean) String() string { + if !b.Valid { + return "null" + } + if b.Val { + return "true" + } + return "false" +} diff --git a/bench/queryx/db/queryx/boolean_column.go b/bench/queryx/db/queryx/boolean_column.go new file mode 100644 index 0000000..72764e3 --- /dev/null +++ b/bench/queryx/db/queryx/boolean_column.go @@ -0,0 +1,39 @@ +// Code generated by queryx, DO NOT EDIT. + +package queryx + +import "fmt" + +type BooleanColumn struct { + Name string + Table *Table +} + +func (t *Table) NewBooleanColumn(name string) *BooleanColumn { + return &BooleanColumn{ + Table: t, + Name: name, + } +} + +func (c *BooleanColumn) EQ(v bool) *Clause { + return &Clause{ + fragment: fmt.Sprintf("%s.%s = ?", c.Table.Name, c.Name), + args: []interface{}{v}, + } +} + +func (c *BooleanColumn) NE(v bool) *Clause { + return &Clause{ + fragment: fmt.Sprintf("%s.%s <> ?", c.Table.Name, c.Name), + args: []interface{}{v}, + } +} + +func (b *BooleanColumn) Asc() string { + return fmt.Sprintf("%s.%s ASC", b.Table.Name, b.Name) +} + +func (b *BooleanColumn) Desc() string { + return fmt.Sprintf("%s.%s DESC", b.Table.Name, b.Name) +} diff --git a/bench/queryx/db/queryx/boolean_test.go b/bench/queryx/db/queryx/boolean_test.go new file mode 100644 index 0000000..31a8fb6 --- /dev/null +++ b/bench/queryx/db/queryx/boolean_test.go @@ -0,0 +1,40 @@ +// Code generated by queryx, DO NOT EDIT. + +package queryx + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestNewBoolean(t *testing.T) { + b1 := NewBoolean(true) + require.Equal(t, true, b1.Val) + require.True(t, b1.Valid) + + b2 := NewNullableBoolean(nil) + require.False(t, b2.Valid) +} + +func TestBooleanJSON(t *testing.T) { + type Foo struct { + X Boolean `json:"x"` + Y Boolean `json:"y"` + } + x := NewBoolean(true) + y := NewNullableBoolean(nil) + s := `{"x":true,"y":null}` + + f1 := Foo{X: x, Y: y} + b, err := json.Marshal(f1) + require.NoError(t, err) + require.Equal(t, s, string(b)) + + var f2 Foo + err = json.Unmarshal([]byte(s), &f2) + require.NoError(t, err) + require.Equal(t, x, f2.X) + require.Equal(t, y, f2.Y) +} diff --git a/bench/queryx/db/queryx/clause.go b/bench/queryx/db/queryx/clause.go new file mode 100644 index 0000000..a5b0357 --- /dev/null +++ b/bench/queryx/db/queryx/clause.go @@ -0,0 +1,57 @@ +// Code generated by queryx, DO NOT EDIT. + +package queryx + +import ( + "fmt" + "strings" +) + +type Clause struct { + fragment string + args []interface{} + err error +} + +func NewClause(fragment string, args []interface{}) *Clause { + return &Clause{ + fragment: fragment, + args: args, + } +} + +func (c *Clause) Err() error { + return c.err +} + +func (c *Clause) And(clauses ...*Clause) *Clause { + if len(clauses) == 0 { + return c + } + + var fragments []string + var args []interface{} + clauses = append([]*Clause{c}, clauses...) + for _, clause := range clauses { + fragments = append(fragments, fmt.Sprintf("(%s)", clause.fragment)) + args = append(args, clause.args...) + } + + return NewClause(strings.Join(fragments, " AND "), args) +} + +func (c *Clause) Or(clauses ...*Clause) *Clause { + if len(clauses) == 0 { + return c + } + + var fragments []string + var args []interface{} + clauses = append([]*Clause{c}, clauses...) + for _, clause := range clauses { + fragments = append(fragments, fmt.Sprintf("(%s)", clause.fragment)) + args = append(args, clause.args...) + } + + return NewClause(strings.Join(fragments, " OR "), args) +} diff --git a/bench/queryx/db/queryx/clause_test.go b/bench/queryx/db/queryx/clause_test.go new file mode 100644 index 0000000..b7d548c --- /dev/null +++ b/bench/queryx/db/queryx/clause_test.go @@ -0,0 +1,37 @@ +// Code generated by queryx, DO NOT EDIT. + +package queryx + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestClauseAddOr(t *testing.T) { + c1 := &Clause{ + fragment: "a = ?", + args: []interface{}{1}, + } + c2 := &Clause{ + fragment: "b = ?", + args: []interface{}{"x"}, + } + + c3 := c1.And(c2) + require.Equal(t, "(a = ?) AND (b = ?)", c3.fragment) + require.Equal(t, []interface{}{1, "x"}, c3.args) + + c4 := c1.Or(c2) + require.Equal(t, "(a = ?) OR (b = ?)", c4.fragment) + require.Equal(t, []interface{}{1, "x"}, c4.args) + + c5 := c1.And(c1.Or(c2)) + require.Equal(t, "(a = ?) AND ((a = ?) OR (b = ?))", c5.fragment) + require.Equal(t, []interface{}{1, 1, "x"}, c5.args) + + require.Equal(t, "a = ?", c1.fragment) + require.Equal(t, []interface{}{1}, c1.args) + require.Equal(t, "b = ?", c2.fragment) + require.Equal(t, []interface{}{"x"}, c2.args) +} diff --git a/bench/queryx/db/queryx/config.go b/bench/queryx/db/queryx/config.go new file mode 100644 index 0000000..a430ec8 --- /dev/null +++ b/bench/queryx/db/queryx/config.go @@ -0,0 +1,26 @@ +// Code generated by queryx, DO NOT EDIT. + +package queryx + +import () + +type Config struct { + URL string +} + +func NewConfig(env string) *Config { + switch env { + case "development": + return &Config{ + URL: fixURL("postgresql://postgres:postgres@localhost:5432/test?sslmode=disable"), + } + case "test": + return &Config{ + URL: fixURL(getenv("DATABASE_URL")), + } + } + return nil +} +func fixURL(rawURL string) string { + return rawURL +} diff --git a/bench/queryx/db/queryx/date_test.go b/bench/queryx/db/queryx/date_test.go new file mode 100644 index 0000000..b45d5cf --- /dev/null +++ b/bench/queryx/db/queryx/date_test.go @@ -0,0 +1,40 @@ +// Code generated by queryx, DO NOT EDIT. + +package queryx + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestNewDate(t *testing.T) { + d1 := NewDate("2012-11-10") + require.Equal(t, "2012-11-10", d1.Val.Format("2006-01-02")) + require.True(t, d1.Valid) + + d2 := NewNullableDate(nil) + require.False(t, d2.Valid) +} + +func TestDateJSON(t *testing.T) { + type Foo struct { + X Date `json:"x"` + Y Date `json:"y"` + } + x := NewDate("2012-11-10") + y := NewNullableDate(nil) + s := `{"x":"2012-11-10","y":null}` + + f1 := Foo{X: x, Y: y} + b, err := json.Marshal(f1) + require.NoError(t, err) + require.Equal(t, s, string(b)) + + var f2 Foo + err = json.Unmarshal([]byte(s), &f2) + require.NoError(t, err) + require.Equal(t, x, f2.X) + require.Equal(t, y, f2.Y) +} diff --git a/bench/queryx/db/queryx/datetime_test.go b/bench/queryx/db/queryx/datetime_test.go new file mode 100644 index 0000000..824edf3 --- /dev/null +++ b/bench/queryx/db/queryx/datetime_test.go @@ -0,0 +1,40 @@ +// Code generated by queryx, DO NOT EDIT. + +package queryx + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestNewDatetime(t *testing.T) { + d1 := NewDatetime("2012-12-12 11:10:09") + require.Equal(t, "2012-12-12 11:10:09", d1.Val.Format("2006-01-02 15:04:05")) + require.True(t, d1.Valid) + + d2 := NewNullableDatetime(nil) + require.False(t, d2.Valid) +} + +func TestDatetimeJSON(t *testing.T) { + type Foo struct { + X Datetime `json:"x"` + Y Datetime `json:"y"` + } + x := NewDatetime("2012-12-12 11:10:09") + y := NewNullableDatetime(nil) + s := `{"x":"2012-12-12T03:10:09Z","y":null}` + + f1 := Foo{X: x, Y: y} + b, err := json.Marshal(f1) + require.NoError(t, err) + require.Equal(t, s, string(b)) + + var f2 Foo + err = json.Unmarshal([]byte(s), &f2) + require.NoError(t, err) + require.Equal(t, x, f2.X) + require.Equal(t, y, f2.Y) +} diff --git a/bench/queryx/db/queryx/db.go b/bench/queryx/db/queryx/db.go new file mode 100644 index 0000000..f37b790 --- /dev/null +++ b/bench/queryx/db/queryx/db.go @@ -0,0 +1,110 @@ +// Code generated by queryx, DO NOT EDIT. + +package queryx + +import ( + "database/sql" + "regexp" +) + +type DB interface { + Exec(query string, args ...interface{}) (sql.Result, error) + Query(query string, args ...interface{}) (*sql.Rows, error) +} + +type Adapter struct { + db DB +} + +func NewAdapter(db DB) *Adapter { + return &Adapter{db: db} +} + +func (a *Adapter) Query(query string, args ...interface{}) *Rows { + return &Rows{ + adapter: a, + query: query, + args: args, + } +} + +type Rows struct { + adapter *Adapter + query string + args []interface{} + err error +} + +func (r *Rows) Scan(v interface{}) error { + if r.err != nil { + return r.err + } + var err error + query, args := r.query, r.args + matched1, err := regexp.MatchString(`.* IN (.*?)`, query) + if err != nil { + return err + } + matched2, err := regexp.MatchString(`.* in (.*?)`, query) + if err != nil { + return err + } + if matched1 || matched2 { + query, args, err = In(query, args...) + if err != nil { + return err + } + } + query, args = rebind(query, args) + rows, err := r.adapter.db.Query(query, args...) + if err != nil { + return err + } + err = ScanSlice(rows, v) + if err != nil { + return err + } + return err +} + +type Row struct { + adapter *Adapter + query string + args []interface{} +} + +func (r *Row) Scan(v interface{}) error { + query, args := r.query, r.args + matched1, err := regexp.MatchString(`.* IN (.*?)`, query) + if err != nil { + return err + } + matched2, err := regexp.MatchString(`.* in (.*?)`, query) + if err != nil { + return err + } + if matched1 || matched2 { + query, args, err = In(query, args...) + if err != nil { + return err + } + } + query, args = rebind(query, args) + rows, err := r.adapter.db.Query(query, args...) + if err != nil { + return err + } + err = ScanOne(rows, v) + if err != nil { + return err + } + return err +} + +func (a *Adapter) QueryOne(query string, args ...interface{}) *Row { + return &Row{ + adapter: a, + query: query, + args: args, + } +} diff --git a/bench/queryx/db/queryx/delete.go b/bench/queryx/db/queryx/delete.go new file mode 100644 index 0000000..736b99d --- /dev/null +++ b/bench/queryx/db/queryx/delete.go @@ -0,0 +1,36 @@ +// Code generated by queryx, DO NOT EDIT. + +package queryx + +import "fmt" + +type DeleteStatemnet struct { + from string + where *Clause +} + +func NewDelete() *DeleteStatemnet { + return &DeleteStatemnet{} +} + +func (s *DeleteStatemnet) From(from string) *DeleteStatemnet { + s.from = from + return s +} + +func (s *DeleteStatemnet) Where(expr *Clause) *DeleteStatemnet { + s.where = expr + return s +} + +func (s *DeleteStatemnet) ToSQL() (string, []interface{}) { + sql, args := "", []interface{}{} + sql = fmt.Sprintf("DELETE FROM %s", s.from) + + if s.where != nil { + sql = fmt.Sprintf("%s WHERE %s", sql, s.where.fragment) + args = append(args, s.where.args...) + } + + return sql, args +} diff --git a/bench/queryx/db/queryx/delete_test.go b/bench/queryx/db/queryx/delete_test.go new file mode 100644 index 0000000..82f2d51 --- /dev/null +++ b/bench/queryx/db/queryx/delete_test.go @@ -0,0 +1,15 @@ +package queryx + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestNewDelete(t *testing.T) { + s := NewDelete().From("users") + + sql, args := s.ToSQL() + require.Equal(t, "DELETE FROM users", sql) + require.Equal(t, []interface{}{}, args) +} diff --git a/bench/queryx/db/queryx/env.go b/bench/queryx/db/queryx/env.go new file mode 100644 index 0000000..79c79ec --- /dev/null +++ b/bench/queryx/db/queryx/env.go @@ -0,0 +1,7 @@ +package queryx + +import "os" + +func getenv(k string) string { + return os.Getenv(k) +} diff --git a/bench/queryx/db/queryx/float_test.go b/bench/queryx/db/queryx/float_test.go new file mode 100644 index 0000000..5ff6574 --- /dev/null +++ b/bench/queryx/db/queryx/float_test.go @@ -0,0 +1,40 @@ +// Code generated by queryx, DO NOT EDIT. + +package queryx + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestNewFloat(t *testing.T) { + f1 := NewFloat(2.1) + require.Equal(t, 2.1, f1.Val) + require.True(t, f1.Valid) + + f2 := NewNullableFloat(nil) + require.False(t, f2.Valid) +} + +func TestFloatJSON(t *testing.T) { + type Foo struct { + X Float `json:"x"` + Y Float `json:"y"` + } + x := NewFloat(2.1) + y := NewNullableFloat(nil) + s := `{"x":2.1,"y":null}` + + f1 := Foo{X: x, Y: y} + b, err := json.Marshal(f1) + require.NoError(t, err) + require.Equal(t, s, string(b)) + + var f2 Foo + err = json.Unmarshal([]byte(s), &f2) + require.NoError(t, err) + require.Equal(t, x, f2.X) + require.Equal(t, y, f2.Y) +} diff --git a/bench/queryx/db/queryx/insert.go b/bench/queryx/db/queryx/insert.go new file mode 100644 index 0000000..7e831bd --- /dev/null +++ b/bench/queryx/db/queryx/insert.go @@ -0,0 +1,82 @@ +// Code generated by queryx, DO NOT EDIT. + +package queryx + +import ( + "fmt" + "strings" +) + +type InsertStatement struct { + into string + columns []string + values [][]interface{} + returning []string + onConflict string +} + +func NewInsert() *InsertStatement { + return &InsertStatement{} +} + +func (s *InsertStatement) Into(into string) *InsertStatement { + s.into = into + return s +} + +func (s *InsertStatement) Columns(columns ...string) *InsertStatement { + s.columns = columns + return s +} + +func (s *InsertStatement) Values(values ...interface{}) *InsertStatement { + if len(values) > 0 { + s.values = append(s.values, values) + } + return s +} +func (s *InsertStatement) Returning(returning ...string) *InsertStatement { + s.returning = returning + return s +} + +func (s *InsertStatement) OnConflict(onConflict string) *InsertStatement { + s.onConflict = onConflict + return s +} + +func (s *InsertStatement) ToSQL() (string, []interface{}) { + sql := fmt.Sprintf("INSERT INTO %s", s.into) + + if len(s.columns) > 0 { + sql = fmt.Sprintf("%s(\"%s\")", sql, strings.Join(s.columns, "\", \"")) + } else { + sql = fmt.Sprintf("%s DEFAULT VALUES", sql) + } + values := []string{} + for _, v := range s.values { + ss := []string{} + for range v { + ss = append(ss, "?") + } + values = append(values, fmt.Sprintf("(%s)", strings.Join(ss, ", "))) + } + if len(values) > 0 { + sql = fmt.Sprintf("%s VALUES %s", sql, strings.Join(values, ", ")) + } + + if len(s.returning) > 0 { + sql = fmt.Sprintf("%s RETURNING %s", sql, strings.Join(s.returning, ", ")) + } + + if s.onConflict != "" { + sql = fmt.Sprintf("%s %s", sql, s.onConflict) + } + + args := []interface{}{} + for _, v := range s.values { + args = append(args, v...) + } + + return sql, args +} diff --git a/bench/queryx/db/queryx/insert_test.go b/bench/queryx/db/queryx/insert_test.go new file mode 100644 index 0000000..6eb3065 --- /dev/null +++ b/bench/queryx/db/queryx/insert_test.go @@ -0,0 +1,33 @@ +package queryx + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestNewInsert(t *testing.T) { + s1 := NewInsert().Into("users") + sql, args := s1.ToSQL() + require.Equal(t, "INSERT INTO users DEFAULT VALUES", sql) + require.Equal(t, []interface{}{}, args) + + var columns []string + var values []interface{} + s2 := NewInsert().Into("users").Columns(columns...).Values(values...).Returning("id", "name") + sql, args = s2.ToSQL() + require.Equal(t, "INSERT INTO users DEFAULT VALUES RETURNING id, name", sql) + require.Equal(t, []interface{}{}, args) + + s3 := NewInsert().Into("users").Columns("name", "email").Values("test", "test@example.com") + sql, args = s3.ToSQL() + require.Equal(t, "INSERT INTO users(name, email) VALUES (?, ?)", sql) + require.Equal(t, []interface{}{"test", "test@example.com"}, args) + + s4 := NewInsert().Into("users").Columns("name", "email"). + Values("test1", "test1@example.com"). + Values("test2", "test2@example.com") + sql, args = s4.ToSQL() + require.Equal(t, "INSERT INTO users(name, email) VALUES (?, ?), (?, ?)", sql) + require.Equal(t, []interface{}{"test1", "test1@example.com", "test2", "test2@example.com"}, args) +} diff --git a/bench/queryx/db/queryx/integer.go b/bench/queryx/db/queryx/integer.go new file mode 100644 index 0000000..060eb85 --- /dev/null +++ b/bench/queryx/db/queryx/integer.go @@ -0,0 +1,78 @@ +// Code generated by queryx, DO NOT EDIT. + +package queryx + +import ( + "database/sql" + "database/sql/driver" + "encoding/json" + "strconv" +) + +type Integer struct { + Val int32 + Valid bool + Set bool +} + +func NewInteger(v int32) Integer { + return Integer{Val: v, Valid: true, Set: true} +} + +func NewNullableInteger(v *int32) Integer { + if v != nil { + return NewInteger(*v) + } + return Integer{Set: true} +} + +// Scan implements the Scanner interface. +func (i *Integer) Scan(value interface{}) error { + n := sql.NullInt64{} + err := n.Scan(value) + if err != nil { + return err + } + i.Val, i.Valid = int32(n.Int64), n.Valid + return nil +} + +// Value implements the driver Valuer interface. +func (i Integer) Value() (driver.Value, error) { + if !i.Valid { + return nil, nil + } + return int64(i.Val), nil +} + +// MarshalJSON implements the json.Marshaler interface. +func (i Integer) MarshalJSON() ([]byte, error) { + if !i.Valid { + return json.Marshal(nil) + } + return json.Marshal(i.Val) +} + +// UnmarshalJSON implements the json.Unmarshaler interface. +func (i *Integer) UnmarshalJSON(data []byte) error { + i.Set = true + s := string(data) + if s == "null" { + return nil + } + i.Valid = true + p, err := strconv.ParseInt(s, 10, 32) + if err != nil { + return err + } + i.Val = int32(p) + return nil +} + +// String implements the stringer interface. +func (i Integer) String() string { + if !i.Valid { + return "null" + } + return strconv.Itoa(int(i.Val)) +} diff --git a/bench/queryx/db/queryx/integer_column.go b/bench/queryx/db/queryx/integer_column.go new file mode 100644 index 0000000..00d3e35 --- /dev/null +++ b/bench/queryx/db/queryx/integer_column.go @@ -0,0 +1,88 @@ +// Code generated by queryx, DO NOT EDIT. + +package queryx + +import "fmt" + +type IntegerColumn struct { + Name string + Table *Table +} + +func (t *Table) NewIntegerColumn(name string) *IntegerColumn { + return &IntegerColumn{ + Table: t, + Name: name, + } +} + +func (c *IntegerColumn) EQ(v int32) *Clause { + return &Clause{ + fragment: fmt.Sprintf("%s.%s = ?", c.Table.Name, c.Name), + args: []interface{}{v}, + } +} + +func (c *IntegerColumn) NE(v int32) *Clause { + return &Clause{ + fragment: fmt.Sprintf("%s.%s <> ?", c.Table.Name, c.Name), + args: []interface{}{v}, + } +} + +func (c *IntegerColumn) LT(v int32) *Clause { + return &Clause{ + fragment: fmt.Sprintf("%s.%s < ?", c.Table.Name, c.Name), + args: []interface{}{v}, + } +} + +func (c *IntegerColumn) GT(v int32) *Clause { + return &Clause{ + fragment: fmt.Sprintf("%s.%s > ?", c.Table.Name, c.Name), + args: []interface{}{v}, + } +} + +func (c *IntegerColumn) LE(v int32) *Clause { + return &Clause{ + fragment: fmt.Sprintf("%s.%s <= ?", c.Table.Name, c.Name), + args: []interface{}{v}, + } +} + +func (c *IntegerColumn) GE(v int32) *Clause { + return &Clause{ + fragment: fmt.Sprintf("%s.%s >= ?", c.Table.Name, c.Name), + args: []interface{}{v}, + } +} + +func (c *IntegerColumn) In(v []int32) *Clause { + return &Clause{ + fragment: fmt.Sprintf("%s.%s IN (?)", c.Table.Name, c.Name), + args: []interface{}{v}, + } +} + +func (c *IntegerColumn) InRange(start int32, end int32) *Clause { + return &Clause{ + fragment: fmt.Sprintf("%s.%s >= ? and %s.%s< ?", c.Table.Name, c.Name, c.Table.Name, c.Name), + args: []interface{}{start, end}, + } +} + +func (c *IntegerColumn) Between(start int32, end int32) *Clause { + return &Clause{ + fragment: fmt.Sprintf("%s.%s >= ? and %s.%s<= ?", c.Table.Name, c.Name, c.Table.Name, c.Name), + args: []interface{}{start, end}, + } +} + +func (c *IntegerColumn) Asc() string { + return fmt.Sprintf("%s.%s ASC", c.Table.Name, c.Name) +} + +func (c *IntegerColumn) Desc() string { + return fmt.Sprintf("%s.%s DESC", c.Table.Name, c.Name) +} diff --git a/bench/queryx/db/queryx/integer_test.go b/bench/queryx/db/queryx/integer_test.go new file mode 100644 index 0000000..1a2c2ec --- /dev/null +++ b/bench/queryx/db/queryx/integer_test.go @@ -0,0 +1,40 @@ +// Code generated by queryx, DO NOT EDIT. + +package queryx + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestNewInteger(t *testing.T) { + i1 := NewInteger(2) + require.Equal(t, int32(2), i1.Val) + require.True(t, i1.Valid) + + i2 := NewNullableInteger(nil) + require.False(t, i2.Valid) +} + +func TestIntegerJSON(t *testing.T) { + type Foo struct { + X Integer `json:"x"` + Y Integer `json:"y"` + } + x := NewInteger(2) + y := NewNullableInteger(nil) + s := `{"x":2,"y":null}` + + f1 := Foo{X: x, Y: y} + b, err := json.Marshal(f1) + require.NoError(t, err) + require.Equal(t, s, string(b)) + + var f2 Foo + err = json.Unmarshal([]byte(s), &f2) + require.NoError(t, err) + require.Equal(t, x, f2.X) + require.Equal(t, y, f2.Y) +} diff --git a/bench/queryx/db/queryx/json_test.go b/bench/queryx/db/queryx/json_test.go new file mode 100644 index 0000000..3987900 --- /dev/null +++ b/bench/queryx/db/queryx/json_test.go @@ -0,0 +1,42 @@ +package queryx + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestNewJSON(t *testing.T) { + m := map[string]interface{}{"a": 1} + j1 := NewJSON(m) + require.Equal(t, m, j1.Val) + require.True(t, j1.Valid) + + j2 := NewNullableJSON(nil) + require.False(t, j2.Valid) + + j3 := NewNullableJSON(m) + require.True(t, j3.Valid) +} + +func TestJSONJSON(t *testing.T) { + type Foo struct { + X JSON `json:"x"` + Y JSON `json:"y"` + } + x := NewJSON(map[string]interface{}{"a": "b"}) + y := NewNullableJSON(nil) + s := `{"x":{"a":"b"},"y":null}` + + f1 := Foo{X: x, Y: y} + b, err := json.Marshal(f1) + require.NoError(t, err) + require.Equal(t, s, string(b)) + + var f2 Foo + err = json.Unmarshal([]byte(s), &f2) + require.NoError(t, err) + require.Equal(t, x, f2.X) + require.Equal(t, y, f2.Y) +} diff --git a/bench/queryx/db/queryx/logger.go b/bench/queryx/db/queryx/logger.go new file mode 100644 index 0000000..8d0fe04 --- /dev/null +++ b/bench/queryx/db/queryx/logger.go @@ -0,0 +1,15 @@ +// Code generated by queryx, DO NOT EDIT. + +package queryx + +type Logger interface { + Print(v ...interface{}) + Printf(format string, v ...interface{}) + Println(v ...interface{}) + Panic(v ...interface{}) + Panicf(format string, v ...interface{}) + Panicln(v ...interface{}) + Fatal(v ...interface{}) + Fatalf(format string, v ...interface{}) + Fatalln(v ...interface{}) +} diff --git a/bench/queryx/db/queryx/model_change.go b/bench/queryx/db/queryx/model_change.go new file mode 100644 index 0000000..f128d87 --- /dev/null +++ b/bench/queryx/db/queryx/model_change.go @@ -0,0 +1,143 @@ +// Code generated by queryx, DO NOT EDIT. + +package queryx + +type ModelChange struct { + ID BigInt + Name String + Title String + Fax String + Web String + Age BigInt + Right Boolean + Counter Integer +} + +func (c *ModelChange) Changes() (columns []string, values []interface{}) { + if c == nil { + return columns, values + } + if c.ID.Set { + columns = append(columns, "id") + values = append(values, c.ID) + } + if c.Name.Set { + columns = append(columns, "name") + values = append(values, c.Name) + } + if c.Title.Set { + columns = append(columns, "title") + values = append(values, c.Title) + } + if c.Fax.Set { + columns = append(columns, "fax") + values = append(values, c.Fax) + } + if c.Web.Set { + columns = append(columns, "web") + values = append(values, c.Web) + } + if c.Age.Set { + columns = append(columns, "age") + values = append(values, c.Age) + } + if c.Right.Set { + columns = append(columns, "right") + values = append(values, c.Right) + } + if c.Counter.Set { + columns = append(columns, "counter") + values = append(values, c.Counter) + } + return columns, values +} + +func (c *ModelChange) SetID(id int64) *ModelChange { + c.ID = NewBigInt(id) + c.ID.Set = true + return c +} + +func (c *ModelChange) SetName(name string) *ModelChange { + c.Name = NewString(name) + c.Name.Set = true + return c +} + +func (c *ModelChange) SetNullableName(name *string) *ModelChange { + c.Name = NewNullableString(name) + c.Name.Set = true + return c +} + +func (c *ModelChange) SetTitle(title string) *ModelChange { + c.Title = NewString(title) + c.Title.Set = true + return c +} + +func (c *ModelChange) SetNullableTitle(title *string) *ModelChange { + c.Title = NewNullableString(title) + c.Title.Set = true + return c +} + +func (c *ModelChange) SetFax(fax string) *ModelChange { + c.Fax = NewString(fax) + c.Fax.Set = true + return c +} + +func (c *ModelChange) SetNullableFax(fax *string) *ModelChange { + c.Fax = NewNullableString(fax) + c.Fax.Set = true + return c +} + +func (c *ModelChange) SetWeb(web string) *ModelChange { + c.Web = NewString(web) + c.Web.Set = true + return c +} + +func (c *ModelChange) SetNullableWeb(web *string) *ModelChange { + c.Web = NewNullableString(web) + c.Web.Set = true + return c +} + +func (c *ModelChange) SetAge(age int64) *ModelChange { + c.Age = NewBigInt(age) + c.Age.Set = true + return c +} + +func (c *ModelChange) SetNullableAge(age *int64) *ModelChange { + c.Age = NewNullableBigInt(age) + c.Age.Set = true + return c +} + +func (c *ModelChange) SetRight(right bool) *ModelChange { + c.Right = NewBoolean(right) + c.Right.Set = true + return c +} + +func (c *ModelChange) SetNullableRight(right *bool) *ModelChange { + c.Right = NewNullableBoolean(right) + c.Right.Set = true + return c +} + +func (c *ModelChange) SetCounter(counter int32) *ModelChange { + c.Counter = NewInteger(counter) + c.Counter.Set = true + return c +} + +func (c *ModelChange) SetNullableCounter(counter *int32) *ModelChange { + c.Counter = NewNullableInteger(counter) + c.Counter.Set = true + return c +} diff --git a/bench/queryx/db/queryx/scan.go b/bench/queryx/db/queryx/scan.go new file mode 100644 index 0000000..4dde2a1 --- /dev/null +++ b/bench/queryx/db/queryx/scan.go @@ -0,0 +1,135 @@ +// Code generated by queryx, DO NOT EDIT. + +package queryx + +import ( + "database/sql" + "fmt" + "reflect" + "strings" +) + +func ScanOne(rows *sql.Rows, v interface{}) error { + rv := reflect.ValueOf(v) + if rv.Kind() != reflect.Ptr { + return fmt.Errorf("must pass pointer") + } + + columns, err := rows.Columns() + if err != nil { + return err + } + + typ := rv.Type().Elem() + + // column -> index + names := make(map[string]int, typ.NumField()) + for i := 0; i < typ.NumField(); i++ { + f := typ.Field(i) + names[columnName(f)] = i + } + + if !rows.Next() { + return sql.ErrNoRows + } + + indexes := make(map[int]int, typ.NumField()) + for i, c := range columns { + name := strings.ToLower(strings.Split(c, "(")[0]) + index, ok := names[name] + if !ok { + return fmt.Errorf("name not found") + } + indexes[i] = index + } + + values := make([]interface{}, len(columns)) + for i := range columns { + t := typ.Field(indexes[i]).Type + values[i] = reflect.New(t).Interface() + + } + + // scan into interfaces + if err := rows.Scan(values...); err != nil { + return err + } + + for i, v := range values { + reflect.Indirect(rv).Field(indexes[i]).Set(reflect.Indirect(reflect.ValueOf(v))) + } + + if rows.Next() { + return fmt.Errorf("more than one row") + } + + return nil +} + +func ScanSlice(rows *sql.Rows, v interface{}) error { + rv := reflect.ValueOf(v) + if rv.Kind() != reflect.Ptr { + return fmt.Errorf("must pass pointer") + } + + rv = reflect.Indirect(rv) + if k := rv.Kind(); k != reflect.Slice { + return fmt.Errorf("must pass slice") + } + + columns, err := rows.Columns() + if err != nil { + return err + } + + typ := rv.Type().Elem() + + names := make(map[string]int, typ.NumField()) + for i := 0; i < typ.NumField(); i++ { + f := typ.Field(i) + names[columnName(f)] = i + } + + indexes := make(map[int]int, typ.NumField()) + for i, c := range columns { + name := strings.ToLower(strings.Split(c, "(")[0]) + index, ok := names[name] + if !ok { + return fmt.Errorf("name %+v not found", name) + } + indexes[i] = index + } + + for rows.Next() { + values := make([]interface{}, len(columns)) + for i := range columns { + t := typ.Field(indexes[i]).Type + values[i] = reflect.New(t).Interface() + } + + // scan into interfaces + if err := rows.Scan(values...); err != nil { + return err + } + + // convert to reflect.Value + e := reflect.New(typ).Elem() + for i, v := range values { + fv := e.Field(indexes[i]) + fv.Set(reflect.Indirect(reflect.ValueOf(v))) + } + + vv := reflect.Append(rv, e) + rv.Set(vv) + } + + return nil +} + +func columnName(f reflect.StructField) string { + name := strings.ToLower(f.Name) + if tag, ok := f.Tag.Lookup("db"); ok { + name = tag + } + return name +} diff --git a/bench/queryx/db/queryx/schema.go b/bench/queryx/db/queryx/schema.go new file mode 100644 index 0000000..5a8d2fe --- /dev/null +++ b/bench/queryx/db/queryx/schema.go @@ -0,0 +1,52 @@ +// Code generated by queryx, DO NOT EDIT. + +package queryx + +type Schema struct { + Model *Table + ModelID *BigIntColumn + ModelName *StringColumn + ModelTitle *StringColumn + ModelFax *StringColumn + ModelWeb *StringColumn + ModelAge *BigIntColumn + ModelRight *BooleanColumn + ModelCounter *IntegerColumn +} + +func NewSchema() *Schema { + model := NewTable("models") + + return &Schema{ + Model: model, + ModelID: model.NewBigIntColumn("id"), + ModelName: model.NewStringColumn("name"), + ModelTitle: model.NewStringColumn("title"), + ModelFax: model.NewStringColumn("fax"), + ModelWeb: model.NewStringColumn("web"), + ModelAge: model.NewBigIntColumn("age"), + ModelRight: model.NewBooleanColumn("right"), + ModelCounter: model.NewIntegerColumn("counter"), + } +} + +func (s *Schema) And(clauses ...*Clause) *Clause { + return clauses[0].And(clauses[1:]...) +} + +func (s *Schema) Or(clauses ...*Clause) *Clause { + return clauses[0].Or(clauses[1:]...) +} + +func (s *Schema) ChangeModel() *ModelChange { + return &ModelChange{ + ID: BigInt{}, + Name: String{}, + Title: String{}, + Fax: String{}, + Web: String{}, + Age: BigInt{}, + Right: Boolean{}, + Counter: Integer{}, + } +} diff --git a/bench/queryx/db/queryx/select.go b/bench/queryx/db/queryx/select.go new file mode 100644 index 0000000..fec8eeb --- /dev/null +++ b/bench/queryx/db/queryx/select.go @@ -0,0 +1,142 @@ +// Code generated by queryx, DO NOT EDIT. + +package queryx + +import ( + "fmt" + "strings" +) + +type SelectStatement struct { + selection []string + from string + where *Clause + limit *int + offset *int + order []string + group string + having string + distinct []string + joins []string +} + +func NewSelect() *SelectStatement { + return &SelectStatement{} +} + +func (s *SelectStatement) Select(selection ...string) *SelectStatement { + s.selection = selection + return s +} + +func (s *SelectStatement) From(from string) *SelectStatement { + s.from = from + return s +} + +func (s *SelectStatement) Where(clauses ...*Clause) *SelectStatement { + if len(clauses) == 0 { + return s + } + + if s.where == nil { + s.where = clauses[0].And(clauses[1:]...) + } else { + s.where = s.where.And(clauses...) + } + + return s +} + +func (s *SelectStatement) Limit(limit int) *SelectStatement { + s.limit = &limit + return s +} + +func (s *SelectStatement) Offset(offset int) *SelectStatement { + s.offset = &offset + return s +} + +func (s *SelectStatement) Order(order ...string) *SelectStatement { + s.order = append(s.order, order...) + return s +} + +func (s *SelectStatement) Distinct(distinct ...string) *SelectStatement { + s.distinct = distinct + return s +} + +func (s *SelectStatement) GroupBy(group string) *SelectStatement { + s.group = group + return s +} + +func (s *SelectStatement) Having(having string) *SelectStatement { + s.having = having + return s +} + +func (s *SelectStatement) Join(join ...string) *SelectStatement { + s.joins = append(s.joins, join...) + return s +} + +// convert select statement to update statement +func (s *SelectStatement) Update() *UpdateStatement { + if s.limit != nil { + return NewUpdate().Table(s.from).Where(s.where) // TODO: convert into subquery + } + return NewUpdate().Table(s.from).Where(s.where) +} + +// convert select statement to delete statement +func (s *SelectStatement) Delete() *DeleteStatemnet { + return NewDelete().From(s.from).Where(s.where) +} + +func (s *SelectStatement) ToSQL() (string, []interface{}) { + var query string + if len(s.distinct) > 0 { + query = fmt.Sprintf("SELECT DISTINCT %s FROM %s", strings.Join(s.distinct, ", "), s.from) + } else { + query = fmt.Sprintf("SELECT %s FROM %s", strings.Join(s.selection, ", "), s.from) + } + args := []interface{}{} + + if len(s.joins) > 0 { + for i := 0; i < len(s.joins); i++ { + query = fmt.Sprintf("%s %s", query, s.joins[i]) + } + } + + if s.where != nil { + query = fmt.Sprintf("%s WHERE %s", query, s.where.fragment) + args = append(args, s.where.args...) + } + + if s.group != "" { + query = fmt.Sprintf("%s GROUP BY %s", query, s.group) + } + + if s.having != "" { + query = fmt.Sprintf("%s HAVING %s", query, s.having) + } + + if s.order != nil { + query = fmt.Sprintf("%s ORDER BY %s", query, strings.Join(s.order, ", ")) + } + + if s.limit != nil { + query = fmt.Sprintf("%s LIMIT ?", query) + args = append(args, *s.limit) + } + + if s.offset != nil { + query = fmt.Sprintf("%s OFFSET ?", query) + args = append(args, *s.offset) + } + + return query, args +} diff --git a/bench/queryx/db/queryx/select_test.go b/bench/queryx/db/queryx/select_test.go new file mode 100644 index 0000000..5c4b897 --- /dev/null +++ b/bench/queryx/db/queryx/select_test.go @@ -0,0 +1,35 @@ +package queryx + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestSelect(t *testing.T) { + s := NewSelect().Select("users.*").From("users") + sql, args := s.ToSQL() + require.Equal(t, `SELECT users.* FROM users`, sql) + require.Equal(t, []interface{}{}, args) + + sql, args = s.Update().Columns("name", "email").Values("test", "test@example.com").ToSQL() + require.Equal(t, `UPDATE users SET name = ?, email = ?`, sql) + require.Equal(t, []interface{}{"test", "test@example.com"}, args) + + s1 := s.Limit(1) + sql, args = s1.ToSQL() + require.Equal(t, `SELECT users.* FROM users LIMIT ?`, sql) + require.Equal(t, []interface{}{1}, args) +} + +func TestSelectWhere(t *testing.T) { + s1 := NewSelect().Select("users.*").From("users").Where(NewClause("id = ?", []interface{}{1})) + sql, args := s1.ToSQL() + require.Equal(t, `SELECT users.* FROM users WHERE id = ?`, sql) + require.Equal(t, []interface{}{1}, args) + + s1.Where(NewClause("name = ?", []interface{}{"test"})) + sql, args = s1.ToSQL() + require.Equal(t, `SELECT users.* FROM users WHERE (id = ?) AND (name = ?)`, sql) + require.Equal(t, []interface{}{1, "test"}, args) +} diff --git a/bench/queryx/db/queryx/string.go b/bench/queryx/db/queryx/string.go new file mode 100644 index 0000000..d47dd75 --- /dev/null +++ b/bench/queryx/db/queryx/string.go @@ -0,0 +1,72 @@ +// Code generated by queryx, DO NOT EDIT. + +package queryx + +import ( + "database/sql" + "database/sql/driver" + "encoding/json" + "fmt" +) + +type String struct { + Val string + Valid bool + Set bool +} + +func NewString(v string) String { + return String{Val: v, Valid: true, Set: true} +} + +func NewNullableString(v *string) String { + if v != nil { + return NewString(*v) + } + return String{Set: true} +} + +// Scan implements the Scanner interface. +func (s *String) Scan(value interface{}) error { + ns := sql.NullString{String: s.Val} + err := ns.Scan(value) + s.Val, s.Valid = ns.String, ns.Valid + return err +} + +// Value implements the driver Valuer interface. +func (s String) Value() (driver.Value, error) { + if !s.Valid { + return nil, nil + } + return s.Val, nil +} + +// MarshalJSON implements the json.Marshaler interface. +func (s String) MarshalJSON() ([]byte, error) { + if !s.Valid { + return json.Marshal(nil) + } + return json.Marshal(s.Val) +} + +// UnmarshalJSON implements the json.Unmarshaler interface. +func (s *String) UnmarshalJSON(data []byte) error { + s.Set = true + if string(data) == "null" { + return nil + } + s.Valid = true + if err := json.Unmarshal(data, &s.Val); err != nil { + return err + } + return nil +} + +// String implements the stringer interface. +func (s String) String() string { + if !s.Valid { + return "null" + } + return fmt.Sprintf(`"%s"`, s.Val) +} diff --git a/bench/queryx/db/queryx/string_column.go b/bench/queryx/db/queryx/string_column.go new file mode 100644 index 0000000..00601c9 --- /dev/null +++ b/bench/queryx/db/queryx/string_column.go @@ -0,0 +1,93 @@ +// Code generated by queryx, DO NOT EDIT. + +package queryx + +import "fmt" + +type StringColumn struct { + Name string + Table *Table +} + +func (t *Table) NewStringColumn(name string) *StringColumn { + return &StringColumn{ + Table: t, + Name: name, + } +} + +func (c *StringColumn) NE(v string) *Clause { + return &Clause{ + fragment: fmt.Sprintf("%s.%s <> ?", c.Table.Name, c.Name), + args: []interface{}{v}, + } +} +func (c *StringColumn) EQ(v string) *Clause { + return &Clause{ + fragment: fmt.Sprintf("lower(%s.%s) = lower(?)", c.Table.Name, c.Name), + args: []interface{}{v}, + } +} + +func (c *StringColumn) IEQ(v string) *Clause { + return &Clause{ + fragment: fmt.Sprintf("%s.%s = ?", c.Table.Name, c.Name), + args: []interface{}{v}, + } +} + +func (c *StringColumn) In(v []string) *Clause { + return &Clause{ + fragment: fmt.Sprintf("%s.%s IN (?)", c.Table.Name, c.Name), + args: []interface{}{v}, + } +} + +func (c *StringColumn) Like(v string) *Clause { + return &Clause{ + fragment: fmt.Sprintf("%s.%s ilike ?", c.Table.Name, c.Name), + args: []interface{}{fmt.Sprintf("%s%s%s", "%", v, "%")}, + } +} + +func (c *StringColumn) ILike(v string) *Clause { + return &Clause{ + fragment: fmt.Sprintf("%s.%s like ?", c.Table.Name, c.Name), + args: []interface{}{fmt.Sprintf("%s%s%s", "%", v, "%")}, + } +} +func (c *StringColumn) IStartsWith(v string) *Clause { + return &Clause{ + fragment: fmt.Sprintf("%s.%s like ?", c.Table.Name, c.Name), + args: []interface{}{fmt.Sprintf("%s%s", v, "%")}, + } +} + +func (c *StringColumn) StartsWith(v string) *Clause { + return &Clause{ + fragment: fmt.Sprintf("%s.%s ilike ?", c.Table.Name, c.Name), + args: []interface{}{fmt.Sprintf("%s%s", v, "%")}, + } +} + +func (c *StringColumn) EndsWith(v string) *Clause { + return &Clause{ + fragment: fmt.Sprintf("%s.%s ilike ?", c.Table.Name, c.Name), + args: []interface{}{fmt.Sprintf("%s%s", "%", v)}, + } +} + +func (c *StringColumn) IEndsWith(v string) *Clause { + return &Clause{ + fragment: fmt.Sprintf("%s.%s like ?", c.Table.Name, c.Name), + args: []interface{}{fmt.Sprintf("%s%s", "%", v)}, + } +} + +func (c *StringColumn) Asc() string { + return fmt.Sprintf("%s.%s ASC", c.Table.Name, c.Name) +} + +func (c *StringColumn) Desc() string { + return fmt.Sprintf("%s.%s DESC", c.Table.Name, c.Name) +} diff --git a/bench/queryx/db/queryx/string_test.go b/bench/queryx/db/queryx/string_test.go new file mode 100644 index 0000000..d2e7644 --- /dev/null +++ b/bench/queryx/db/queryx/string_test.go @@ -0,0 +1,40 @@ +// Code generated by queryx, DO NOT EDIT. + +package queryx + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestNewString(t *testing.T) { + s1 := NewString("ss") + require.Equal(t, "ss", s1.Val) + require.True(t, s1.Valid) + + s2 := NewNullableString(nil) + require.False(t, s2.Valid) +} + +func TestStringJSON(t *testing.T) { + type Foo struct { + X String `json:"x"` + Y String `json:"y"` + } + x := NewString("ss") + y := NewNullableString(nil) + s := `{"x":"ss","y":null}` + + f1 := Foo{X: x, Y: y} + b, err := json.Marshal(f1) + require.NoError(t, err) + require.Equal(t, s, string(b)) + + var f2 Foo + err = json.Unmarshal([]byte(s), &f2) + require.NoError(t, err) + require.Equal(t, x, f2.X) + require.Equal(t, y, f2.Y) +} diff --git a/bench/queryx/db/queryx/table.go b/bench/queryx/db/queryx/table.go new file mode 100644 index 0000000..75a2e9a --- /dev/null +++ b/bench/queryx/db/queryx/table.go @@ -0,0 +1,13 @@ +// Code generated by queryx, DO NOT EDIT. + +package queryx + +type Table struct { + Name string +} + +func NewTable(name string) *Table { + return &Table{ + Name: name, + } +} diff --git a/bench/queryx/db/queryx/time_test.go b/bench/queryx/db/queryx/time_test.go new file mode 100644 index 0000000..55bb768 --- /dev/null +++ b/bench/queryx/db/queryx/time_test.go @@ -0,0 +1,40 @@ +// Code generated by queryx, DO NOT EDIT. + +package queryx + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestNewTime(t *testing.T) { + t1 := NewTime("12:11:10") + require.Equal(t, "12:11:10", t1.Val.Format("15:04:05")) + require.True(t, t1.Valid) + + t2 := NewNullableTime(nil) + require.False(t, t2.Valid) +} + +func TestTimeJSON(t *testing.T) { + type Foo struct { + X Time `json:"x"` + Y Time `json:"y"` + } + x := NewTime("12:11:10") + y := NewNullableTime(nil) + s := `{"x":"12:11:10","y":null}` + + f1 := Foo{X: x, Y: y} + b, err := json.Marshal(f1) + require.NoError(t, err) + require.Equal(t, s, string(b)) + + var f2 Foo + err = json.Unmarshal([]byte(s), &f2) + require.NoError(t, err) + require.Equal(t, x, f2.X) + require.Equal(t, y, f2.Y) +} diff --git a/bench/queryx/db/queryx/update.go b/bench/queryx/db/queryx/update.go new file mode 100644 index 0000000..2388cfb --- /dev/null +++ b/bench/queryx/db/queryx/update.go @@ -0,0 +1,67 @@ +// Code generated by queryx, DO NOT EDIT. + +package queryx + +import ( + "fmt" + "strings" +) + +type UpdateStatement struct { + table string + columns []string + values []interface{} + where *Clause + returning string +} + +func NewUpdate() *UpdateStatement { + return &UpdateStatement{} +} + +func (s *UpdateStatement) Table(table string) *UpdateStatement { + s.table = table + return s +} + +func (s *UpdateStatement) Columns(columns ...string) *UpdateStatement { + s.columns = columns + return s +} + +func (s *UpdateStatement) Values(values ...interface{}) *UpdateStatement { + s.values = values + return s +} + +func (s *UpdateStatement) Where(expr *Clause) *UpdateStatement { + s.where = expr + return s +} + +func (s *UpdateStatement) Returning(returning string) *UpdateStatement { + s.returning = returning + return s +} + +func (s *UpdateStatement) ToSQL() (string, []interface{}) { + sql, args := fmt.Sprintf("UPDATE %s SET", s.table), s.values + + sets := []string{} + for _, col := range s.columns { + sets = append(sets, fmt.Sprintf("%s = ?", col)) + } + + sql = fmt.Sprintf("%s %s", sql, strings.Join(sets, ", ")) + + if s.where != nil { + sql = fmt.Sprintf("%s WHERE %s", sql, s.where.fragment) + args = append(args, s.where.args...) + } + + if s.returning != "" { + sql = fmt.Sprintf("%s RETURNING %s", sql, s.returning) + } + + return sql, args +} diff --git a/bench/queryx/db/queryx/update_test.go b/bench/queryx/db/queryx/update_test.go new file mode 100644 index 0000000..0d5e40e --- /dev/null +++ b/bench/queryx/db/queryx/update_test.go @@ -0,0 +1,14 @@ +package queryx + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestNewUpdate(t *testing.T) { + s := NewUpdate().Table("users").Columns("name", "email").Values("test", "test@example.com") + sql, args := s.ToSQL() + require.Equal(t, "UPDATE users SET name = ?, email = ?", sql) + require.Equal(t, []interface{}{"test", "test@example.com"}, args) +} diff --git a/bench/queryx/db/queryx/uuid_test.go b/bench/queryx/db/queryx/uuid_test.go new file mode 100644 index 0000000..3fcd2ee --- /dev/null +++ b/bench/queryx/db/queryx/uuid_test.go @@ -0,0 +1,40 @@ +// Code generated by queryx, DO NOT EDIT. + +package queryx + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestNewUUID(t *testing.T) { + u1 := NewUUID("a81e44c5-7e18-4dfe-b9b3-d9280629d2ef") + require.Equal(t, "a81e44c5-7e18-4dfe-b9b3-d9280629d2ef", u1.Val) + require.True(t, u1.Valid) + + u2 := NewNullableUUID(nil) + require.False(t, u2.Valid) +} + +func TestUUIDJSON(t *testing.T) { + type Foo struct { + X UUID `json:"x"` + Y UUID `json:"y"` + } + x := NewUUID("a81e44c5-7e18-4dfe-b9b3-d9280629d2ef") + y := NewNullableUUID(nil) + s := `{"x":"a81e44c5-7e18-4dfe-b9b3-d9280629d2ef","y":null}` + + f1 := Foo{X: x, Y: y} + b, err := json.Marshal(f1) + require.NoError(t, err) + require.Equal(t, s, string(b)) + + var f2 Foo + err = json.Unmarshal([]byte(s), &f2) + require.NoError(t, err) + require.Equal(t, x, f2.X) + require.Equal(t, y, f2.Y) +} diff --git a/bench/queryx/schema.hcl b/bench/queryx/schema.hcl new file mode 100644 index 0000000..47cb81d --- /dev/null +++ b/bench/queryx/schema.hcl @@ -0,0 +1,41 @@ +database "db" { + adapter = "postgresql" + time_zone = "Asia/Shanghai" + + config "development" { + url = "postgresql://postgres:postgres@localhost:5432/test?sslmode=disable" + } + config "test" { + url = env("DATABASE_URL") + } + + generator "client-golang" { + test = true + } + + model "Model" { + timestamps = false + column "name" { + type = string + } + column "title" { + type = string + } + column "fax" { + type = string + } + column "web" { + type = string + } + column "age" { + type = bigint + } + column "right" { + type = boolean + } + column "counter" { + type = integer + } + } + +} diff --git a/go-orm-benchmarks b/go-orm-benchmarks new file mode 100644 index 0000000..befe653 Binary files /dev/null and b/go-orm-benchmarks differ diff --git a/go.mod b/go.mod index 8bcdfa0..a82d23b 100644 --- a/go.mod +++ b/go.mod @@ -32,7 +32,7 @@ require ( ) require ( - ariga.io/atlas v0.12.0 // indirect + ariga.io/atlas v0.14.2 // indirect github.com/Masterminds/semver/v3 v3.2.1 // indirect github.com/agext/levenshtein v1.2.3 // indirect github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect @@ -90,6 +90,7 @@ require ( github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d // indirect github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e // indirect github.com/spf13/cast v1.5.1 // indirect + github.com/swiftcarrot/queryx v0.2.8 // indirect github.com/syndtr/goleveldb v1.0.0 // indirect github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect github.com/vmihailenco/bufpool v0.1.11 // indirect diff --git a/go.sum b/go.sum index 083b0f1..7ff5f86 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ ariga.io/atlas v0.12.0 h1:jDfjxT3ppKhzqLS26lZv9ni7p9TVNrhy7SQquaF7bPs= ariga.io/atlas v0.12.0/go.mod h1:+TR129FJZ5Lvzms6dvCeGWh1yR6hMvmXBhug4hrNIGk= +ariga.io/atlas v0.14.2 h1:efxCuSGnDuhx7xm4JaqImR6xd+PqyizgGy5u/XUEI/g= +ariga.io/atlas v0.14.2/go.mod h1:isZrlzJ5cpoCoKFoY9knZug7Lq4pP1cm8g3XciLZ0Pw= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= @@ -73,6 +75,7 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DATA-DOG/go-sqlmock v1.4.1/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= +github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/Knetic/govaluate v3.0.0+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= @@ -87,6 +90,7 @@ github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWX github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= +github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo= github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -102,6 +106,7 @@ github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apmckinlay/gsuneido v0.0.0-20190404155041-0b6cd442a18f/go.mod h1:JU2DOj5Fc6rol0yaT79Csr47QR0vONGwJtBNGRD7jmc= github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM= +github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk= github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw= github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= @@ -164,6 +169,7 @@ github.com/couchbase/go-couchbase v0.0.0-20200519150804-63f3cdb75e0d/go.mod h1:T github.com/couchbase/gomemcached v0.0.0-20200526233749-ec430f949808/go.mod h1:srVSlQLB8iXBVXHgnqemxUXqN6FCvClgCMPCsjBDR7c= github.com/couchbase/goutils v0.0.0-20180530154633-e865a1461c8a/go.mod h1:BQwMFlJzDjFDG3DJUdU0KORxn88UlsOULuxLExMh3Hs= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= @@ -325,6 +331,7 @@ github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= +github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -444,6 +451,8 @@ github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+l github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/hcl/v2 v2.10.0/go.mod h1:FwWsfWEjyV/CMj8s/gqAuiviY72rJ1/oayI9WftqcKg= +github.com/hashicorp/hcl/v2 v2.13.0/go.mod h1:e4z5nxYlWNPdDSNYX+ph14EvWYMFm3eP0zIUqPc2jr0= github.com/hashicorp/hcl/v2 v2.17.0 h1:z1XvSUyXd1HP10U4lrLg5e0JMVz6CPaJvAgxM0KNZVY= github.com/hashicorp/hcl/v2 v2.17.0/go.mod h1:gJyW2PTShkJqQBKpAmPO3yxMxIuoXkOF2TpqXzrQyx4= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= @@ -594,6 +603,7 @@ github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -601,6 +611,7 @@ github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/ledisdb/ledisdb v0.0.0-20200510135210-d35789ec47e6/go.mod h1:n931TsDuKuq+uX4v1fulaMbA/7ZLLhjc85h7chZGBCQ= @@ -611,6 +622,7 @@ github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lib/pq v1.10.5/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.6/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= @@ -650,6 +662,7 @@ github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= +github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U= github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= @@ -670,6 +683,7 @@ github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HK github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= @@ -808,6 +822,7 @@ github.com/segmentio/fasthash v1.0.3 h1:EI9+KE1EwvMLBWwjpRDc+fEM+prwxDYbslddQGtr github.com/segmentio/fasthash v1.0.3/go.mod h1:waKX8l2N8yckOgmSsXJi7x1ZfdKZ4x7KRMzBtS3oedY= github.com/serenize/snaker v0.0.0-20201027110005-a7ad2135616e h1:zWKUYT07mGmVBH+9UgnHXd/ekCK99C8EbDSAt5qsjXE= github.com/serenize/snaker v0.0.0-20201027110005-a7ad2135616e/go.mod h1:Yow6lPLSAXx2ifx470yD/nUe22Dv5vBvxK/UK9UUTVs= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= @@ -847,10 +862,12 @@ github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155 github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g= github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.12.0/go.mod h1:b6COn30jlNxbm/V2IqWiNWkJ+vZNiMNksliPCiuKtSI= github.com/ssdb/gossdb v0.0.0-20180723034631-88f6b59b84ec/go.mod h1:QBvMkMya+gXctz3kmljlUCu/yB3GZ6oee+dUozsezQE= @@ -869,6 +886,7 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1-0.20210427113832-6241f9ab9942/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.5/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= @@ -876,6 +894,8 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= github.com/subosito/gotenv v1.3.0/go.mod h1:YzJjq/33h7nrwdY+iHMhEOEEbW0ovIz0tB6t6PwAXzs= github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= +github.com/swiftcarrot/queryx v0.2.8 h1:LBT/hq4XXN00i7ACB8ADdiZ0LuqwfVrsUzizLs8AlY0= +github.com/swiftcarrot/queryx v0.2.8/go.mod h1:twSuo07JjJUq2Lru3uW7SlW3pB1xZdHPp1coFQ8ePQg= github.com/syndtr/goleveldb v0.0.0-20160425020131-cfa635847112/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0= github.com/syndtr/goleveldb v0.0.0-20181127023241-353a9fca669c/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0= github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= @@ -897,9 +917,12 @@ github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijb github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/vmihailenco/bufpool v0.1.11 h1:gOq2WmBrq0i2yW5QJ16ykccQ4wH9UyEsgLm6czKAd94= github.com/vmihailenco/bufpool v0.1.11/go.mod h1:AFf/MOy3l2CFTKbxwt0mp2MwnqjNEs5H/UxrkA5jxTQ= +github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= +github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4= github.com/vmihailenco/msgpack/v5 v5.3.4/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc= github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU= github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc= +github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= github.com/vmihailenco/tagparser v0.1.2 h1:gnjoVuB/kljJ5wICEEOpx98oXMWPLj22G67Vbd1qPqc= github.com/vmihailenco/tagparser v0.1.2/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= @@ -926,6 +949,9 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/gopher-lua v0.0.0-20171031051903-609c9cd26973/go.mod h1:aEV29XrmTYFr3CiRxZeGHpkvbwq+prZduBqMaascyCU= +github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8= +github.com/zclconf/go-cty v1.8.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk= +github.com/zclconf/go-cty v1.10.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk= github.com/zclconf/go-cty v1.13.2 h1:4GvrUxe/QUDYuJKAav4EYqdM47/kZa672LwmXFmEKT0= github.com/zclconf/go-cty v1.13.2/go.mod h1:YKQzy/7pZ7iq2jNFzy5go57xdxdWoLLpaEp4u238AE0= github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8= @@ -968,6 +994,7 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= +golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -989,6 +1016,7 @@ golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20220307211146-efcb8507fb70/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220511200225-c6db032c6c88/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220517005047-85d78b3ac167/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= @@ -1032,10 +1060,12 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1150,6 +1180,7 @@ golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1257,6 +1288,7 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1328,6 +1360,7 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.11.0 h1:EMCa6U9S2LtZXLAMoWiR/R8dAQFRqbAitmbJ2UKhoi8= golang.org/x/tools v0.11.0/go.mod h1:anzJrxPjNtfgiYQYirP2CPGzGLxrH2u2QBhn6Bf3qY8= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/main.go b/main.go index 88b0896..8c58ba2 100644 --- a/main.go +++ b/main.go @@ -27,7 +27,7 @@ var defaultBenchmarkNames = []string{ "pg", "pgx", "pgx_pool", "pop", "raw", "reform", "rel", "sqlboiler", "sqlc", "sqlx", "upper", "xorm", - "zorm", "gen", + "zorm", "gen", "queryx", } type ListOpts []string @@ -124,6 +124,7 @@ func runBenchmarks(orms ListOpts) { "xorm": bench.CreateXorm(), "zorm": bench.CreateZorm(), "gen": bench.CreateGen(), + "queryx": bench.CreateQueryx(), } table := new(tabwriter.Writer)