pgxstdlib

package
v1.1.10 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 7, 2026 License: Apache-2.0 Imports: 8 Imported by: 0

README

pgxstdlib

A PostgreSQL database/sql driver that works under TinyGo as well as standard Go, so application code is shared between compilers.

import "github.com/shibukawa/tinygodriver/database/sql/pgxstdlib"

db, err := pgxstdlib.Open("postgres://user:pass@localhost:5432/db?sslmode=verify-full")

Both builds are pgx's stdlib driver:

Build Backend
Standard Go upstream pgx, unmodified
TinyGo or -tags force_tinygo_logic vendored pgx with TLS rerouted

TinyGo ships crypto/tls as a stub that cannot be linked, so the vendored copy routes TLS through the https package instead. That is three patched files out of 145; see internal/PATCHES.md.

TLS

sslmode works on both builds, including verify-full with a custom sslrootcert. On TinyGo the handshake runs on the already-connected socket after PostgreSQL's SSLRequest, using the platform's native TLS stack.

Platform Upgrade backend Max version
Standard Go crypto/tls TLS 1.3
macOS Secure Transport TLS 1.2
macOS with -tags darwinstarttlswith13 mbedTLS TLS 1.3
Linux mbedTLS TLS 1.3

Two differences from libpq are deliberate:

  • verify-ca is treated as verify-full. libpq skips the host name check there; the native backends cannot express that, and checking the name too is stricter, never weaker.
  • sslcert and sslkey are rejected rather than ignored, because the native backends cannot offer a client certificate.

A platform with no TLS backend refuses any mode but disable, and never falls back to plaintext silently.

TinyGo notes

Build with -scheduler=threads. Under the cooperative scheduler a blocking socket call holds the whole runtime, so background goroutines never run and query cancellation stops working without any error.

Blank-import netdev, as with any TinyGo program that uses the network:

import _ "github.com/shibukawa/tinygodriver/netdev"

Unix domain sockets and IPv6 are unavailable there, so connect over TCP to an IPv4 host.

Cancellation

context cancellation is wired to PostgreSQL's CancelRequest, which opens a second connection to ask the server to stop. pgx's default instead moves the deadline on the in-flight connection, which does nothing under TinyGo: netdev reads the deadline once when a read begins, so a later change cannot interrupt it and the query runs to completion with no error. This package installs the CancelRequest handler on both builds so behavior does not differ by compiler.

Reaching pgx directly

Batch, CopyFrom and LISTEN/NOTIFY have no database/sql equivalent. WithConn leases a pooled connection and hands over the pgx connection behind it:

err := pgxstdlib.WithConn(ctx, db, func(c *pgxstdlib.Conn) error {
	b := &pgxstdlib.Batch{}
	b.Queue("INSERT INTO t(a) VALUES ($1)", 1)
	b.Queue("INSERT INTO t(a) VALUES ($1)", 2)
	return c.SendBatch(ctx, b).Close()
})

Conn, Batch, Rows, PgError and the rest are aliases for the pgx types, so the values are pgx's own. Going through this package is mandatory rather than tidy: the TinyGo build uses the vendored pgx under internal/, which no package outside pgxstdlib may import, so hand-writing the sql.Conn.Raw assertion compiles on standard Go and fails on TinyGo with use of internal package not allowed. examples/pgxdemo is built on both paths and is what keeps that from regressing.

c is only valid inside the callback, as with sql.Conn.Raw. Read and close BatchResults and Rows before returning.

Updating the vendored pgx

go mod download github.com/jackc/pgx/v5@vX.Y.Z   # after bumping PGX_VERSION
python3 internal/vendor.py && python3 internal/patch.py

patch.py anchors every edit on an exact upstream string and fails loudly if one is gone, so a version bump reports what needs attention instead of quietly producing a tree that still links crypto/tls.

Tests

The integration tests skip unless a server is configured:

docker run -d --name pgtest -e POSTGRES_PASSWORD=pass -e POSTGRES_USER=user \
    -e POSTGRES_DB=db -p 55432:5432 postgres:17

PGXSTDLIB_TEST_DSN='postgres://user:pass@localhost:55432/db?sslmode=disable' \
    go test ./database/sql/pgxstdlib/

Add -tags force_tinygo_logic to run the same suite against the vendored backend without a TinyGo toolchain. The TLS tests need a server with ssl=on; tls_test.go documents the setup.

Documentation

Overview

Package pgxstdlib provides a PostgreSQL database/sql driver that works under both TinyGo and standard Go.

Both builds are pgx/stdlib layered over database/pgx, which supplies the parsed configuration and its defaults. Standard Go uses upstream pgx unmodified; TinyGo uses a vendored copy with TLS removed, because TinyGo ships crypto/tls as a stub that cannot be linked. See database/internal/PATCHES.md. Code that wants pgx itself rather than database/sql should use database/pgx directly.

db, err := pgxstdlib.Open("postgres://user:pass@localhost:5432/db?sslmode=disable")
if err != nil { ... }
defer db.Close()

var n int
err = db.QueryRowContext(ctx, "SELECT 1").Scan(&n)

Everything database/sql offers works on both compilers: parameters, prepared statements, transactions, column metadata, and context cancellation.

Reaching pgx directly

Batch, CopyFrom and LISTEN/NOTIFY have no database/sql equivalent. WithConn hands them the underlying pgx connection, on both compilers:

err := pgxstdlib.WithConn(ctx, db, func(c *pgxstdlib.Conn) error {
	b := &pgxstdlib.Batch{}
	b.Queue("INSERT INTO t(a) VALUES ($1)", 1)
	b.Queue("INSERT INTO t(a) VALUES ($1)", 2)
	return c.SendBatch(ctx, b).Close()
})

The pgx types are re-exported here as aliases, so Conn is pgx's own Conn and takes pgx's own methods. Naming them through this package is not a style preference on TinyGo, it is the only option: that build uses the vendored pgx under internal/, which no package outside pgxstdlib may import. Writing the sql.Conn.Raw dance by hand works on standard Go and does not compile under TinyGo, because the type assertion has to name a type that is out of reach.

Anything derived from c, including BatchResults and Rows, must be finished before the callback returns; see WithConn.

TinyGo notes

Build with -scheduler=threads. Under the cooperative scheduler a blocking socket call holds the whole runtime, so background goroutines never run and query cancellation silently stops working.

Import netdev for its side effect, as with any TinyGo program using the network:

import _ "github.com/shibukawa/tinygodriver/netdev"

Unix domain sockets and IPv6 are unavailable there, so connect over TCP to an IPv4 host. TLS support depends on the platform; see Open.

Index

Constants

This section is empty.

Variables

View Source
var (
	CopyFromRows  = pgx.CopyFromRows
	CopyFromSlice = pgx.CopyFromSlice
)

The CopyFrom source constructors, as variables because Go has no alias for a function.

View Source
var (
	RowToMap   = pgx.RowToMap
	ForEachRow = pgx.ForEachRow
)

Functions

func AppendRows added in v1.1.4

func AppendRows[T any, S ~[]T](slice S, rows Rows, fn RowToFunc[T]) (S, error)

func CollectExactlyOneRow added in v1.1.4

func CollectExactlyOneRow[T any](rows Rows, fn RowToFunc[T]) (T, error)

func CollectOneRow added in v1.1.4

func CollectOneRow[T any](rows Rows, fn RowToFunc[T]) (T, error)

func CollectRows added in v1.1.4

func CollectRows[T any](rows Rows, fn RowToFunc[T]) ([]T, error)

func Open

func Open(dsn string) (*sql.DB, error)

Open opens a database handle for a libpq-style URL or keyword DSN.

The handle is lazy in the usual database/sql way: no connection is made until the first use. Call db.PingContext to verify the settings eagerly.

sslmode is honored on both builds. On TinyGo it is served by the platform's native TLS stack, which starts TLS on the already-connected socket after PostgreSQL's SSLRequest, so verify-full and a custom sslrootcert both work. Two differences from libpq are deliberate:

  • verify-ca is treated as verify-full. libpq would skip the host name check; the native backends cannot express that, and checking the name as well is stricter, never weaker.
  • sslcert and sslkey are rejected rather than ignored, because the native backends cannot offer a client certificate.

A platform with no TLS backend refuses any mode but disable. It never falls back to plaintext silently.

func OpenContext

func OpenContext(ctx context.Context, dsn string) (*sql.DB, error)

OpenContext is Open plus an eager connectivity check, so configuration errors surface at open time instead of at first query.

func RowTo added in v1.1.4

func RowTo[T any](row CollectableRow) (T, error)

func RowToAddrOf added in v1.1.4

func RowToAddrOf[T any](row CollectableRow) (*T, error)

func RowToAddrOfStructByName added in v1.1.4

func RowToAddrOfStructByName[T any](row CollectableRow) (*T, error)

func RowToAddrOfStructByNameLax added in v1.1.4

func RowToAddrOfStructByNameLax[T any](row CollectableRow) (*T, error)

func RowToAddrOfStructByPos added in v1.1.4

func RowToAddrOfStructByPos[T any](row CollectableRow) (*T, error)

func RowToStructByName added in v1.1.4

func RowToStructByName[T any](row CollectableRow) (T, error)

func RowToStructByNameLax added in v1.1.4

func RowToStructByNameLax[T any](row CollectableRow) (T, error)

func RowToStructByPos added in v1.1.4

func RowToStructByPos[T any](row CollectableRow) (T, error)

func WithConn added in v1.1.4

func WithConn(ctx context.Context, db *sql.DB, fn func(*Conn) error) error

WithConn runs fn with the pgx connection behind one pooled database/sql connection, so Batch, CopyFrom and LISTEN/NOTIFY are reachable without leaving the database/sql surface.

err := pgxstdlib.WithConn(ctx, db, func(c *pgxstdlib.Conn) error {
	b := &pgxstdlib.Batch{}
	b.Queue("INSERT INTO t(a) VALUES ($1)", 1)
	b.Queue("INSERT INTO t(a) VALUES ($1)", 2)
	return c.SendBatch(ctx, b).Close()
})

The connection is leased for the duration of fn and returned to the pool afterwards. Use WithSQLConn instead when a *sql.Conn is already held, for example because the work needs session state.

c must not be used after fn returns, and neither may anything holding it, such as BatchResults or Rows. This is the contract of sql.Conn.Raw, which WithConn is built on: outside fn the connection is no longer locked against database/sql's own use, so a read there can interleave with another query on the same socket. Read the results and close them inside fn.

func WithSQLConn added in v1.1.4

func WithSQLConn(sc *sql.Conn, fn func(*Conn) error) error

WithSQLConn is WithConn against a connection the caller already holds. The same restriction applies: nothing derived from c may outlive fn.

Types

type Batch added in v1.1.4

type Batch = pgx.Batch

The pgx types reachable through WithConn, re-exported from database/pgx. That package resolves per build to upstream pgx or to the vendored copy, so this file needs no build tag, and a *pgxstdlib.Conn is the same type as a *pgx.Conn from database/pgx on either compiler.

On TinyGo the re-export is not a convenience, it is the only access there is: the vendored pgx sits under database/internal/, which a caller outside this repository cannot import, so without these a callback could never name the type it just received. See rawconn.go.

type BatchResults added in v1.1.4

type BatchResults = pgx.BatchResults

The pgx types reachable through WithConn, re-exported from database/pgx. That package resolves per build to upstream pgx or to the vendored copy, so this file needs no build tag, and a *pgxstdlib.Conn is the same type as a *pgx.Conn from database/pgx on either compiler.

On TinyGo the re-export is not a convenience, it is the only access there is: the vendored pgx sits under database/internal/, which a caller outside this repository cannot import, so without these a callback could never name the type it just received. See rawconn.go.

type CollectableRow added in v1.1.4

type CollectableRow = pgx.CollectableRow

The row-collection helpers. Generic functions can be neither aliased nor bound to a variable, so unlike the types above they are one-line forwards.

type CommandTag added in v1.1.4

type CommandTag = pgx.CommandTag

The pgx types reachable through WithConn, re-exported from database/pgx. That package resolves per build to upstream pgx or to the vendored copy, so this file needs no build tag, and a *pgxstdlib.Conn is the same type as a *pgx.Conn from database/pgx on either compiler.

On TinyGo the re-export is not a convenience, it is the only access there is: the vendored pgx sits under database/internal/, which a caller outside this repository cannot import, so without these a callback could never name the type it just received. See rawconn.go.

type Conn added in v1.1.4

type Conn = pgx.Conn

The pgx types reachable through WithConn, re-exported from database/pgx. That package resolves per build to upstream pgx or to the vendored copy, so this file needs no build tag, and a *pgxstdlib.Conn is the same type as a *pgx.Conn from database/pgx on either compiler.

On TinyGo the re-export is not a convenience, it is the only access there is: the vendored pgx sits under database/internal/, which a caller outside this repository cannot import, so without these a callback could never name the type it just received. See rawconn.go.

type CopyFromSource added in v1.1.4

type CopyFromSource = pgx.CopyFromSource

The pgx types reachable through WithConn, re-exported from database/pgx. That package resolves per build to upstream pgx or to the vendored copy, so this file needs no build tag, and a *pgxstdlib.Conn is the same type as a *pgx.Conn from database/pgx on either compiler.

On TinyGo the re-export is not a convenience, it is the only access there is: the vendored pgx sits under database/internal/, which a caller outside this repository cannot import, so without these a callback could never name the type it just received. See rawconn.go.

type Identifier added in v1.1.4

type Identifier = pgx.Identifier

The pgx types reachable through WithConn, re-exported from database/pgx. That package resolves per build to upstream pgx or to the vendored copy, so this file needs no build tag, and a *pgxstdlib.Conn is the same type as a *pgx.Conn from database/pgx on either compiler.

On TinyGo the re-export is not a convenience, it is the only access there is: the vendored pgx sits under database/internal/, which a caller outside this repository cannot import, so without these a callback could never name the type it just received. See rawconn.go.

type Notification added in v1.1.4

type Notification = pgx.Notification

The pgx types reachable through WithConn, re-exported from database/pgx. That package resolves per build to upstream pgx or to the vendored copy, so this file needs no build tag, and a *pgxstdlib.Conn is the same type as a *pgx.Conn from database/pgx on either compiler.

On TinyGo the re-export is not a convenience, it is the only access there is: the vendored pgx sits under database/internal/, which a caller outside this repository cannot import, so without these a callback could never name the type it just received. See rawconn.go.

type PgError added in v1.1.4

type PgError = pgx.PgError

The pgx types reachable through WithConn, re-exported from database/pgx. That package resolves per build to upstream pgx or to the vendored copy, so this file needs no build tag, and a *pgxstdlib.Conn is the same type as a *pgx.Conn from database/pgx on either compiler.

On TinyGo the re-export is not a convenience, it is the only access there is: the vendored pgx sits under database/internal/, which a caller outside this repository cannot import, so without these a callback could never name the type it just received. See rawconn.go.

type QueuedQuery added in v1.1.4

type QueuedQuery = pgx.QueuedQuery

The pgx types reachable through WithConn, re-exported from database/pgx. That package resolves per build to upstream pgx or to the vendored copy, so this file needs no build tag, and a *pgxstdlib.Conn is the same type as a *pgx.Conn from database/pgx on either compiler.

On TinyGo the re-export is not a convenience, it is the only access there is: the vendored pgx sits under database/internal/, which a caller outside this repository cannot import, so without these a callback could never name the type it just received. See rawconn.go.

type Row added in v1.1.4

type Row = pgx.Row

The pgx types reachable through WithConn, re-exported from database/pgx. That package resolves per build to upstream pgx or to the vendored copy, so this file needs no build tag, and a *pgxstdlib.Conn is the same type as a *pgx.Conn from database/pgx on either compiler.

On TinyGo the re-export is not a convenience, it is the only access there is: the vendored pgx sits under database/internal/, which a caller outside this repository cannot import, so without these a callback could never name the type it just received. See rawconn.go.

type RowToFunc added in v1.1.4

type RowToFunc[T any] = pgx.RowToFunc[T]

The row-collection helpers. Generic functions can be neither aliased nor bound to a variable, so unlike the types above they are one-line forwards.

type Rows added in v1.1.4

type Rows = pgx.Rows

The pgx types reachable through WithConn, re-exported from database/pgx. That package resolves per build to upstream pgx or to the vendored copy, so this file needs no build tag, and a *pgxstdlib.Conn is the same type as a *pgx.Conn from database/pgx on either compiler.

On TinyGo the re-export is not a convenience, it is the only access there is: the vendored pgx sits under database/internal/, which a caller outside this repository cannot import, so without these a callback could never name the type it just received. See rawconn.go.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL