decl

package
v0.0.0-...-575b669 Latest Latest
Warning

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

Go to latest
Published: Nov 4, 2026 License: MIT Imports: 8 Imported by: 0

Documentation

Overview

Package decl declares database schemas as plain Go values that build directly into the meta model. A catalog (decl.NewCatalog) is the reusable root: each Table call assembles and returns a meta.Table immediately. Foreign keys name their target table (References("users", "id")) and are resolved when the catalog Builds, so tables may be declared in any order, reference themselves, and be split across files or packages. Build assembles the meta.Metadata that every generator — the typed table bindings (codegen) and the DDL (meta.Metadata.CreateSQL) — consumes.

A catalog package conventionally takes the name schema and imports this package for the building blocks:

package schema

import (
	"github.com/aita/sqlkit/sql"
	"github.com/aita/sqlkit/decl"
)

var Catalog = decl.NewCatalog()

var Users = Catalog.Table("users",
	decl.Column("id", decl.BigInt, decl.PrimaryKey(), decl.Default()),
	decl.Column("email", decl.VarChar(255), decl.Unique()),
	decl.Column("name", decl.Text, decl.Nullable()),
	decl.Column("created_at", decl.Timestamp, decl.Default(sql.Raw("now()"))),
)

var Posts = Catalog.Table("posts",
	decl.Column("id", decl.BigInt, decl.PrimaryKey(), decl.Default()),
	decl.Column("user_id", decl.BigInt, decl.References("users", "id")),
	decl.Column("title", decl.Text),
	decl.Index("user_id"),
	decl.Check(sql.Raw("title <> ''")),
)

Catalog.Table returns a meta.Table, so Users and Posts are meta.Table values. Column types are portable (meta.Type carries both dialects), so the table is dialect-agnostic; the generators resolve the type when you choose a dialect. decl is purely the schema DSL: Build resolves foreign keys, validates, and assembles the meta.Metadata the generators consume. A small generator program turns the catalog into Go source and DDL:

md, err := schema.Catalog.Build()
if err != nil { ... }
files, err := codegen.Generate(md, codegen.Options{PackageName: "appdb"})
err = codegen.Write("appdb", files)
d, err := sql.DialectByName("postgres")
sqlText, err := md.CreateSQL(d)

Tables declared with Catalog.Table live in the unnamed default schema and generate unqualified names (use this for schemaless engines like MySQL or SQLite); Catalog.Schema("public").Table(...) qualifies them. Columns are NOT NULL unless marked Nullable. Mistakes — duplicate names, dangling foreign keys — are collected and reported by Build.

Index

Constants

View Source
const (
	NoAction   = meta.NoAction
	Restrict   = meta.Restrict
	Cascade    = meta.Cascade
	SetNull    = meta.SetNull
	SetDefault = meta.SetDefault
)

The referential actions a foreign key may take, for use with OnDelete/OnUpdate.

Variables

View Source
var (
	Bool     = Type("bool")
	SmallInt = Type("smallint")
	Int      = Type("int")
	BigInt   = Type("bigint")
	Float    = Type("float")
	Double   = Type("double")
	Text     = Type("text")
	Bytes    = Type("bytes")
	JSON     = Type("json")
	UUID     = DataType{/* contains filtered or unexported fields */}
	Date     = Type("date")
	Time     = Type("time")
	// Timestamp is a point in time: timestamptz on PostgreSQL, datetime on
	// MySQL. Use Type("timestamp") for the engines' bare timestamp types.
	Timestamp = Type("timestamp")
)

Portable column types. Each names a registered kind, resolved to the engine name of the chosen dialect by the generators.

Functions

func BuildColumn

func BuildColumn(element TableElement) (meta.Column, error)

BuildColumn assembles a single meta.Column from one decl Column element, for operations that take a lone column such as ALTER TABLE ADD COLUMN. The element must declare exactly one column; column-level Unique/References (table-level constructs) carry no meaning on a lone column and are dropped.

func BuildTable

func BuildTable(schema, name string, elements ...TableElement) (meta.Table, error)

BuildTable assembles a single meta.Table from decl table elements without a catalog, validating it. It is the reuse point for tools that build one table at a time — such as migration operations — so they share the decl vocabulary (Column, Index, UniqueIndex, Check, PrimaryKey, TableComment) and its validation. Foreign keys declared with References/ForeignKey need their target table in a catalog, so name-based foreign keys belong in the caller (e.g. the migrate operator) instead.

func GoType

func GoType(t reflect.Type) meta.GoType

GoType derives a meta.GoType from a Go type for pinning a column's Go type (DataType.Go): the import path comes from the type's package and the name from its source spelling (e.g. "uuid.UUID", "string"). GoTypeOf is the generic, reflection-free spelling of the common case.

decl.GoType(reflect.TypeOf(uuid.UUID{})) // {ImportPath: "github.com/google/uuid", Name: "uuid.UUID"}

func GoTypeOf

func GoTypeOf[T any]() meta.GoType

GoTypeOf derives a meta.GoType from the type parameter T, e.g. decl.GoTypeOf[uuid.UUID](). It is GoType applied to reflect.TypeOf a T.

func RegisterType

func RegisterType(kind string, names map[string]string)

RegisterType registers (or extends) the engine type names a type kind resolves to per dialect, so a custom type or a new dialect is added in one place rather than by changing the DSL. names is keyed by dialect ("postgres", "mysql", "sqlite", "libsql", ...); entries merge into any existing registration for the kind, so a later call can add a dialect to a built-in kind. After registering, declare columns of the kind with Type(kind):

decl.RegisterType("uuid", map[string]string{"sqlite": "text"})
decl.RegisterType("geometry", map[string]string{
	"postgres": "geometry", "mysql": "geometry", "sqlite": "blob",
})
decl.Column("shape", decl.Type("geometry"))

Types

type Catalog

type Catalog struct {
	// contains filtered or unexported fields
}

Catalog is the root the tables hang off: it accumulates the meta.Tables declared on it, plus any declaration errors, and Builds them into a meta.Metadata. Create one with NewCatalog.

func NewCatalog

func NewCatalog(opts ...CatalogOption) *Catalog

NewCatalog returns an empty catalog to declare tables on. It is the entry point of the DSL: var Catalog = decl.NewCatalog(). Options configure catalog-wide defaults such as DefaultSchema.

func (*Catalog) BindingNames

func (d *Catalog) BindingNames() func(meta.Table) string

BindingNames returns a codegen.Options.BindingName hook that maps each table to the identifier declared for it with BindingName, and the empty string for tables without one (so codegen falls back to its default derivation). It returns nil when no table in the catalog declares a BindingName, so callers can assign it unconditionally:

codegen.Generate(md, codegen.Options{
	PackageName: "appdb",
	BindingName: catalog.BindingNames(),
})

func (*Catalog) Build

func (d *Catalog) Build() (meta.Metadata, error)

Build resolves the catalog's foreign keys, validates it, and returns the assembled meta.Metadata. Columns, indexes, and checks are assembled eagerly by Table; foreign keys are resolved here, once every table is known, so they may target a table declared later or the table itself. No dialect is needed — column types stay portable and the generators resolve them. Build does not mutate the catalog, so it may be called more than once. The returned meta.Metadata is the dialect-agnostic value the codegen generator and the DDL renderer consume.

func (*Catalog) Enum

func (d *Catalog) Enum(name string, values ...string)

Enum declares an enum type in the catalog's default schema. Use Catalog.Schema("other").Enum(...) to place it in a different schema.

func (*Catalog) Extension

func (d *Catalog) Extension(name string, opts ...ExtensionOption)

Extension declares a database extension (PostgreSQL CREATE EXTENSION). The extension is created idempotently (IF NOT EXISTS). Extensions are not schema-scoped objects, so they are declared on the catalog rather than a Schema handle; pass ExtensionSchema to install the extension's objects into a schema.

func (*Catalog) Grant

func (d *Catalog) Grant(privileges []string, opts ...GrantOption)

Grant declares a privilege grant (GRANT ... ON ... TO ...). Pass no privileges for ALL PRIVILEGES. The grant needs at least one object (GrantOn / GrantOnType) and one grantee (GrantTo); CreateSQL emits the GRANT and DropSQL the REVOKE.

func (*Catalog) MaterializedView

func (d *Catalog) MaterializedView(name string, query sql.Query, opts ...MaterializedViewOption)

MaterializedView declares a materialized view in the catalog's default schema. Use Catalog.Schema("other").MaterializedView(...) to place it elsewhere.

func (*Catalog) Schema

func (d *Catalog) Schema(name string, opts ...SchemaOption) *Schema

Schema returns a handle for the named schema (PostgreSQL schema / MySQL database). Declaring tables through it qualifies them with the schema name.

By default the schema is assumed to already exist and is not managed by sqlkit. Pass Create to put it under migration management: the generated CreateSQL emits a CREATE SCHEMA IF NOT EXISTS for it and DropSQL a DROP SCHEMA. sqlkit never creates the default schema (Catalog.Table's schema), which is assumed to exist; mark additional schemas explicitly.

func (*Catalog) Sequence

func (d *Catalog) Sequence(name string, opts ...SequenceOption)

Sequence declares a sequence in the catalog's default schema. Use Catalog.Schema("other").Sequence(...) to place it in a different schema.

func (*Catalog) Table

func (d *Catalog) Table(name string, elements ...TableElement) meta.Table

Table declares a table in the catalog's default schema (unnamed unless set with DefaultSchema) and returns the assembled meta.Table. Use Catalog.Schema("other").Table(...) to place a table in a different schema.

type CatalogOption

type CatalogOption func(*Catalog)

CatalogOption configures a Catalog at construction.

func DefaultSchema

func DefaultSchema(name string) CatalogOption

DefaultSchema sets the schema that Catalog.Table (and any table declared without an explicit Schema handle) lands in. Without it the default schema is unnamed, which generates unqualified names.

type ColumnOption

type ColumnOption func(*columnDef)

ColumnOption customizes a Column declaration.

func Comment

func Comment(text string) ColumnOption

Comment sets the column's comment; the code generator carries it into the generated bindings as a doc comment. Table comments use the table-level TableComment.

func Default

func Default(expr ...sql.Expression) ColumnOption

Default marks the column as having a database-side default, so generated bindings report it in Defaulted and inserts can omit it. The optional expression is used by the DDL generator: Default(sql.Raw("now()")) renders as DEFAULT now(), while a bare Default() on an integer primary key renders as a serial/auto-increment column. A blank expression (nil or an empty sql.Raw) is treated as absent, equivalent to a bare Default(). The expression must not bind values; wrap literals in sql.Raw to inline them.

func Nullable

func Nullable() ColumnOption

Nullable allows NULL values; the code generator maps the column to a pointer type.

func PrimaryKey

func PrimaryKey() ColumnOption

PrimaryKey marks the column as (part of) the table's primary key. Mark several columns to declare a composite key.

func References

func References(refTable string, refColumn string, opts ...FKOption) ColumnOption

References declares the column a foreign key to column refColumn of refTable. refTable names a table in the catalog ("users", or "schema.users" to cross schemas), resolved at Build, so it may be declared after this one or be this table itself (a self-reference). Composite keys use the table-level ForeignKey instead.

func Unique

func Unique() ColumnOption

Unique declares a single-column unique index (named uq_<table>_<column>) on the column. Multi-column uniqueness uses the table-level UniqueIndex.

type DataType

type DataType struct {
	// contains filtered or unexported fields
}

DataType is a portable column type implementing meta.Type. It names a canonical type kind (e.g. "bigint", "varchar") plus any length/precision modifiers; the per-dialect engine type name is resolved through the type registry when a generator picks a dialect, so meta stays dialect-free and new dialects (sqlite, libsql, ...) are added by registering, not by changing this type.

A DataType fixes the DB type — Type names a registered kind (or, for an unregistered name, one used verbatim on every dialect) and TypeFor pins a distinct engine name per dialect inline — and can additionally pin the Go type the code generator maps it to with Go/GoFor, so a custom column type is fully specified at the schema source.

func Array

func Array(elem DataType) DataType

Array declares a one-dimensional array of the element type, e.g. Array(Text) for text[] or Array(Int) for int4[]. Arrays are a PostgreSQL feature: the column is unavailable on other dialects (the generators report it as a missing type when targeting them). The code generator maps the column to a Go slice of the element's type ([]string, []int32, ...), honoring any Go type pinned on the element with Go/GoFor. Nullability is irrelevant to the Go type — a slice is already nilable — so a nullable array stays a slice, not a pointer.

func Char

func Char(n int) DataType

Char is a fixed-length string of n characters.

func Decimal

func Decimal(precision, scale int) DataType

Decimal is an exact numeric with the given precision and scale.

func EnumType

func EnumType(name string) DataType

EnumType declares a column whose type is the named enum (PostgreSQL CREATE TYPE ... AS ENUM). The name must match an Enum declared on the catalog (bare, or "schema.enum" when qualified) — Catalog.Build reports a descriptive error when it doesn't, the same way it validates a dangling References target; the DDL renders the column with that type and the code generator maps it to the generated Go enum type (enum "mood" -> type Mood). Enum types are PostgreSQL-only, so a column of this type is unavailable on other dialects.

func Type

func Type(name string) DataType

Type declares a column type by its kind name. A registered kind (the built-in standard types, or one added with RegisterType) resolves to its engine name per dialect; an unregistered name is used verbatim on every dialect (e.g. Type("citext")). For a custom type the generator's tables don't know, pin its Go type with Go/GoFor (e.g. Type("citext").Go(decl.GoTypeOf[string]())).

func TypeFor

func TypeFor(names map[string]string) DataType

TypeFor declares a column type with a distinct, fixed engine type name per dialect inline, keyed by dialect name ("postgres", "mysql", "sqlite", ...). A dialect absent from names makes the type unavailable there, which the generators report as an error when targeting it. For a reusable type, prefer RegisterType plus Type. Chain Go/GoFor to also pin the Go type.

decl.TypeFor(map[string]string{"postgres": "uuid", "mysql": "char"})

func VarChar

func VarChar(n int) DataType

VarChar is a variable-length string of at most n characters.

func (DataType) Engine

func (t DataType) Engine(dialect string) (meta.Engine, bool)

Engine implements meta.Type.

func (DataType) Go

func (t DataType) Go(gt meta.GoType) DataType

Go pins the Go type the code generator maps this column type to on every dialect, returning a copy of the type. Build the meta.GoType with the GoType or GoTypeOf helpers, e.g. to fix the mapping of a custom DB type at the schema source:

decl.Type("citext").Go(decl.GoTypeOf[string]())
decl.Type("uuid").Go(decl.GoTypeOf[uuid.UUID]())

For a nullable column the generator still applies the pointer; pin the bare type. GoFor pins a different type per dialect.

func (DataType) GoFor

func (t DataType) GoFor(dialect string, gt meta.GoType) DataType

GoFor pins the Go type for a single dialect ("postgres", "mysql", ...); an empty dialect pins the default used when a dialect has no specific pin. Chain calls to pin dialects distinctly. It returns a copy of the type.

func (DataType) GoType

func (t DataType) GoType(dialect string) (meta.GoType, bool)

GoType implements meta.GoTyped: it reports the Go type pinned for the dialect (with Go/GoFor), so codegen's DefaultTypeMapper maps the column to it without a separate override. ok is false when no type is pinned, falling back to the generator's tables.

type ExtensionOption

type ExtensionOption func(*meta.Extension)

ExtensionOption customizes an extension declared with Extension.

func ExtensionSchema

func ExtensionSchema(schema string) ExtensionOption

ExtensionSchema installs the extension's objects into the named schema (CREATE EXTENSION ... WITH SCHEMA name). Like a table declared through Catalog.Schema(name), the schema is assumed to already exist unless it is also put under migration management with Catalog.Schema(name, decl.Create()) — CreateSQL never emits a CREATE SCHEMA for a name it only sees here.

type FKOption

type FKOption func(*fkDef)

FKOption customizes a foreign key declared with References or ForeignKey.

func OnDelete

func OnDelete(action ReferentialAction) FKOption

OnDelete sets the foreign key's ON DELETE referential action.

func OnUpdate

func OnUpdate(action ReferentialAction) FKOption

OnUpdate sets the foreign key's ON UPDATE referential action.

type GrantOption

type GrantOption func(*meta.Grant)

GrantOption customizes a grant declared with Grant.

func GrantOn

func GrantOn(objects ...string) GrantOption

GrantOn sets the objects the privileges apply to, with the default object type TABLE.

func GrantOnType

func GrantOnType(objectType string, objects ...string) GrantOption

GrantOnType sets the object-type keyword (e.g. "SEQUENCE", "SCHEMA") and the objects the privileges apply to.

func GrantTo

func GrantTo(grantees ...string) GrantOption

GrantTo sets the grantee roles. Use "PUBLIC" for the PUBLIC pseudo-role.

func GrantWithGrantOption

func GrantWithGrantOption() GrantOption

GrantWithGrantOption adds WITH GRANT OPTION.

type MaterializedViewOption

type MaterializedViewOption func(*meta.MaterializedView)

MaterializedViewOption customizes a materialized view declared with MaterializedView.

func MaterializedViewColumns

func MaterializedViewColumns(columns ...string) MaterializedViewOption

MaterializedViewColumns names the view's columns explicitly.

func MaterializedViewWithNoData

func MaterializedViewWithNoData() MaterializedViewOption

MaterializedViewWithNoData creates the view unpopulated (WITH NO DATA).

type ReferentialAction

type ReferentialAction = meta.ReferentialAction

ReferentialAction is a foreign-key ON DELETE / ON UPDATE action, re-exported from the meta model so a catalog declares them without importing meta.

type Schema

type Schema struct {
	// contains filtered or unexported fields
}

Schema is a handle for declaring tables in a named schema of a catalog.

func (*Schema) Enum

func (s *Schema) Enum(name string, values ...string)

Enum declares an enum type (PostgreSQL CREATE TYPE ... AS ENUM) in the schema.

func (*Schema) MaterializedView

func (s *Schema) MaterializedView(name string, query sql.Query, opts ...MaterializedViewOption)

MaterializedView declares a materialized view (PostgreSQL) in the schema, defined by query. The query must compile to plain SQL with no bind values; inline literals with sql.Raw.

func (*Schema) Name

func (s *Schema) Name() string

Name returns the schema name ("" for the default schema).

func (*Schema) Sequence

func (s *Schema) Sequence(name string, opts ...SequenceOption)

Sequence declares a sequence (PostgreSQL CREATE SEQUENCE) in the schema. Unset options take the engine default.

func (*Schema) Table

func (s *Schema) Table(name string, elements ...TableElement) meta.Table

Table declares a table in the schema and returns the assembled meta.Table. Foreign keys it declares are resolved later, by Build, against every table in the catalog — so a table may reference one declared after it, or itself.

type SchemaOption

type SchemaOption func(*schemaConfig)

SchemaOption configures a schema declared with Catalog.Schema.

func Create

func Create() SchemaOption

Create marks the schema for migration management, so CreateSQL emits a CREATE SCHEMA IF NOT EXISTS for it (and DropSQL a DROP SCHEMA). Without it a schema is assumed to already exist and is left alone:

Catalog.Schema("audit", decl.Create()).Table("log", ...)

type SequenceOption

type SequenceOption func(*meta.Sequence)

SequenceOption customizes a sequence declared with Sequence.

func SequenceCache

func SequenceCache(n int64) SequenceOption

SequenceCache sets CACHE.

func SequenceCycle

func SequenceCycle(cycle bool) SequenceOption

SequenceCycle sets CYCLE (true) or NO CYCLE (false).

func SequenceIncrement

func SequenceIncrement(n int64) SequenceOption

SequenceIncrement sets INCREMENT BY.

func SequenceMaxValue

func SequenceMaxValue(n int64) SequenceOption

SequenceMaxValue sets MAXVALUE.

func SequenceMinValue

func SequenceMinValue(n int64) SequenceOption

SequenceMinValue sets MINVALUE.

func SequenceOwnedBy

func SequenceOwnedBy(column string) SequenceOption

SequenceOwnedBy sets OWNED BY a "table.column", tying the sequence's lifetime to that column, or "NONE" to detach it.

func SequenceStart

func SequenceStart(n int64) SequenceOption

SequenceStart sets START WITH.

type TableElement

type TableElement interface {
	// contains filtered or unexported methods
}

TableElement is a table declaration item: Column, Index, UniqueIndex, Check, TableComment, and ForeignKey produce them.

func BindingName

func BindingName(name string) TableElement

BindingName overrides the base Go identifier the code generator derives for the table's binding. By default the binding name comes from the table name (table "users" -> var Users, type UsersTable, …); set this to choose another, e.g. when the table name maps to an awkward identifier:

var People = Catalog.Table("tbl_person",
	decl.BindingName("Person"),
	decl.Column("id", decl.BigInt, decl.PrimaryKey(), decl.Default()),
	// ...
)

The name backs every binding identifier (the package var, table type, and—in the relation-aware layout—the columns type, its constructor, the relations type, and the filter bundle). It must be a valid, exported Go identifier and distinct across the generated tables. The override is a codegen hint kept on the catalog, not the schema model; wire it into generation with codegen.Options.BindingName: Catalog.BindingNames().

func Check

func Check(expr sql.Expression) TableElement

Check declares a CHECK constraint. expr is the bare boolean expression, e.g. sql.Raw("price > 0") or sql.Gt(sql.Col("price"), sql.Raw("0")). Constraints are named ck_<table>_<n> in declaration order. The expression must not bind values; wrap literals in sql.Raw to inline them.

func Column

func Column(name string, typ DataType, opts ...ColumnOption) TableElement

Column declares a column. Columns are NOT NULL by default; apply options to mark them Nullable, PrimaryKey, Default(ed), Unique, commented, or as a foreign key (References).

func ForeignKey

func ForeignKey(columns []string, refTable string, refColumns []string, opts ...FKOption) TableElement

ForeignKey declares a (possibly composite) foreign-key constraint: the local columns reference refColumns of refTable. refTable names a table in the catalog ("users", or "schema.users" to cross schemas), resolved at Build, so it may be declared after this one or be this table itself. For the common single-column case the References column option is more convenient.

func Index

func Index(columns ...string) TableElement

Index declares a secondary index over the named columns, named idx_<table>_<columns>.

func TableComment

func TableComment(text string) TableElement

TableComment sets the table's comment. Column comments use the Comment option instead.

func UniqueIndex

func UniqueIndex(columns ...string) TableElement

UniqueIndex declares a unique index (or UNIQUE constraint) over the named columns, named uq_<table>_<columns>. For a single column the Unique column option is more convenient.

Jump to

Keyboard shortcuts

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