dalgo2sqlite

package module
v0.1.16 Latest Latest
Warning

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

Go to latest
Published: Sep 21, 2026 License: MIT Imports: 15 Imported by: 0

README

dalgo2sqlite

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

  • dbschema.Adapter — schema introspection via sqlite_master and pragma_table_info
  • ddl.Applier — SQLite-flavored CREATE TABLE / CREATE INDEX / DROP TABLE etc.
  • dal.ConcurrencyAware — advertises Concurrency() = 1 for write paths (SQLite is single-writer)

Used by consumers (e.g. datatug-cli's db copy) that need schema-modification and concurrency hints through the unified DALgo abstraction without hand-rolling engine-specific SQL.

Our approach to development

We build with our own tooling:

  • SpecScore — specify requirements as SpecScore.md artifacts
  • SpecStudio — author & manage specs across their lifecycle
  • inGitDB — store structured data in Git where applicable
  • DALgo — data access layer for Go
  • cover100.dev — drive toward 100% test coverage
  • DataTug — query & explore data

SQLite driver: modernc.org/sqlite (pure Go, CGO_ENABLED=0)

This package uses modernc.org/sqlite — a pure-Go transpilation of the SQLite C library — instead of the former github.com/mattn/go-sqlite3 cgo binding. The migration was done because:

  • The cgo driver required a C toolchain in every build environment (CI, containers, cross-compilation targets).
  • It prevented single static binaries for downstream consumers such as datatug-cli.
  • modernc.org/sqlite exposes the same database/sql interface (driver name "sqlite" instead of "sqlite3"), so dalgo2sql continues to work unchanged.
  • All existing SQLite features relied on here — sqlite_master introspection, PRAGMA table/index queries, transactional DDL — behave identically under the pure-Go driver.

The cgo_enabled: true flag can be removed from this repo's CI workflow and from any downstream workflow that no longer needs cgo for other reasons.

DTQL JOIN acceptance

The checked-in testdata/joins directory contains a small Chinook-shaped SQLite seed, the canonical nested Invoice/Customer/Employee DTQL query, and expected rows. The test loads the seed into a temporary database, runs the query through both the database and read-transaction entry points, and checks ordered results and LEFT JOIN null extension. A second query carries independent algorithm preferences on outer, nested, and sibling JOINs; SQLite's native execution may ignore those physical preferences and must return the same rows. The tests also check the canonical fixture hashes, their DALgo source commit, and the expected diagnostics for malformed JOIN references.

Run the complete acceptance journey from this repository's root:

go test ./end2end -run 'TestDTQL(NestedJoinChinook|JoinFixtureManifest|JoinNegativeFixtures)' -count=1

Documentation

Overview

Package dalgo2sqlite is the SQLite-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 SQLite-native implementations of:

Index

Constants

View Source
const Version = "0.1.0"

Version is the dalgo2sqlite package version. Updated by hand on each release; consumed by Adapter.Version().

Variables

This section is empty.

Functions

func IsAlreadyExists added in v0.1.6

func IsAlreadyExists(err error) bool

IsAlreadyExists reports whether err — the raw error modernc.org/sqlite (the pure-Go SQLite driver this adapter registers; see database.go) returns for a failed INSERT — represents a duplicate-key violation: a duplicate primary key or a duplicate value in a UNIQUE index.

It is dalgo2sqlite's implementation of the github.com/dal-go/dalgo2sql.DbOptions.IsAlreadyExists hook, which dalgo2sql itself cannot supply because detecting a duplicate key is driver-specific. NewDatabase and NewDatabaseWithOptions wire it in by default whenever the caller-supplied DbOptions leaves IsAlreadyExists nil (see database.go), so ordinary use needs no extra configuration. It is exported so a caller assembling a custom dalgo2sql.DbOptions can still reference it directly — e.g. to compose it with another classifier, or to confirm what it matches.

It matches only on errors.As(err, *sqlite.Error) and that error's extended result code — never on message text, which is not a stable contract across modernc.org/sqlite versions or SQLite builds. The primary result code SQLITE_CONSTRAINT (19) is deliberately not matched: it also covers NOT NULL, CHECK, and FOREIGN KEY violations, none of which are duplicate keys, so matching it would misclassify them.

Types

type Database

type Database struct {
	dal.NoConcurrency // SupportsConcurrentConnections() = false

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

Database is the dalgo2sqlite driver instance. It implements dal.DB by embedding a dal.DB obtained from dalgo2sql.NewDatabase, and adds SQLite-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 only insofar as SQLite itself is — readers can be concurrent under WAL mode; writers serialize.

func NewDatabase

func NewDatabase(dbPath string) (*Database, error)

NewDatabase opens (or creates) the SQLite file at dbPath using modernc.org/sqlite (pure Go, CGO_ENABLED=0), pings to surface malformed-file 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.

Use NewDatabaseWithOptions when you need to supply per-collection primary-key metadata (required for Insert/Get/Delete with map[string]any data).

func NewDatabaseWithOptions added in v0.0.17

func NewDatabaseWithOptions(dbPath string, schema dal.Schema, opts dalgo2sql.DbOptions) (*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.

If opts.IsAlreadyExists is nil, it defaults to this package's IsAlreadyExists, so an Insert over an existing primary key or unique index fails with an error satisfying record.IsAlreadyExists without any extra configuration. Set opts.IsAlreadyExists explicitly to override that default (e.g. with a func that also calls dalgo2sqlite.IsAlreadyExists).

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

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

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 is implemented in T15.

func (*Database) DropCollection

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

DropCollection drops the table; SQLite cascades to its indexes.

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 *record.Key) ([]dal.CollectionRef, error)

ListCollections returns the user-defined tables in alphabetical order. The parent *record.Key is ignored — SQLite has no catalog/schema hierarchy.

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:

  • The primary-key constraint (one row if any PK columns exist)
  • Foreign-key constraints from PRAGMA foreign_key_list

CHECK clauses and inline NOT NULL constraints are NOT enumerated (SQLite doesn't expose CHECK source portably). Callers read those from DescribeCollection.Fields.

func (*Database) ListIndexes

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

ListIndexes is implemented in T16.

func (*Database) ListReferrers

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

ListReferrers performs an O(N) scan: for each other user-defined table, query PRAGMA foreign_key_list and check whether any row references ref.Name.

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.1.0

func (d *Database) SupportsConcurrentConnections() bool

SupportsConcurrentConnections reports SQLite's own concurrency behaviour (always false — see dal.NoConcurrency), not dalgo2sql's. An explicit method is required here: dal.NoConcurrency 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 SQLite 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

Jump to

Keyboard shortcuts

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