aboutsummaryrefslogtreecommitdiff
path: root/interface.go
diff options
context:
space:
mode:
Diffstat (limited to 'interface.go')
-rw-r--r--interface.go168
1 files changed, 168 insertions, 0 deletions
diff --git a/interface.go b/interface.go
new file mode 100644
index 0000000..d4a5b56
--- /dev/null
+++ b/interface.go
@@ -0,0 +1,168 @@
+// SPDX-FileCopyrightText: 2026 Stefan Majewsky <majewsky@gmx.net>
+// SPDX-License-Identifier: Apache-2.0
+
+// Package gg_pgx contains integration for using [gg/gsql] (and libraries based on it, such as [Oblast]) with the PostgreSQL driver library [pgx].
+//
+// [gg/gsql]: https://pkg.go.dev/go.xyrillian.de/gg/gsql
+// [Oblast]: https://pkg.go.dev/go.xyrillian.de/oblast
+// [pgx]: https://github.com/jackc/pgx
+package gg_pgx
+
+import (
+ "context"
+
+ "github.com/jackc/pgx/v5"
+ "github.com/jackc/pgx/v5/pgxpool"
+ "go.xyrillian.de/gg/gsql"
+)
+
+// TODO: test coverage (via gg/pgruntime)
+
+// NOTE: The internal structure of these types follows the pattern established
+// by (and explained in) `std.go` of `go.xyrillian.de/gg/gsql`.
+
+// Conn wraps [*pgx.Conn] into a [gsql.Handle].
+//
+// Because this type has [*pgx.Conn] as an embedded field,
+// all methods from that type work on this type as well.
+type Conn struct {
+ *pgx.Conn
+ gsql.ConnectionHandle
+}
+
+// NewConn wraps an instance of [*pgx.Conn] into the [Conn] type that implements [gsql.Handle].
+func NewConn(conn *pgx.Conn) *Conn {
+ return &Conn{conn, connHandle{conn}}
+}
+
+// Begin is like [pgx.Conn.Begin], but wraps the resulting transaction into a [Handle].
+func (conn *Conn) Begin(ctx context.Context) (*Tx, error) {
+ tx, err := conn.Conn.Begin(ctx)
+ return maybeNewTx(tx), err
+}
+
+// BeginTx is like [pgx.Conn.BeginTx], but wraps the resulting transaction into a [Handle].
+func (conn *Conn) BeginTx(ctx context.Context, opts pgx.TxOptions) (*Tx, error) {
+ tx, err := conn.Conn.BeginTx(ctx, opts)
+ return maybeNewTx(tx), err
+}
+
+// Pool wraps [*pgxpool.Pool] into a [gsql.Handle].
+//
+// Because this type has [*pgxpool.Pool] as an embedded field,
+// all methods from that type work on this type as well.
+type Pool struct {
+ *pgxpool.Pool
+ gsql.ConnectionHandle
+}
+
+// NewPool wraps an instance of [*pgxpool.Pool] into the [Pool] type that implements [gsql.Handle].
+func NewPool(pool *pgxpool.Pool) *Pool {
+ return &Pool{pool, poolHandle{pool}}
+}
+
+// Acquire is like [pgxpool.Pool.Acquire], but wraps the resulting connection into a [Handle].
+func (pool *Pool) Acquire(ctx context.Context) (*PoolConn, error) {
+ conn, err := pool.Pool.Acquire(ctx)
+ return maybe(NewPoolConn, conn), err
+}
+
+// AcquireAllIdle is like [pgxpool.Pool.AcquireAllIdle], but wraps the resulting connections into [Handle] instances.
+func (pool *Pool) AcquireAllIdle(ctx context.Context) []*PoolConn {
+ conns := pool.Pool.AcquireAllIdle(ctx)
+ result := make([]*PoolConn, len(conns))
+ for idx, conn := range conns {
+ result[idx] = maybe(NewPoolConn, conn)
+ }
+ return result
+}
+
+// AcquireFunc is like [pgxpool.Pool.AcquireFunc], but wraps the resulting connection into a [Handle].
+func (pool *Pool) AcquireFunc(ctx context.Context, f func(*PoolConn) error) error {
+ return pool.Pool.AcquireFunc(ctx, func(conn *pgxpool.Conn) error {
+ return f(maybe(NewPoolConn, conn))
+ })
+}
+
+// Begin is like [pgxpool.Pool.Begin], but wraps the resulting transaction into a [Handle].
+func (pool *Pool) Begin(ctx context.Context) (*Tx, error) {
+ tx, err := pool.Pool.Begin(ctx)
+ return maybeNewTx(tx), err
+}
+
+// BeginTx is like [pgxpool.Pool.BeginTx], but wraps the resulting transaction into a [Handle].
+func (pool *Pool) BeginTx(ctx context.Context, opts pgx.TxOptions) (*Tx, error) {
+ tx, err := pool.Pool.BeginTx(ctx, opts)
+ return maybeNewTx(tx), err
+}
+
+// PoolConn wraps [*pgxpool.Conn] into a [gsql.Handle].
+//
+// Because this type has [*pgxpool.Conn] as an embedded field,
+// all methods from that type work on this type as well.
+type PoolConn struct {
+ *pgxpool.Conn
+ gsql.ConnectionHandle
+}
+
+// NewPoolConn wraps an instance of [*pgxpool.Conn] into the [PoolConn] type that implements [gsql.Handle].
+func NewPoolConn(pool *pgxpool.Conn) *PoolConn {
+ return &PoolConn{pool, poolConnHandle{pool}}
+}
+
+// Begin is like [pgxpool.Conn.Begin], but wraps the resulting transaction into a [Handle].
+func (conn *PoolConn) Begin(ctx context.Context) (*Tx, error) {
+ tx, err := conn.Conn.Begin(ctx)
+ return maybeNewTx(tx), err
+}
+
+// BeginTx is like [pgxpool.Conn.BeginTx], but wraps the resulting transaction into a [Handle].
+func (conn *PoolConn) BeginTx(ctx context.Context, opts pgx.TxOptions) (*Tx, error) {
+ tx, err := conn.Conn.BeginTx(ctx, opts)
+ return maybeNewTx(tx), err
+}
+
+// GetConn is like [pgxpool.Conn.Conn], but wraps the resulting connection into a [Handle].
+//
+// This method should be called "Conn", but one of the embedded fields of this type blocks that name.
+func (conn *PoolConn) GetConn() *Conn {
+ return maybe(NewConn, conn.Conn.Conn())
+}
+
+// Hijack is like [pgxpool.Conn.Conn], but wraps the resulting connection into a [Handle].
+func (conn *PoolConn) Hijack() *Conn {
+ return maybe(NewConn, conn.Conn.Hijack())
+}
+
+// Tx wraps [pgx.Tx] into a [gsql.Handle].
+//
+// Because this type has [pgx.Tx] as an embedded field,
+// all methods from that type work on this type as well.
+type Tx struct {
+ pgx.Tx
+ gsql.Handle
+}
+
+// NewTx wraps an instance of [pgx.Tx] into the [Tx] type that implements [Handle].
+func NewTx(tx pgx.Tx) *Tx {
+ return &Tx{tx, txHandle{tx}}
+}
+
+// Conn is like the Conn() method of [pgx.Tx], but wraps the resulting connection into a [Handle].
+func (t *Tx) Conn() *Conn {
+ return maybe(NewConn, t.Tx.Conn())
+}
+
+func maybeNewTx(tx pgx.Tx) *Tx {
+ if tx == nil {
+ return nil
+ }
+ return NewTx(tx)
+}
+
+func maybe[T, U any](wrap func(*T) *U, value *T) *U {
+ if value == nil {
+ return nil
+ }
+ return wrap(value)
+}