dalgo2postgres

package module
v0.3.1 Latest Latest
Warning

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

Go to latest
Published: Oct 4, 2026 License: MIT Imports: 23 Imported by: 1

README

dalgo2postgres

PostgreSQL-specific DALgo driver. Wraps github.com/dal-go/dalgo2sql to provide the dal.DB surface, and adds PostgreSQL-native implementations of:

  • dbschema.SchemaReader — schema introspection via information_schema and pg_indexes
  • ddl.Applier — PostgreSQL-flavored CREATE TABLE / CREATE INDEX / DROP TABLE / ALTER TABLE
  • dal.ConcurrencyAware — advertises SupportsConcurrentConnections() = true (PostgreSQL supports concurrent connections from multiple goroutines and processes, unlike SQLite which serializes writers)

Constructor usage

import (
    "github.com/dal-go/dalgo/dal"
    "github.com/dal-go/dalgo2postgres"
    "github.com/dal-go/dalgo2sql"
)

// Minimal — no per-collection primary-key configuration.
db, err := dalgo2postgres.NewDatabase("postgres://user:pass@localhost:5432/mydb?sslmode=disable")

// With per-collection recordset options (required for Insert/Get/Delete with map[string]any data):
db, err := dalgo2postgres.NewDatabaseWithOptions(dsn, dal.NewSchema(nil, nil),
    dalgo2sql.DbOptions{
        Recordsets: map[string]*dalgo2sql.Recordset{
            "widgets": dalgo2sql.NewRecordset("widgets", dalgo2sql.Table,
                []dal.FieldRef{dal.Field("id")}),
        },
    })
if err != nil {
    return err
}
defer db.Close()

Schema reader options

The schema reader (ListCollections, DescribeCollection, ListIndexes, ListConstraints, ListReferrers) lists tables and views of one PostgreSQL schema. Both constructors take optional settings:

db, err := dalgo2postgres.NewDatabase(dsn,
    dalgo2postgres.WithSchema("sales"),                              // default "public"
    dalgo2postgres.WithIdentifierMode(dalgo2postgres.IdentifierExact)) // default IdentifierFoldLower
  • IdentifierFoldLower (default) lower-cases a table name before looking it up, matching the lower-cased tables this package's DDL creates. It is the default so that callers that pass no option and create their tables through this package keep working. A caller that reads a database it did not create with this package must pass IdentifierExact explicitly: with the default, ListCollections reports Album but DescribeCollection("Album") looks for album and answers not found.
  • IdentifierExact looks the table up under exactly the name given, so a table created as "Album" is found as Album and not as album.
  • A nil option is ignored; an unknown IdentifierMode makes the constructor return an error before it connects.

A collection reference that names its own schema (dal.NewQualifiedRootCollectionRef("sales", "Album", "")) is read from that schema, whatever WithSchema says; a reference without a schema is read from the configured one. The reader never answers a reference from a schema it does not name. ListReferrers reports only referrers in the same schema as the queried table; a foreign key into another schema shows up in DescribeCollection as ReferencedNamespace, which is empty when the target is in the table's own schema.

NonDeterministicTextColumns(ctx, ref) lists the columns of a table or view whose equality is not exact (type citext, or a non-deterministic collation such as a case-insensitive ICU collation), in column order. It addresses the table exactly as the schema reader does (reference schema, else WithSchema; name per IdentifierMode), returns an empty list when every column is exact, and a not-found error when the table does not exist, so an empty list always means "exists and exact". A domain over citext and an array of citext are not reported.

WithSchema and IdentifierExact affect only the schema reader. The DDL this package writes still lower-cases every name (see "Identifier case folding" below), and record operations (Get, Insert, queries) address tables as dalgo2sql renders them and resolve them through the connection's search_path.

ListReferrers returns one Referrer per foreign key, with the referencing columns in key order. A table with two foreign keys into the queried table is therefore two entries with the same Collection (a Referrer carries no constraint name); callers that want one entry per table group by Collection.Name(). When the queried reference names its schema, each referrer's Collection names it too, so passing it back to DescribeCollection reads the table that was found.

Error messages name the collection ("sales"."Album" when the reference named its schema).

A column whose type has no dbschema.Type (uuid, json, jsonb, arrays, enums, interval, inet, money, ...) is described as a String field; it never fails the table. Base tables and views are listed; dbschema has no collection kind, so a view cannot be told apart from a table in the result. Materialized views and foreign tables are not listed (PostgreSQL does not expose materialized views in information_schema.tables, and foreign tables are filtered out).

Connection errors

NewDatabase and NewDatabaseWithOptions return a *dalgo2postgres.ConnectionError when they cannot open or reach the server. Its text is built, never filtered:

  • a fixed sentence chosen by the kind of failure, decided from the types in the driver's error chain (errors.As and errors.Is), never from its text;
  • the host, port and database name the driver itself parsed, each only when it passes a strict check and is not repeated from a secret (below).

Nothing from the connection string, and no message of the driver or the server (not a setting name, not a server's "role ... does not exist"), is copied into it: those hold the user name and the password. For the same reason the driver's error is not reachable: errors.Unwrap returns nil. Callers branch on the fields:

var connErr *dalgo2postgres.ConnectionError
if errors.As(err, &connErr) {
    switch connErr.Kind {
    case dalgo2postgres.FailureServer: // connErr.SQLState, for example "28P01"
    case dalgo2postgres.FailureNetwork, dalgo2postgres.FailureTimeout, dalgo2postgres.FailureTLS:
    case dalgo2postgres.FailureInvalidDSN, dalgo2postgres.FailureMisread:
    }
    // connErr.Host, connErr.Port, connErr.Database: empty when not nameable.
}
Kind When
FailureInvalidDSN the driver cannot parse the string, or a file or service it names cannot be read; nothing is named
FailureMisread the string would be read as something else than was meant; refused before any connection (below)
FailureNetwork name resolution failed, or the connection was refused or reset
FailureTLS the TLS handshake failed
FailureTimeout the attempt timed out or was canceled
FailureServer the server answered with an error; SQLState holds its five-character code
FailureOther anything else (also the zero value)

A part is named only when:

  • host is 1 to 253 letters, digits, dots, hyphens, colons and brackets (a name with an underscore, or a socket directory, is left out), and the string names exactly one server (with several, the failure cannot be pinned to one);
  • port is the driver's port, under the same single-server condition;
  • database is 1 to 63 letters, digits, underscores and hyphens;
  • and none of them is equal to the user name or the password, or shares a run of four bytes with either or with a runtime setting the driver kept (for example application_name): postgres:postgres@host/postgres names no database.

What this means for a string you write:

  • Separate key=value pairs with white space. A semicolon, comma or ampersand (the PHP PDO and Npgsql habit, host=h;user=u;password=p) is read by the driver as part of the value before it, so the string connects to the wrong place or not at all; the error names nothing of it (the host and database are left out because they do not pass the check).
  • Write a URL that starts exactly with postgres:// or postgresql:// and percent-encode a password with a special character (p%2Fss, not p/ss). A URL in literal quotes (as docker --env-file keeps them), with a leading space, in capitals or with another scheme (postgresql+psycopg2://), or with an unencoded ? in the password, is read by the driver as key=value pairs whose first setting name holds the credentials, and a server would receive it. Such a string is refused before any connection is attempted (FailureMisread). A URL whose password holds an unencoded / or # is still passed to the driver, because the driver reads it, but its parts are not named.
  • A configuration registered with stdlib.RegisterConnConfig is passed to the driver as before; its host, port and database come from the driver's own error.

Limits: an option value the driver does not keep (sslpassword, a path) cannot be compared with the host and the database, so one that equals them would show the host or database, which is only what it is. And this covers construction only: a later failure to connect (the pool opening a new connection, a dropped connection) comes from the driver without this treatment and may name the database user, never the password.

Type mapping

dbschema.Type PostgreSQL column type
String TEXT (or VARCHAR(n) when Length is set)
Int BIGINT
Float DOUBLE PRECISION
Bool BOOLEAN
Time TIMESTAMPTZ
Decimal NUMERIC (or NUMERIC(total, scale) when Precision is set)
Bytes BYTEA

Running the tests

The tests that touch a live database are gated on the DALGO2POSTGRES_TEST_DSN environment variable. When it is not set, those tests are skipped so plain CI passes without a database.

Start a local PostgreSQL 17 server (Docker):

docker run -d \
  --name postgres17 \
  -e POSTGRES_USER=ovdb \
  -e POSTGRES_PASSWORD=ovdb \
  -e POSTGRES_DB=ovdb \
  -p 15432:5432 \
  postgres:17

Then run the tests:

DALGO2POSTGRES_TEST_DSN='postgres://ovdb:ovdb@127.0.0.1:15432/ovdb?sslmode=disable' \
    go test ./... -count=1 -v

PostgreSQL driver: github.com/jackc/pgx/v5/stdlib (pure Go, CGO_ENABLED=0)

This package uses github.com/jackc/pgx/v5/stdlib — a pure-Go PostgreSQL driver exposed through the standard database/sql interface (driver name "pgx"). No C toolchain is required; the package compiles with CGO_ENABLED=0.

Placeholder dialect

PostgreSQL requires positional parameter markers $1, $2, … rather than the ? style used by SQLite and MySQL. dalgo2postgres automatically sets dalgo2sql.DbOptions.Placeholder = dalgo2sql.PlaceholderDollar so that all SQL emitted through dalgo2sql uses the correct form. This field was added to dalgo2sql as a minimal backward-compatible extension; the zero value (PlaceholderQuestion) preserves the existing behavior for all other drivers.

Duplicate-key classification

record.ErrRecordExists / record.IsAlreadyExists let callers detect an Insert over an existing key without depending on driver-specific error types or message text. dalgo2sql owns the wrapping (it applies fmt.Errorf("%w: %w", record.ErrRecordExists, err) at its single execInsert choke point, covering Insert, InsertMulti, and their transactional variants) but delegates the one question only a driver knows the answer to — is this particular error a unique-key violation? — to dalgo2sql.DbOptions.IsAlreadyExists.

dalgo2postgres supplies that hook: NewDatabase and NewDatabaseWithOptions default DbOptions.IsAlreadyExists to dalgo2postgres.IsAlreadyExists whenever the caller leaves it nil (a caller-supplied hook is never overridden). It reports true when the error is a *pgconn.PgError (from github.com/jackc/pgx/v5/pgconn, the driver this package uses) whose Code is the PostgreSQL SQLSTATE 23505 (unique_violation) — matched on the SQLSTATE code via errors.As, never the error message. Sibling class-23 codes such as 23503 (foreign_key_violation) and 23514 (check_violation) are integrity violations but not duplicate keys, and are deliberately reported as false.

dalgo2postgres.IsAlreadyExists is exported so callers who construct their own dalgo2sql.DbOptions directly (e.g. calling dalgo2sql.NewDatabase themselves) can reuse it instead of rewriting the same *pgconn.PgError check.

Identifier case folding

PostgreSQL folds unquoted identifiers to lower case. dalgo2postgres quotes identifiers (so reserved words and otherwise-illegal names stay usable) but lower-cases them first, so the case-preserving quoted form agrees with the unquoted references that dalgo2sql's DML and dalgo's structured-query rendering emit (which PostgreSQL also folds to lower case). DDL and DML thus always address the same physical identifier.

Consequence: collection (table) and field (column) names are stored lower-cased. Typed clients round-trip transparently because encoding/json unmarshalling is case-insensitive; consumers reading raw records observe lower-cased field names. Fully case-preserving storage would require dalgo's structured-query String() to quote column identifiers, tracked upstream.

Documentation

Overview

Package dalgo2postgres is the PostgreSQL-specific DALgo driver.

It composes github.com/dal-go/dalgo2sql for the dal.DB read/write surface (transactions, recordset reader, Get/Set/Insert/Delete) and adds PostgreSQL-native implementations of:

Index

Constants

View Source
const DefaultSchema = "public"

DefaultSchema is the PostgreSQL schema the schema reader inspects when WithSchema is not used.

Variables

View Source
var Version = resolveVersion(debug.ReadBuildInfo)

Version is the module version recorded in the consuming binary's build information (a tag or a pseudo-version), or "devel" when none is recorded, for example in a local-path replace or a working-tree build. Treat it as read-only.

Functions

func IsAlreadyExists added in v0.2.1

func IsAlreadyExists(err error) bool

IsAlreadyExists is a dalgo2sql.DbOptions.IsAlreadyExists classifier for PostgreSQL via github.com/jackc/pgx/v5 (the driver this package uses — see the "pgx" import in database.go). It reports whether err represents a unique-key violation: the driver reports these as a *pgconn.PgError whose Code is the SQLSTATE "23505" (unique_violation).

Detection matches the SQLSTATE code via errors.As, never the error message — messages are not a stable contract and can vary by locale or server version. A sibling class-23 code such as "23503" (foreign_key_violation) or "23514" (check_violation) is a different kind of constraint failure, not a duplicate key, and is deliberately reported as false here.

NewDatabaseWithOptions (and therefore NewDatabase) wires this in automatically whenever the caller's dalgo2sql.DbOptions.IsAlreadyExists is nil. It is exported so callers who build a dalgo2sql.DbOptions directly — e.g. calling dalgo2sql.NewDatabase themselves rather than going through this package's constructors — can reuse it instead of rewriting the same *pgconn.PgError check.

Types

type ConnectionError added in v0.3.1

type ConnectionError struct {
	// Kind is what failed.
	Kind FailureKind
	// SQLState is the five-character SQLSTATE code of a server error (Kind is
	// [FailureServer]), for example "28P01" for a rejected password.
	SQLState string
	// Host is the server's host name or address, named only when the connection
	// string names exactly one server and the name passes the check.
	Host string
	// Port is the server's port, under the same conditions as Host.
	Port string
	// Database is the database name, named only when it passes the check.
	Database string
	// contains filtered or unexported fields
}

ConnectionError is the error NewDatabase and NewDatabaseWithOptions return when the connection to PostgreSQL cannot be opened or verified.

Its text is built only from a fixed sentence chosen by Kind and from the parts below that passed a strict check. Nothing from the connection string, and nothing from the driver's or the server's own message, is ever copied into it: those hold the user name, the password and other settings, and callers print errors. For the same reason the driver's error is not reachable through errors.Unwrap, errors.Is or errors.As; branch on Kind and SQLState instead.

A part that is empty was not known, or could not be named safely.

func (*ConnectionError) Error added in v0.3.1

func (e *ConnectionError) Error() string

Error returns the text, built from the fixed sentence, the SQLSTATE code and the named parts only.

type Database

type Database struct {
	dal.ConcurrencyAvailable // SupportsConcurrentConnections() = true

	dal.DB // delegate for the dal.DB surface
	// contains filtered or unexported fields
}

Database is the dalgo2postgres driver instance. It implements dal.DB by embedding a dal.DB obtained from dalgo2sql.NewDatabase, and adds PostgreSQL-specific dbschema, ddl, and concurrency surfaces.

The embedded dal.DB (rather than a named field) is what lets Database satisfy dal.DB itself: dal.DB is sealed by an unexported marker method, and embedding is the only way for that method to be promoted onto a decorating type — see dal.NewDB's doc comment.

Construct via NewDatabase. Database values are safe for concurrent use — the underlying PostgreSQL server and pgx connection pool both support concurrent connections from multiple goroutines.

func NewDatabase

func NewDatabase(dsn string, options ...Option) (*Database, error)

NewDatabase opens a connection to the PostgreSQL server identified by dsn using github.com/jackc/pgx/v5/stdlib (pure Go, CGO_ENABLED=0), pings to surface connectivity errors at construction time, wraps the *sql.DB via dalgo2sql.NewDatabase for the dal.DB surface, and returns a *Database that satisfies dal.DB + dal.ConcurrencyAware.

A failure to open or reach the server is a *ConnectionError; its text holds nothing from dsn or from the driver's own message.

Use NewDatabaseWithOptions when you need to supply per-collection primary-key metadata (required for Insert/Get/Delete with map[string]any data). The optional Option values choose the schema and table-name matching the schema reader uses.

func NewDatabaseWithOptions

func NewDatabaseWithOptions(dsn string, schema dal.Schema, opts dalgo2sql.DbOptions, options ...Option) (*Database, error)

NewDatabaseWithOptions is like NewDatabase but accepts a dal.Schema and dalgo2sql.DbOptions so callers can configure per-collection primary-key mappings required by Insert/Get/Delete operations.

Example — open a DB whose "widgets" table has "id" as its primary key:

db, err := dalgo2postgres.NewDatabaseWithOptions(dsn, dal.NewSchema(nil, nil),
    dalgo2sql.DbOptions{
        Recordsets: map[string]*dalgo2sql.Recordset{
            "widgets": dalgo2sql.NewRecordset("widgets", dalgo2sql.Table,
                []dal.FieldRef{dal.Field("id")}),
        },
    })

The optional Option values choose the PostgreSQL schema the schema reader inspects (WithSchema) and whether it matches table names exactly (WithIdentifierMode). A nil option is ignored; an unknown IdentifierMode is an error, returned before any connection is attempted.

A failure to open or reach the server is a *ConnectionError, as for NewDatabase.

func (*Database) Adapter

func (d *Database) Adapter() dal.Adapter

Adapter returns the driver/version identifier.

func (*Database) AlterCollection

func (d *Database) AlterCollection(ctx context.Context, name string, ops ...ddl.AlterOp) error

AlterCollection applies ops in order inside a single transaction. Partial failures roll back and leave the collection untouched.

func (*Database) Close

func (d *Database) Close() error

Close closes the underlying *sql.DB. After Close the Database value is unusable; further method calls will fail with an error from database/sql.

func (*Database) CreateCollection

func (d *Database) CreateCollection(ctx context.Context, c dbschema.CollectionDef, opts ...ddl.Option) error

CreateCollection creates a table and its inline indexes transactionally. On any error, the transaction rolls back and no schema state remains.

func (*Database) Delete

func (d *Database) Delete(ctx context.Context, key *dalrecord.Key) error

func (*Database) DeleteMulti

func (d *Database) DeleteMulti(ctx context.Context, keys []*dalrecord.Key) error

func (*Database) DescribeCollection

func (d *Database) DescribeCollection(ctx context.Context, ref *dal.CollectionRef) (*dbschema.CollectionDef, error)

DescribeCollection returns the full schema definition for the named table or view. It queries information_schema for columns and primary-key membership. The table is read from the schema the reference names, else from the configured schema. A column whose PostgreSQL type has no dbschema counterpart (uuid, json, jsonb, arrays, enums, interval, inet, money, ...) is reported as a String field rather than failing the whole table.

func (*Database) DropCollection

func (d *Database) DropCollection(ctx context.Context, name string, opts ...ddl.Option) error

DropCollection drops the table and all its indexes (Postgres cascades automatically).

func (*Database) ExecuteQueryToRecordsReader

func (d *Database) ExecuteQueryToRecordsReader(ctx context.Context, query dal.Query) (dal.RecordsReader, error)

func (*Database) ExecuteQueryToRecordsetReader

func (d *Database) ExecuteQueryToRecordsetReader(ctx context.Context, query dal.Query, opts ...recordset.Option) (dal.RecordsetReader, error)

func (*Database) Exists

func (d *Database) Exists(ctx context.Context, key *dalrecord.Key) (bool, error)

func (*Database) Get

func (d *Database) Get(ctx context.Context, record dalrecord.Record) error

func (*Database) GetMulti

func (d *Database) GetMulti(ctx context.Context, records []dalrecord.Record) error

func (*Database) ID

func (d *Database) ID() string

ID returns the driver-issued database ID (delegated to dalgo2sql).

func (*Database) Insert

func (d *Database) Insert(ctx context.Context, record dalrecord.Record, opts ...dal.InsertOption) error

func (*Database) ListCollections

func (d *Database) ListCollections(ctx context.Context, parent *dalrecord.Key) ([]dal.CollectionRef, error)

ListCollections returns the tables and views of the configured schema (DefaultSchema unless WithSchema says otherwise) in alphabetical order, under the names PostgreSQL reports. The parent *dalrecord.Key is ignored — Postgres has a flat table namespace within a schema.

func (*Database) ListConstraints

func (d *Database) ListConstraints(ctx context.Context, ref *dal.CollectionRef) ([]dbschema.ConstraintDef, error)

ListConstraints returns a best-effort survey of constraints on the table via information_schema, in the schema the reference names, else the configured schema:

  • PRIMARY KEY constraint (one row if any PK columns exist)
  • UNIQUE constraints
  • FOREIGN KEY constraints

CHECK constraints are NOT enumerated here; read them from DescribeCollection if needed.

func (*Database) ListIndexes

func (d *Database) ListIndexes(ctx context.Context, ref *dal.CollectionRef) ([]dbschema.IndexDef, error)

ListIndexes returns the non-primary-key indexes on the named table via pg_indexes, from the schema the reference names, else the configured schema.

func (*Database) ListReferrers

func (d *Database) ListReferrers(ctx context.Context, ref *dal.CollectionRef) ([]dbschema.Referrer, error)

ListReferrers returns the foreign keys that reference ref, one Referrer per foreign key, with the referencing columns in key order. Only referrers in the schema of ref (the schema the reference names, else the configured one) are reported, because the reader addresses a single schema. It reads pg_catalog rather than information_schema: constraint names are unique only per table in PostgreSQL, so information_schema joins on the name alone mix up tables that reuse a name.

func (*Database) NonDeterministicTextColumns added in v0.3.0

func (d *Database) NonDeterministicTextColumns(ctx context.Context, ref *dal.CollectionRef) ([]string, error)

NonDeterministicTextColumns returns the columns of the referenced table (or view) whose equality is not exact: columns of type citext and columns with a non-deterministic collation (for example a case-insensitive ICU collation), in column order. Under such a column "A" = "a" can be true, so a comparison or a group-by on it does not mean what a byte-for-byte match means; callers use this list to know where a pushed-down comparison differs from Go's.

The table is looked up in the schema the reference names (dal.NewQualifiedRootCollectionRef), else in the Database's schema (DefaultSchema unless WithSchema says otherwise), and its name is matched as the schema reader matches it (see IdentifierMode). Both are sent as bound parameters. A table with no such column yields an empty non-nil slice. A table that does not exist (under the name as IdentifierMode resolves it) yields a "not found" error, never an empty list, so an empty result means the table exists and none of its columns is inexact in the sense below.

The match is on the column's own type and collation. A column whose type is a domain over citext, or an array of citext, is not reported; treat such columns as inexact yourself.

func (*Database) RunReadonlyTransaction

func (d *Database) RunReadonlyTransaction(ctx context.Context, f dal.ROTxWorker, opts ...dal.TransactionOption) error

func (*Database) RunReadwriteTransaction

func (d *Database) RunReadwriteTransaction(ctx context.Context, f dal.RWTxWorker, opts ...dal.TransactionOption) error

func (*Database) Schema

func (d *Database) Schema() dal.Schema

Schema returns the dal-level Schema (delegated to dalgo2sql).

func (*Database) Set

func (d *Database) Set(ctx context.Context, record dalrecord.Record) error

func (*Database) SetMulti

func (d *Database) SetMulti(ctx context.Context, records []dalrecord.Record) error

func (*Database) SupportsConcurrentConnections added in v0.2.0

func (d *Database) SupportsConcurrentConnections() bool

SupportsConcurrentConnections reports PostgreSQL's own concurrency behaviour (always true — see dal.ConcurrencyAvailable), not dalgo2sql's. An explicit method is required here: dal.ConcurrencyAvailable and the embedded dal.DB (whose Backend requirement embeds dal.ConcurrencyAware) both declare this method at the same promotion depth, which Go otherwise treats as an ambiguous selector.

func (*Database) SupportsTransactionalDDL

func (d *Database) SupportsTransactionalDDL() bool

SupportsTransactionalDDL reports that PostgreSQL supports transactional DDL — every CREATE / DROP / ALTER statement can be wrapped in a BEGIN/COMMIT and is rolled back atomically on commit failure.

func (*Database) Update

func (d *Database) Update(ctx context.Context, key *dalrecord.Key, updates []update.Update, preconditions ...dal.Precondition) error

func (*Database) UpdateMulti

func (d *Database) UpdateMulti(ctx context.Context, keys []*dalrecord.Key, updates []update.Update, preconditions ...dal.Precondition) error

func (*Database) UpdateRecord

func (d *Database) UpdateRecord(ctx context.Context, record dalrecord.Record, updates []update.Update, preconditions ...dal.Precondition) error

UpdateRecord is not supported at the database level by dalgo2sql; use Update with an explicit key instead, or call UpdateRecord inside a RunReadwriteTransaction where the transaction object does support it.

func (*Database) Upsert

func (d *Database) Upsert(ctx context.Context, record dalrecord.Record) error

type FailureKind added in v0.3.1

type FailureKind int

FailureKind says what kind of failure a ConnectionError reports. It decides the sentence the error's text uses.

const (
	// FailureOther is any failure the kinds below do not describe. It is the
	// zero value.
	FailureOther FailureKind = iota
	// FailureInvalidDSN: the driver cannot parse the connection string, or a file
	// or service the string names cannot be read.
	FailureInvalidDSN
	// FailureMisread: the driver would accept the string but read it as something
	// else than was meant (a URL in quotes, with a leading space or with another
	// scheme). It is refused before any connection is attempted.
	FailureMisread
	// FailureNetwork: the server could not be reached (name resolution, refused or
	// reset connection).
	FailureNetwork
	// FailureTLS: the TLS handshake with the server failed.
	FailureTLS
	// FailureTimeout: the attempt timed out or was canceled.
	FailureTimeout
	// FailureServer: the server answered with an error. [ConnectionError.SQLState]
	// holds its SQLSTATE code.
	FailureServer
)

type IdentifierMode added in v0.2.4

type IdentifierMode int

IdentifierMode says how the schema reader treats the table names it is given.

const (
	// IdentifierFoldLower lower-cases a table name before looking it up. It
	// matches the DDL this package writes (see quoteIdent), which stores every
	// name lower-cased. It is the default, so callers that pass no option and
	// create their tables through this package keep their behaviour. A caller
	// that reads a database it did not create with this package (mixed-case
	// table names) must choose [IdentifierExact] explicitly: with the default,
	// ListCollections reports "Album" but DescribeCollection("Album") looks for
	// "album" and answers not found.
	IdentifierFoldLower IdentifierMode = iota

	// IdentifierExact looks a table up under exactly the name it is given.
	// PostgreSQL names are case-sensitive once quoted, so "Album" and "album"
	// are different tables; choose this mode to read a database whose tables
	// were created with mixed-case names.
	//
	// The mode affects only the schema reader (ListCollections,
	// DescribeCollection, ListIndexes, ListConstraints, ListReferrers,
	// NonDeterministicTextColumns). The DDL this package writes still
	// lower-cases every name, and record operations address tables as dalgo2sql
	// renders them, resolved by the connection's search_path.
	IdentifierExact
)

type Option added in v0.2.4

type Option func(*Database)

Option customises how a Database reads its schema.

An option must be free of side effects and must only set fields of the Database it is given: the constructors apply every option twice, first to a zero Database (to reject an invalid set before any connection is attempted, when the embedded dal.DB is still nil), then to the real one. An option that touches anything else, or that does something observable when it runs, would run twice and, the first time, against a Database that has no connection.

func WithIdentifierMode added in v0.2.4

func WithIdentifierMode(mode IdentifierMode) Option

WithIdentifierMode selects how table names given to the schema reader are matched. See IdentifierMode.

func WithSchema added in v0.2.4

func WithSchema(name string) Option

WithSchema makes the schema reader inspect the named PostgreSQL schema instead of DefaultSchema. An empty name keeps the default. The name is always sent to the server as a bound parameter, never inside SQL text.

A collection reference that names its own schema (dal.NewQualifiedRootCollectionRef) is read from that schema instead.

The option affects only the schema reader (ListCollections, DescribeCollection, ListIndexes, ListConstraints, ListReferrers, NonDeterministicTextColumns). The DDL this package writes and record operations still follow the connection's search_path.

Jump to

Keyboard shortcuts

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