aboutsummaryrefslogtreecommitdiff
path: root/benchmark/internal
diff options
context:
space:
mode:
authorStefan Majewsky <majewsky@gmx.net>2026-05-12 23:32:28 +0200
committerStefan Majewsky <majewsky@gmx.net>2026-05-12 23:32:28 +0200
commita86a346ecceb7ad409f116474c1593b201012cf2 (patch)
tree267505a9e6bba398f7a379a046df64a8aec45b1c /benchmark/internal
parent23fa77bbe1286b55e2526c0a965da1a4c3048415 (diff)
downloadgo-oblast-a86a346ecceb7ad409f116474c1593b201012cf2.tar.gz
add PostgreSQL benchmark, comparing lib/pq against pgx both with and w/o Oblast
Diffstat (limited to 'benchmark/internal')
-rw-r--r--benchmark/internal/oblast_pgx/handle.go81
-rw-r--r--benchmark/internal/oblast_pgx/results.go66
-rw-r--r--benchmark/internal/oblast_pgx/statement.go60
3 files changed, 207 insertions, 0 deletions
diff --git a/benchmark/internal/oblast_pgx/handle.go b/benchmark/internal/oblast_pgx/handle.go
new file mode 100644
index 0000000..6a88e2b
--- /dev/null
+++ b/benchmark/internal/oblast_pgx/handle.go
@@ -0,0 +1,81 @@
+// SPDX-FileCopyrightText: 2026 Stefan Majewsky <majewsky@gmx.net>
+// SPDX-License-Identifier: Apache-2.0
+
+package oblast_pgx
+
+import (
+ "context"
+ "fmt"
+ "strconv"
+ "sync/atomic"
+
+ "github.com/jackc/pgx/v5"
+ "github.com/jackc/pgx/v5/pgconn"
+ "go.xyrillian.de/oblast/handle"
+)
+
+type Handle interface {
+ Exec(ctx context.Context, sql string, args ...any) (pgconn.CommandTag, error)
+ Query(ctx context.Context, sql string, args ...any) (pgx.Rows, error)
+ QueryRow(ctx context.Context, sql string, args ...any) pgx.Row
+}
+
+var (
+ _ Handle = &pgx.Conn{}
+ _ Handle = pgx.Tx(nil)
+)
+
+// TODO: offer wrapping for pgxpool.Pool and pgxpool.Conn?
+func Wrap(h Handle) handle.Handle {
+ switch h := h.(type) {
+ case *pgx.Conn:
+ return wrappedHandle{h}
+ case pgx.Tx:
+ return wrappedHandle{h}
+ default:
+ panic(fmt.Sprintf("unexpected type: %#v", h))
+ }
+}
+
+var preparedStatementId atomic.Uint64
+
+type wrappedHandle struct {
+ inner Handle
+}
+
+// Prepare implements the [handle.Handle] interface.
+func (h wrappedHandle) Prepare(ctx context.Context, query string, repeated bool) (handle.Statement, error) {
+ if !repeated {
+ return wrappedUnpreparedStatement{query, h.inner}, nil
+ }
+
+ name := "oblast_pgx_" + strconv.FormatUint(preparedStatementId.Add(1), 10)
+ switch inner := h.inner.(type) {
+ case *pgx.Conn:
+ stmt, err := inner.Prepare(ctx, name, query)
+ return wrappedPreparedStatement{ctx, stmt, h.inner}, err
+ case pgx.Tx:
+ stmt, err := inner.Conn().Prepare(ctx, name, query)
+ return wrappedPreparedStatement{ctx, stmt, h.inner}, err
+ default:
+ panic("unreachable") // because of the check in func Wrap()
+ }
+}
+
+// Releases a prepared statement.
+func deallocate(ctx context.Context, h Handle, stmt *pgconn.StatementDescription) error {
+ switch h := h.(type) {
+ case *pgx.Conn:
+ return h.Deallocate(ctx, stmt.Name)
+ case pgx.Tx:
+ return h.Conn().Deallocate(ctx, stmt.Name)
+ default:
+ panic("unreachable") // because of the check in func Wrap()
+ }
+}
+
+// Query implements the [handle.Handle] interface.
+func (h wrappedHandle) Query(ctx context.Context, query string, args []any) (handle.Rows, error) {
+ rows, err := h.inner.Query(ctx, query, args...)
+ return wrappedRows{rows}, err
+}
diff --git a/benchmark/internal/oblast_pgx/results.go b/benchmark/internal/oblast_pgx/results.go
new file mode 100644
index 0000000..3ccb5ce
--- /dev/null
+++ b/benchmark/internal/oblast_pgx/results.go
@@ -0,0 +1,66 @@
+// SPDX-FileCopyrightText: 2026 Stefan Majewsky <majewsky@gmx.net>
+// SPDX-License-Identifier: Apache-2.0
+
+package oblast_pgx
+
+import (
+ "database/sql"
+ "errors"
+
+ "github.com/jackc/pgx/v5"
+ "github.com/jackc/pgx/v5/pgconn"
+ "go.xyrillian.de/oblast/handle"
+)
+
+type wrappedRows struct {
+ inner pgx.Rows
+}
+
+var _ handle.Rows = wrappedRows{}
+
+// Columns implements the [handle.Rows] interface.
+func (r wrappedRows) Columns() ([]string, error) {
+ descriptions := r.inner.FieldDescriptions()
+ result := make([]string, len(descriptions))
+ for idx, desc := range descriptions {
+ result[idx] = desc.Name
+ }
+ return result, nil
+}
+
+// Close implements the [handle.Rows] interface.
+func (r wrappedRows) Close() error {
+ r.inner.Close()
+ return nil
+}
+
+// Err implements the [handle.Rows] interface.
+func (r wrappedRows) Err() error {
+ return r.inner.Err()
+}
+
+// Next implements the [handle.Rows] interface.
+func (r wrappedRows) Next() bool {
+ return r.inner.Next()
+}
+
+// Scan implements the [handle.Rows] interface.
+func (r wrappedRows) Scan(args ...any) error {
+ return r.inner.Scan(args...)
+}
+
+type wrappedResult struct {
+ inner pgconn.CommandTag
+}
+
+var _ sql.Result = wrappedResult{}
+
+// LastInsertId implements the [sql.Result] interface.
+func (r wrappedResult) LastInsertId() (int64, error) {
+ return 0, errors.New("PostgreSQL does not support LastInsertId()")
+}
+
+// LastInsertId implements the [sql.Result] interface.
+func (r wrappedResult) RowsAffected() (int64, error) {
+ return r.inner.RowsAffected(), nil
+}
diff --git a/benchmark/internal/oblast_pgx/statement.go b/benchmark/internal/oblast_pgx/statement.go
new file mode 100644
index 0000000..d81c579
--- /dev/null
+++ b/benchmark/internal/oblast_pgx/statement.go
@@ -0,0 +1,60 @@
+// SPDX-FileCopyrightText: 2026 Stefan Majewsky <majewsky@gmx.net>
+// SPDX-License-Identifier: Apache-2.0
+
+package oblast_pgx
+
+import (
+ "context"
+ "database/sql"
+
+ "github.com/jackc/pgx/v5/pgconn"
+ "go.xyrillian.de/oblast/handle"
+)
+
+type wrappedPreparedStatement struct {
+ ctx context.Context
+ statement *pgconn.StatementDescription
+ handle Handle
+}
+
+type wrappedUnpreparedStatement struct {
+ query string
+ handle Handle
+}
+
+var (
+ _ handle.Statement = wrappedPreparedStatement{}
+ _ handle.Statement = wrappedUnpreparedStatement{}
+)
+
+// Close implements the [handle.Statement] interface.
+func (s wrappedPreparedStatement) Close() error {
+ return deallocate(s.ctx, s.handle, s.statement)
+}
+
+// Close implements the [handle.Statement] interface.
+func (s wrappedUnpreparedStatement) Close() error {
+ return nil
+}
+
+// Exec implements the [handle.Statement] interface.
+func (s wrappedPreparedStatement) Exec(ctx context.Context, args []any) (sql.Result, error) {
+ result, err := s.handle.Exec(ctx, s.statement.Name, args...)
+ return wrappedResult{result}, err
+}
+
+// Exec implements the [handle.Statement] interface.
+func (s wrappedUnpreparedStatement) Exec(ctx context.Context, args []any) (sql.Result, error) {
+ result, err := s.handle.Exec(ctx, s.query, args...)
+ return wrappedResult{result}, err
+}
+
+// QueryRow implements the [handle.Statement] interface.
+func (s wrappedPreparedStatement) QueryRow(ctx context.Context, args, slots []any) error {
+ return s.handle.QueryRow(ctx, s.statement.Name, args...).Scan(slots...)
+}
+
+// QueryRow implements the [handle.Statement] interface.
+func (s wrappedUnpreparedStatement) QueryRow(ctx context.Context, args, slots []any) error {
+ return s.handle.QueryRow(ctx, s.query, args...).Scan(slots...)
+}