sqlstore

package
v1.2.1 Latest Latest
Warning

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

Go to latest
Published: Sep 11, 2026 License: AGPL-3.0 Imports: 17 Imported by: 0

README

sqlstore

internal/sqlstore owns Starport's relational storage contract. It is the SQL twin of internal/storage, and the two packages pair the same way:

State Embedded (single node) Connect (multi-node)
Key-value Badger Valkey
Relational SQLite (pure Go, no cgo) PostgreSQL or MySQL

A single-node deployment gets a real database with no extra process: the embedded SQLite file lives at data/sqlite/starport.db beside the Badger directory, and the development runtime keeps it in memory. When a deployment scales past one gateway, the operator points every node at one shared server, and nothing above this package changes.

Selection

The store is selected by configuration alone:

STARPORT_STORAGE_SQL_MODE=sqlite          # default; SQLITE_PATH selects the file
STARPORT_STORAGE_SQL_MODE=postgres
STARPORT_STORAGE_SQL_POSTGRES_URL=postgres://starport@db.example:5432/starport
STARPORT_STORAGE_SQL_MODE=mysql
STARPORT_STORAGE_SQL_MYSQL_DSN='starport@tcp(db.example:3306)/starport'

Open validates the configuration, dispatches on the type, and for the network backends proves the server answers before returning. Repositories read and write through database/sql on the returned DB; Dialect() names the engine for the rare statement that cannot be written once for all three.

Schema

This package owns every migration. A migration is one .sql file named NNNN_description.sql under migrations/<dialect>/; the three files with one name are the same logical migration, written per dialect because the engines disagree on the margins (MySQL cannot index an unbounded TEXT column or parse ON CONFLICT). Migrate applies the dialect's files in name order, once each, and records what it applied. A test holds the three dialect sets to the same file names, and the shared contract tests hold every backend to the same resulting behavior.

Write MySQL migrations idempotent-safe (IF NOT EXISTS, INSERT IGNORE): MySQL auto-commits DDL, so a failed multi-statement file can leave early statements applied with no record, and the retry must tolerate them.

Tests

go test ./internal/sqlstore/... always proves the embedded backend. The network backends join the same contract suite when the environment names a server, following the Valkey precedent:

TEST_POSTGRES_URL=postgres://starport@127.0.0.1:5432/starport_test \
TEST_MYSQL_DSN='starport@tcp(127.0.0.1:3306)/starport_test' \
go test ./internal/sqlstore/...

Credentials ride the URL or DSN in their usual place; the examples omit them so no scanner mistakes documentation for a secret.

The suite drops and recreates its tables, so point it at a disposable database.

Documentation

Overview

Package sqlstore owns Starport's relational storage contract. It is the SQL twin of internal/storage: where that package pairs an embedded Badger store with a Valkey connect for shared state, this one pairs an embedded SQLite database that rides the binary with a network SQL connect for a multi-node deployment. Relational concepts — identity, teams, grants, account templates — keep their repositories on this contract and never name a driver or a dialect themselves.

The embedded backend uses a pure-Go SQLite driver, so the single-binary install story holds: no cgo, no system library, no separate database process. The package also owns the schema: every migration lives in the embedded migrations directory here, and Migrate applies them in order, once each, recording what it applied. No other package writes schema.

Index

Constants

View Source
const (
	// TypeSQLite is the embedded backend: a database file beside the
	// deployment's other data, created on first open. An empty path keeps
	// the database in memory, which is the development runtime's choice.
	TypeSQLite = "sqlite"
	// TypePostgres is the PostgreSQL connect for a multi-node deployment,
	// the relational counterpart of the Valkey connect.
	TypePostgres = "postgres"
	// TypeMySQL is the MySQL connect for a multi-node deployment.
	TypeMySQL = "mysql"
)

Relational backend type constants.

Variables

View Source
var (
	// ErrUnknownType is returned when the configuration names a backend
	// this package does not serve.
	ErrUnknownType = errors.New("unknown sqlstore type")
	// ErrClosed is returned when an operation reaches a closed store.
	ErrClosed = errors.New("sqlstore closed")
)

Common errors returned by sqlstore operations.

Functions

This section is empty.

Types

type Config

type Config struct {
	Type     string         `env:"TYPE,default=sqlite"`
	SQLite   SQLiteConfig   `env:",prefix=SQLITE_"`
	Postgres PostgresConfig `env:",prefix=POSTGRES_"`
	MySQL    MySQLConfig    `env:",prefix=MYSQL_"`
}

Config selects and configures a relational backend, the way storage.Config selects a key-value one.

func (Config) Validate

func (c Config) Validate() error

Validate reports a configuration this package cannot open.

type DB

type DB struct {
	*sql.DB
	// contains filtered or unexported fields
}

DB is one open relational store. It embeds the standard connection pool, so a repository reads and writes through database/sql, and it names the dialect for the rare statement that cannot be written once for every backend.

func Open

func Open(config Config) (*DB, error)

Open creates a relational store from the configuration. It follows the Go convention internal/storage.Open set: validate, then dispatch on the type.

func (*DB) Bind

func (db *DB) Bind(query string) string

Bind rewrites ? placeholders into the dialect's form. PostgreSQL numbers its placeholders; the other engines take ? as written. A repository on this contract writes its statements once with ? and binds per dialect.

func (*DB) Dialect

func (db *DB) Dialect() string

Dialect names the SQL dialect this store speaks: one of the Type constants.

func (*DB) Migrate

func (db *DB) Migrate(ctx context.Context) error

Migrate brings the store's schema to the current set of embedded migrations. It is safe to call on every startup: an already-applied file is skipped, a new one is applied, and a failure leaves the recorded state equal to what actually ran.

func (*DB) Ping

func (db *DB) Ping(ctx context.Context) error

Ping reports whether the store is reachable, bounded by the context.

type MySQLConfig

type MySQLConfig struct {
	// DSN is a go-sql-driver DSN such as
	// user:password@tcp(host:3306)/starport.
	DSN string `env:"DSN"`
}

MySQLConfig configures the MySQL connect.

type PostgresConfig

type PostgresConfig struct {
	// URL is a postgres:// connection URL, database and credentials
	// included.
	URL string `env:"URL"`
}

PostgresConfig configures the PostgreSQL connect.

type SQLiteConfig

type SQLiteConfig struct {
	// Path is the database file. An empty path opens an in-memory
	// database that lives and dies with the process.
	Path string `env:"PATH"`
}

SQLiteConfig configures the embedded backend.

Jump to

Keyboard shortcuts

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