diff --git a/Makefile b/Makefile index 230c600..a508b63 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,7 @@ test: setup go-mod-tidy @( cd adapters/ksqlite3 ; $(GOBIN)/richgo test $(path) $(args) ) bench: go-mod-tidy - cd benchmarks && go test -bench=. -benchtime=$(TIME) + cd benchmarks && make bench TIME=$(TIME) @echo "Benchmark executed at: $$(date --iso)" @echo "Benchmark executed on commit: $$(git rev-parse HEAD)" diff --git a/benchmarks/Makefile b/benchmarks/Makefile index 6727783..db8a484 100644 --- a/benchmarks/Makefile +++ b/benchmarks/Makefile @@ -1,6 +1,16 @@ GOBIN=$(shell go env GOPATH)/bin -lint: +TIME=1s +bench: sqlc + go test -bench=. -benchtime=$(TIME) + +lint: sqlc @$(GOBIN)/staticcheck ./... @go vet ./... + +$(GOBIN)/sqlc: + go install github.com/kyleconroy/sqlc/cmd/sqlc@latest + +sqlc: $(GOBIN)/sqlc sqlcgen/schema.sql sqlcgen/queries.sql + sqlc generate diff --git a/benchmarks/benchmarks_test.go b/benchmarks/benchmarks_test.go index 28664a0..f8be416 100644 --- a/benchmarks/benchmarks_test.go +++ b/benchmarks/benchmarks_test.go @@ -12,6 +12,7 @@ import ( _ "github.com/lib/pq" "github.com/vingarcia/ksql" "github.com/vingarcia/ksql/adapters/kpgx" + "github.com/vingarcia/ksql/benchmarks/sqlcgen" "gorm.io/driver/postgres" "gorm.io/gorm" ) @@ -272,6 +273,34 @@ func BenchmarkInsert(b *testing.B) { } }) }) + + b.Run("sqlc", func(b *testing.B) { + sqlDB, err := sql.Open(driver, connStr) + if err != nil { + b.Fatalf("error creating sql client: %s", err) + } + sqlDB.SetMaxOpenConns(1) + + sqlcDB := sqlcgen.New(sqlDB) + + err = recreateTable(connStr) + if err != nil { + b.Fatalf("error creating table: %s", err.Error()) + } + + b.Run("insert-one", func(b *testing.B) { + for i := 0; i < b.N; i++ { + user := sqlcgen.InsertUserParams{ + Name: strconv.Itoa(i), + Age: int32(i), + } + _, err := sqlcDB.InsertUser(ctx, user) + if err != nil { + b.Fatalf("insert error: %s", err.Error()) + } + } + }) + }) } func BenchmarkQuery(b *testing.B) { @@ -693,6 +722,44 @@ func BenchmarkQuery(b *testing.B) { } }) }) + + b.Run("sqlc", func(b *testing.B) { + sqlDB, err := sql.Open(driver, connStr) + if err != nil { + b.Fatalf("error creating sql client: %s", err) + } + sqlDB.SetMaxOpenConns(1) + + sqlcDB := sqlcgen.New(sqlDB) + + err = recreateTable(connStr) + if err != nil { + b.Fatalf("error creating table: %s", err.Error()) + } + + err = insertUsers(connStr, 100) + if err != nil { + b.Fatalf("error inserting users: %s", err.Error()) + } + + b.Run("single-row", func(b *testing.B) { + for i := 0; i < b.N; i++ { + _, err := sqlcDB.GetUser(ctx, int32(i%100)) + if err != nil { + b.Fatalf("query error: %s", err.Error()) + } + } + }) + + b.Run("multiple-rows", func(b *testing.B) { + for i := 0; i < b.N; i++ { + _, err := sqlcDB.List10Users(ctx, int32(i%90)) + if err != nil { + b.Fatalf("query error: %s", err.Error()) + } + } + }) + }) } func recreateTable(connStr string) error { diff --git a/benchmarks/sqlc.yaml b/benchmarks/sqlc.yaml new file mode 100644 index 0000000..019a94c --- /dev/null +++ b/benchmarks/sqlc.yaml @@ -0,0 +1,7 @@ +version: 1 +packages: + - path: "sqlcgen" + name: "sqlcgen" + engine: "postgresql" + schema: "sqlcgen/schema.sql" + queries: "sqlcgen/queries.sql" diff --git a/benchmarks/sqlcgen/db.go b/benchmarks/sqlcgen/db.go new file mode 100644 index 0000000..49437cf --- /dev/null +++ b/benchmarks/sqlcgen/db.go @@ -0,0 +1,31 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 + +package sqlcgen + +import ( + "context" + "database/sql" +) + +type DBTX interface { + ExecContext(context.Context, string, ...interface{}) (sql.Result, error) + PrepareContext(context.Context, string) (*sql.Stmt, error) + QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error) + QueryRowContext(context.Context, string, ...interface{}) *sql.Row +} + +func New(db DBTX) *Queries { + return &Queries{db: db} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx *sql.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/benchmarks/sqlcgen/models.go b/benchmarks/sqlcgen/models.go new file mode 100644 index 0000000..e032327 --- /dev/null +++ b/benchmarks/sqlcgen/models.go @@ -0,0 +1,13 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 + +package sqlcgen + +import () + +type User struct { + ID int64 + Name string + Age int32 +} diff --git a/benchmarks/sqlcgen/queries.sql b/benchmarks/sqlcgen/queries.sql new file mode 100644 index 0000000..225b092 --- /dev/null +++ b/benchmarks/sqlcgen/queries.sql @@ -0,0 +1,11 @@ +-- name: InsertUser :one +INSERT INTO users(name, age) +VALUES ($1, $2) RETURNING id; + +-- name: GetUser :one +SELECT id, name, age FROM users +OFFSET $1 LIMIT 1; + +-- name: List10Users :many +SELECT id, name, age FROM users +OFFSET $1 LIMIT 10; diff --git a/benchmarks/sqlcgen/queries.sql.go b/benchmarks/sqlcgen/queries.sql.go new file mode 100644 index 0000000..ba72815 --- /dev/null +++ b/benchmarks/sqlcgen/queries.sql.go @@ -0,0 +1,67 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.14.0 +// source: queries.sql + +package sqlcgen + +import ( + "context" +) + +const getUser = `-- name: GetUser :one +SELECT id, name, age FROM users +OFFSET $1 LIMIT 1 +` + +func (q *Queries) GetUser(ctx context.Context, offset int32) (User, error) { + row := q.db.QueryRowContext(ctx, getUser, offset) + var i User + err := row.Scan(&i.ID, &i.Name, &i.Age) + return i, err +} + +const insertUser = `-- name: InsertUser :one +INSERT INTO users(name, age) +VALUES ($1, $2) RETURNING id +` + +type InsertUserParams struct { + Name string + Age int32 +} + +func (q *Queries) InsertUser(ctx context.Context, arg InsertUserParams) (int64, error) { + row := q.db.QueryRowContext(ctx, insertUser, arg.Name, arg.Age) + var id int64 + err := row.Scan(&id) + return id, err +} + +const list10Users = `-- name: List10Users :many +SELECT id, name, age FROM users +OFFSET $1 LIMIT 10 +` + +func (q *Queries) List10Users(ctx context.Context, offset int32) ([]User, error) { + rows, err := q.db.QueryContext(ctx, list10Users, offset) + if err != nil { + return nil, err + } + defer rows.Close() + var items []User + for rows.Next() { + var i User + if err := rows.Scan(&i.ID, &i.Name, &i.Age); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} diff --git a/benchmarks/sqlcgen/schema.sql b/benchmarks/sqlcgen/schema.sql new file mode 100644 index 0000000..1ea9046 --- /dev/null +++ b/benchmarks/sqlcgen/schema.sql @@ -0,0 +1,6 @@ + +CREATE TABLE users ( + id BIGSERIAL PRIMARY KEY, + name text NOT NULL, + age integer NOT NULL +);