aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md2
-rw-r--r--REUSE.toml1
-rw-r--r--connector.go9
-rw-r--r--go.mod13
-rw-r--r--go.sum28
-rw-r--r--handle.go89
-rw-r--r--results.go66
-rw-r--r--statement.go60
8 files changed, 266 insertions, 2 deletions
diff --git a/README.md b/README.md
index c24135a..3bccaad 100644
--- a/README.md
+++ b/README.md
@@ -5,4 +5,4 @@ SPDX-License-Identifier: Apache-2.0
# gg-pgx
-Integration for connecting [gg/gsql](https://pkg.go.dev/go.xyrillian.de/gg/gsql) (and libraries based on it, such as [Oblast](https://pkg.go.dev/go.xyrillian.de/oblast)) to the PostgreSQL driver library [pgx](https://github.com/jackc/pgx).
+Integration for using [gg/gsql](https://pkg.go.dev/go.xyrillian.de/gg/gsql) (and libraries based on it, such as [Oblast](https://pkg.go.dev/go.xyrillian.de/oblast)) with the PostgreSQL driver library [pgx](https://github.com/jackc/pgx).
diff --git a/REUSE.toml b/REUSE.toml
index 7f498ec..2d17d24 100644
--- a/REUSE.toml
+++ b/REUSE.toml
@@ -9,6 +9,7 @@ path = [
".gitignore",
"description",
"go.mod",
+ "go.sum",
]
SPDX-FileCopyrightText = "Stefan Majewsky <majewsky@gmx.net>"
SPDX-License-Identifier = "Apache-2.0"
diff --git a/connector.go b/connector.go
index cb497da..d7f1242 100644
--- a/connector.go
+++ b/connector.go
@@ -1,6 +1,13 @@
// 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
-// TODO: implement
+// TODO: implement Handle (handle.go is copied from an early prototype in the Oblast benchmark suite and uses the extremely old Wrap() style)
+// TODO: implement ConnectionHandle
+// TODO: test coverage (via gg/pgruntime)
diff --git a/go.mod b/go.mod
index 8e24db2..ffd7e8a 100644
--- a/go.mod
+++ b/go.mod
@@ -1,3 +1,16 @@
module go.xyrillian.de/gg-pgx
go 1.26
+
+require (
+ github.com/jackc/pgx/v5 v5.10.0
+ go.xyrillian.de/gg v1.11.2-0.20260730175754-81e32cec3629
+)
+
+require (
+ github.com/jackc/pgpassfile v1.0.0 // indirect
+ github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
+ github.com/jackc/puddle/v2 v2.2.2 // indirect
+ golang.org/x/sync v0.17.0 // indirect
+ golang.org/x/text v0.29.0 // indirect
+)
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..87edcf1
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,28 @@
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
+github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
+github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
+github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
+github.com/jackc/pgx/v5 v5.10.0 h1:VhSvgU2jSli8o3AqIEOTJr7rZwAEUVo4E4XhR94Zfr0=
+github.com/jackc/pgx/v5 v5.10.0/go.mod h1:mal1tBGAFfLHvZzaYh77YS/eC6IX9OWbRV1QIIM0Jn4=
+github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
+github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
+github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
+go.xyrillian.de/gg v1.11.2-0.20260730175754-81e32cec3629 h1:3IOrqdh3N/X4FLts7prxJ4Sy1IGZsOHl7NOym4hTMdk=
+go.xyrillian.de/gg v1.11.2-0.20260730175754-81e32cec3629/go.mod h1:DoO4fQSWIrBRlNlCjVyrYM0kAEBt/Jg2GkMH+cGRZ0k=
+golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
+golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
+golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
+golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/handle.go b/handle.go
new file mode 100644
index 0000000..7f6514d
--- /dev/null
+++ b/handle.go
@@ -0,0 +1,89 @@
+// SPDX-FileCopyrightText: 2026 Stefan Majewsky <majewsky@gmx.net>
+// SPDX-License-Identifier: Apache-2.0
+
+package gg_pgx
+
+import (
+ "context"
+ "fmt"
+ "strconv"
+ "sync/atomic"
+
+ "github.com/jackc/pgx/v5"
+ "github.com/jackc/pgx/v5/pgconn"
+ "github.com/jackc/pgx/v5/pgxpool"
+ "go.xyrillian.de/gg/gsql"
+)
+
+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 = &pgxpool.Conn{}
+ _ Handle = pgx.Tx(&pgxpool.Tx{})
+)
+
+func Wrap(h Handle) gsql.Handle {
+ switch h := h.(type) {
+ case *pgx.Conn:
+ return wrappedHandle{h}
+ case *pgxpool.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
+}
+
+// GSQLPrepare implements the [gsql.Handle] interface.
+func (h wrappedHandle) GSQLPrepare(ctx context.Context, query string, repeated bool) (gsql.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 *pgxpool.Conn:
+ // pgxpool.Conn does not have Prepare()
+ return wrappedUnpreparedStatement{query, h.inner}, nil
+ 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 *pgxpool.Conn:
+ panic("unreachable") // because func GSQLPrepare() does not return a wrappedPreparedStatement for this underlying type
+ case pgx.Tx:
+ return h.Conn().Deallocate(ctx, stmt.Name)
+ default:
+ panic("unreachable") // because of the check in func Wrap()
+ }
+}
+
+// GSQLQuery implements the [gsql.Handle] interface.
+func (h wrappedHandle) GSQLQuery(ctx context.Context, query string, args []any) (gsql.Rows, error) {
+ rows, err := h.inner.Query(ctx, query, args...)
+ return wrappedRows{rows}, err
+}
diff --git a/results.go b/results.go
new file mode 100644
index 0000000..510bb08
--- /dev/null
+++ b/results.go
@@ -0,0 +1,66 @@
+// SPDX-FileCopyrightText: 2026 Stefan Majewsky <majewsky@gmx.net>
+// SPDX-License-Identifier: Apache-2.0
+
+package gg_pgx
+
+import (
+ "database/sql"
+ "errors"
+
+ "github.com/jackc/pgx/v5"
+ "github.com/jackc/pgx/v5/pgconn"
+ "go.xyrillian.de/gg/gsql"
+)
+
+type wrappedRows struct {
+ inner pgx.Rows
+}
+
+var _ gsql.Rows = wrappedRows{}
+
+// Columns implements the [gsql.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 [gsql.Rows] interface.
+func (r wrappedRows) Close() error {
+ r.inner.Close()
+ return nil
+}
+
+// Err implements the [gsql.Rows] interface.
+func (r wrappedRows) Err() error {
+ return r.inner.Err()
+}
+
+// Next implements the [gsql.Rows] interface.
+func (r wrappedRows) Next() bool {
+ return r.inner.Next()
+}
+
+// Scan implements the [gsql.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/statement.go b/statement.go
new file mode 100644
index 0000000..bb95252
--- /dev/null
+++ b/statement.go
@@ -0,0 +1,60 @@
+// SPDX-FileCopyrightText: 2026 Stefan Majewsky <majewsky@gmx.net>
+// SPDX-License-Identifier: Apache-2.0
+
+package gg_pgx
+
+import (
+ "context"
+ "database/sql"
+
+ "github.com/jackc/pgx/v5/pgconn"
+ "go.xyrillian.de/gg/gsql"
+)
+
+type wrappedPreparedStatement struct {
+ ctx context.Context
+ statement *pgconn.StatementDescription
+ handle Handle
+}
+
+type wrappedUnpreparedStatement struct {
+ query string
+ handle Handle
+}
+
+var (
+ _ gsql.Statement = wrappedPreparedStatement{}
+ _ gsql.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...)
+}