momo

package module
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Nov 6, 2026 License: Apache-2.0 Imports: 17 Imported by: 0

README

Momo

Momo is a Go ORM that generates code from plain Go structs. You describe a table as a struct with a few tags, momo generate writes typed column and relation descriptors, and queries are built from those descriptors, so a typo in a column name or a mismatched value type fails at compile time. SQL stays explicit and inspectable. pgvector support is built in: vector columns, distance expressions, and search settings are part of the core API. Schema migrations are built in as well: momo migrate diff compares your structs with the last snapshot and writes the SQL.

Status: v0.1.0, pre-v1, with no compatibility promise between releases. This release supports PostgreSQL with pgvector. SQLite and MySQL are planned. Momo needs Go 1.26 or newer (see go.mod).

Install

Add the library to your module and install the CLI:

go get github.com/azrtydxb/momo@v0.1.0
go install github.com/azrtydxb/momo/cmd/momo@v0.1.0

Momo expects PostgreSQL with the pgvector extension. The first migration runs CREATE EXTENSION IF NOT EXISTS "vector" for you, which needs a role that may create extensions.

pgvector must be installed on the server itself, not only in your database. Use an image that ships it, such as pgvector/pgvector, or your distribution's pgvector package. If the migration role cannot create extensions, have a superuser run CREATE EXTENSION vector once in the target database before you apply.

Worked example

Every Go snippet below is compiled: the full source lives in examples/quickstart, and a test fails when this README and that code drift apart. Copy the example directory as a starting point, and rewrite its import paths from github.com/azrtydxb/momo/examples/quickstart to your own module path.

These are the imports of the example. The generated packages are imported as <your module>/models/author, <your module>/models/post, and so on, one package per model next to the models package.

import (
	"context"
	"errors"
	"fmt"
	"log"
	"os"

	"github.com/azrtydxb/momo"
	"github.com/azrtydxb/momo/driver/pgxdriver"
	"github.com/azrtydxb/momo/examples/quickstart/models"
	"github.com/azrtydxb/momo/examples/quickstart/models/author"
	"github.com/azrtydxb/momo/examples/quickstart/models/post"
	"github.com/azrtydxb/momo/vector"
)
1. Define the models

A model is a struct that embeds momo.Model. The tag on that field names the table, and each field carries its column rules. A field of type vector.Vector is a pgvector column, and dim sets its dimension. Use the output size of your embedding model for dim, such as 1536.

Tags used here: table:<name> names the table, pk marks the primary key, identity lets the database assign it, column:<name> overrides the column name, size:<n> limits a string, notnull and unique add constraints, index adds an index, default:<sql> sets the column default, read_only means Momo never writes the column, has_many and belongs_to declare a relation, and fk:<Field> names the foreign-key field. How Go types map to column types is in docs/TYPE_MAPPING.md.

// Author writes posts.
type Author struct {
	momo.Model `momo:"table:authors"`
	ID         int64  `momo:"pk,identity"`
	Name       string `momo:"size:200,notnull,unique"`

	Posts []Post `momo:"has_many,fk:AuthorID"`
}

// Post is one article with a 3-dimensional embedding. Real embeddings are
// larger: change dim to the output size of your model, for example 1536.
type Post struct {
	momo.Model `momo:"table:posts"`
	ID         int64         `momo:"pk,identity"`
	AuthorID   int64         `momo:"column:author_id,notnull,index"`
	Title      string        `momo:"size:300,notnull"`
	Embedding  vector.Vector `momo:"dim:3"`
	CreatedAt  time.Time     `momo:"column:created_at,read_only,default:now()"`

	Author *Author `momo:"belongs_to,fk:AuthorID"`
}

Tags cannot express everything. pgvector's hnsw index method has no default operator class, so a MomoConfig method names it. The generator reads this method statically and never runs it.

func (p *Post) MomoConfig(c *momo.Config) {
	c.Index("posts_embedding_hnsw", &p.Embedding).
		Method("hnsw").
		Opclass(&p.Embedding, "vector_cosine_ops")
}
2. Configure and generate

Put a momo.yaml at the module root. version is the config format version (1). packages lists the model packages. naming picks the convention for table and column names you do not set with a tag. dialects lists the databases to generate for. migrate.dir is where migrations are written, and migrate.url_env names the environment variable that holds the database URL.

version: 1
packages:
  - ./models
naming: gorm
dialects: [postgres]
migrate:
  dir:
    postgres: migrations
  url_env:
    postgres: DATABASE_URL

Generate the typed descriptors:

momo generate

This writes a momo_gen.go next to the models and one package per model (models/author, models/post) holding its columns, such as post.Title and post.AuthorID, and its relations, such as author.Posts and post.Author. Commit the generated files. momo generate --check exits 1 when they are stale, which makes it a good CI step. You can also add //go:generate momo generate to a source file.

On Windows, git may convert generated files to CRLF line endings, which makes --check report them as stale. Add this line to .gitattributes:

*_gen.go text eol=lf
3. Create and apply the migration

momo migrate diff compares the models with the latest snapshot and writes the migration offline, without a database. The first run against an empty directory writes the initial schema. Review the generated SQL before you apply it.

momo migrate diff init

Apply it with the URL in the environment variable that momo.yaml names:

export DATABASE_URL='postgres://user:password@localhost:5432/app?sslmode=disable'
momo migrate apply

sslmode=disable is for local development only. Use sslmode=verify-full or another TLS mode against any database you do not reach over a trusted local connection.

Momo v0.1.0 writes forward migrations only. There is no rollback command, so undo a change with a new migration.

Run momo migrate help for the other commands: plan, status, validate, hash, drift, baseline, and resolve. Run migrations over a direct connection to PostgreSQL, not through a transaction-mode pooler: the migration lock is a session advisory lock.

Changing a model

Edit the struct, then run these in order:

momo generate
momo migrate diff add_subtitle
momo migrate apply

diff compares the structs with the latest snapshot. A renamed field or struct looks like a drop plus an add, and diff refuses that without --allow-destructive because it would lose data. Say it is a rename instead with --rename-column table.old=new or --rename-table old=new. Review the SQL before you apply it.

4. Open a database

pgxdriver.NewPool opens a pgx pool and pgxdriver.New wraps it as a Momo driver. The caller owns the pool and closes it.

func openDB(ctx context.Context, url string) (*momo.DB, func(), error) {
	pool, err := pgxdriver.NewPool(ctx, url)
	if err != nil {
		return nil, nil, err
	}
	return momo.New(pgxdriver.New(pool)), pool.Close, nil
}

PgBouncer: the driver's default exec mode sends unnamed statements and binds values on the server, so it is safe behind PgBouncer in transaction pooling mode without further settings. pgxdriver.WithExecMode selects a different pgx mode, but not the simple protocol, which interpolates arguments on the client and is rejected.

5. Insert, select, update, delete

Queries take a momo.Querier, which both *momo.DB and *momo.Tx satisfy. Columns come from the generated packages. Returning reads database-assigned values, such as the identity column, back into the struct.

func createAuthor(ctx context.Context, q momo.Querier, name string) (models.Author, error) {
	a := models.Author{Name: name}
	rows, err := momo.Insert[models.Author](q).Model(&a).Returning(author.ID).Rows(ctx)
	if err != nil {
		return a, err
	}
	defer func() { _ = rows.Close() }()
	for rows.Next() {
		if err := rows.ScanModel(&a); err != nil {
			return a, err
		}
	}
	return a, rows.Err()
}

Models inserts a slice as one multi-row statement:

func createPosts(ctx context.Context, q momo.Querier, authorID int64) error {
	_, err := momo.Insert[models.Post](q).Models([]models.Post{
		{AuthorID: authorID, Title: "Notes on the engine", Embedding: vector.Vector{1, 0, 0}},
		{AuthorID: authorID, Title: "Poetical science", Embedding: vector.Vector{0, 1, 0}},
		{AuthorID: authorID, Title: "Bernoulli numbers", Embedding: vector.Vector{0.9, 0.1, 0}},
	}).Exec(ctx)
	return err
}

Select, update, and delete take typed predicates. An update or delete without a Where is refused unless you call AllRows(), so a forgotten filter cannot touch every row.

func readAndChange(ctx context.Context, q momo.Querier, authorID int64) error {
	posts, err := momo.Select[models.Post](q).
		Where(post.AuthorID.Eq(authorID)).
		OrderBy(post.Title.Asc()).
		All(ctx)
	if err != nil {
		return err
	}
	fmt.Println("posts:", len(posts))

	first, err := momo.Select[models.Post](q).Where(post.Title.Eq("Poetical science")).One(ctx)
	if err != nil {
		return err
	}

	if _, err := momo.Update[models.Post](q).
		Set(post.Title.Set("Poetical science, revised")).
		Where(post.ID.Eq(first.ID)).
		Exec(ctx); err != nil {
		return err
	}
	_, err = momo.Delete[models.Post](q).Where(post.ID.Eq(first.ID)).Exec(ctx)
	return err
}

One returns an error that matches momo.ErrNotFound when no row matches.

Conditional upsert

OnConflict(...).DoUpdate(...) takes a condition with Where, so one statement can take a lease: insert the row, or update it only when the current lease expired or is already yours. RowsAffected is 1 when you hold the lease and 0 when someone else does. In the condition, columns mean the existing row and Excluded() means the proposed one.

The full example, with the model and the partial-index form, is in docs/ai-guide.md under upserts.

To conflict on a partial unique index, give the index predicate with OnConflict(cols...).Where(...) before DoNothing or DoUpdate. Both forms work on PostgreSQL; MySQL returns ErrUnsupported. On SQLite the target predicate must match the index text, so write it as raw SQL, for example momo.SQL("live = 1").

6. Load relations

Relations are loaded eagerly after the parents are fetched, with one query per relation instead of one per parent. momo.Load loads one relation, and momo.LoadAll loads several for the same parents. Has-many (author.Posts) and belongs-to (post.Author) work the same way.

To load relations as part of a query, add them with Preload, for example Preload(author.Posts) on an author query. All, One and First then load the relations after the rows are read, with the same one query per relation, and a result with no rows runs no extra query. Preload returns a copy, so a base query can be reused. Each and Iter stream rows and return an error matching momo.ErrUnsupported when the query has preloads.

Scoped and nested loads, top-N per parent, single-query Joined() loading, relation predicates such as Has, the Link and Replace helpers, graph writes and the error behavior of each are covered in docs/relations.md.

func loadRelations(ctx context.Context, q momo.Querier, authorID int64) error {
	authors, err := momo.Select[models.Author](q).Where(author.ID.Eq(authorID)).All(ctx)
	if err != nil {
		return err
	}
	if err := momo.Load(ctx, q, authors, author.Posts); err != nil {
		return err
	}
	if len(authors) == 0 {
		return fmt.Errorf("author %d not found", authorID)
	}
	posts := authors[0].Posts
	if err := momo.Load(ctx, q, posts, post.Author); err != nil {
		return err
	}
	for _, p := range posts {
		fmt.Println(p.Title, "by", p.Author.Name)
	}
	return nil
}

Distance expressions take a vector column and a query vector, and the result orders a select. The generator emits a momo.VectorColumn for every field of a vector type, such as post.Embedding. It records the dimension from the dim tag, so a wrong-sized query vector is rejected when the query is built.

WithSearch runs a function in a transaction with pgvector settings applied, here hnsw.ef_search. The settings end with the transaction. SearchSettings also covers iterative scans for filtered searches, IVFFlat probes, and ExactSearch.

func similarPosts(ctx context.Context, db *momo.DB, query vector.Vector) error {
	settings := momo.SearchSettings{HNSWEfSearch: 100}
	return db.WithSearch(ctx, settings, func(tx *momo.Tx) error {
		nearest, err := momo.Select[models.Post](tx).
			OrderBy(momo.CosineDistance(post.Embedding, query).Asc()).
			Limit(5).
			All(ctx)
		if err != nil {
			return err
		}
		for _, p := range nearest {
			fmt.Println("near:", p.Title)
		}
		return nil
	})
}

For dense vectors the functions are L2Distance, InnerProductDistance, CosineDistance, and L1Distance. For bit vectors they are HammingDistance and JaccardDistance. Match the function to the operator class of your index: this example indexes with vector_cosine_ops, so it searches with CosineDistance.

8. Transactions

db.Tx begins a transaction, commits when the function returns nil, and rolls back when it returns an error or panics. Pass momo.TxOptions to set the isolation level or read-only mode.

func renameInTx(ctx context.Context, db *momo.DB, id int64, name string) error {
	return db.Tx(ctx, momo.TxOptions{}, func(tx *momo.Tx) error {
		if _, err := momo.Update[models.Author](tx).
			Set(author.Name.Set(name)).
			Where(author.ID.Eq(id)).
			Exec(ctx); err != nil {
			return err
		}
		n, err := momo.Select[models.Author](tx).Where(author.Name.Eq(name)).Count(ctx)
		if err != nil {
			return err
		}
		if n != 1 {
			return errors.New("rename did not take effect") // rolls the transaction back
		}
		return nil
	})
}

To run the whole example against a database with the migrations applied:

DATABASE_URL='postgres://user:password@localhost:5432/app?sslmode=disable' go run ./examples/quickstart
Projections

A projection reads a subset of columns or an aggregate into a struct that is not a table. Embed momo.Projection and select it with From and Columns; see examples/projection-pagination and the Hit type in examples/vector-search. In v0.1.0 the generator does not emit projections, so you write the MomoMeta by hand. A projection has no identity, version, soft-delete or timestamp column, so set Identity, Version, SoftDelete, CreatedAt and UpdatedAt to -1 in its Meta. The Card type in the projection example shows the whole thing.

A projection does not get the automatic soft-delete filter (its SoftDelete is -1). When the table has a soft-delete column, add the filter yourself, for example .Where(article.DeletedAt.IsNull()).

Generating this is tracked in the backlog story Generate Meta for projection structs.

Command line

momo help lists the commands. The ones used above:

  • momo generate [-c momo.yaml] [--check] [patterns...] writes the generated code, or with --check lists stale files and exits 1.
  • momo migrate diff <name> writes a migration for the difference between the models and the latest snapshot. It refuses destructive changes unless you pass --allow-destructive.
  • momo migrate apply [--to N] [--lock-wait 30s] applies pending migrations.
  • momo migrate validate checks the migration directory offline.

Exit codes are 0 for success, 1 for findings such as stale generated files, 2 for usage or configuration errors, and 3 for database errors.

More

Documentation

Overview

Example (VectorMigration)

A migration creates the extension, the table and the ANN index from the schema description.

package main

import (
	"fmt"

	"github.com/azrtydxb/momo/dialect"
	"github.com/azrtydxb/momo/migrate"

	migratepg "github.com/azrtydxb/momo/migrate/postgres"
	"github.com/azrtydxb/momo/schema"
)

func main() {
	to := &schema.Schema{
		Dialect:    dialect.Postgres,
		Extensions: []schema.Extension{{Name: "vector"}},
		Tables: []schema.Table{{
			Name: "documents",
			Columns: []schema.Column{
				{Name: "id", Type: "bigint"},
				{Name: "embedding", Type: "vector(3)"},
			},
			PrimaryKey: []string{"id"},
			Indexes: []schema.Index{{
				Name: "documents_embedding_hnsw", Method: "hnsw",
				Parts: []schema.IndexPart{{Column: "embedding", OpClass: "vector_cosine_ops"}},
				With:  []schema.KV{{Key: "m", Value: "16"}, {Key: "ef_construction", Value: "64"}},
			}},
		}},
	}
	changes, _, err := migrate.Diff(nil, to, migrate.DiffOptions{})
	if err != nil {
		fmt.Println(err)
		return
	}
	stmts, err := migratepg.New().Render(migrate.Order(changes))
	if err != nil {
		fmt.Println(err)
		return
	}
	for _, s := range stmts {
		fmt.Println(s.SQL)
	}
}
Output:
CREATE EXTENSION IF NOT EXISTS "vector"
CREATE TABLE "documents" ("id" bigint NOT NULL, "embedding" vector(3) NOT NULL, CONSTRAINT "documents_pkey" PRIMARY KEY ("id"))
CREATE INDEX "documents_embedding_hnsw" ON "documents" USING "hnsw" ("embedding" "vector_cosine_ops") WITH ("m" = 16, "ef_construction" = 64)

Index

Examples

Constants

View Source
const (
	FlagPK uint16 = 1 << iota
	FlagUnique
	FlagNullable
	FlagIdentity // generated identity; omitted from inserts
	FlagReadOnly // DB-generated or computed; never written
	FlagVersion  // optimistic-lock version column
	FlagSoftDelete
	FlagHasDefault
	FlagCreateOnly
	FlagImmutable
	FlagSensitive
	FlagCreatedAt
	FlagUpdatedAt
)

Column flags carried by generated column descriptors and ColumnMeta.

Variables

View Source
var (
	// ErrNotFound: a single-row operation (One, First, Model update/delete)
	// matched no row. Multi-row reads (All, Each, Iter) never return it.
	ErrNotFound = errors.New("momo: not found")
	// ErrTooManyRows: One matched more than one row.
	ErrTooManyRows = errors.New("momo: more than one row")

	ErrUniqueViolation     = errors.New("momo: unique constraint violation")
	ErrForeignKeyViolation = errors.New("momo: foreign key constraint violation")
	ErrCheckViolation      = errors.New("momo: check constraint violation")
	ErrNotNullViolation    = errors.New("momo: not-null constraint violation")
	// ErrSerialization: the database aborted the transaction because of a
	// serialization failure or deadlock. Retrying the whole transaction
	// may succeed; Momo never retries on its own.
	ErrSerialization = errors.New("momo: serialization failure")

	// ErrOptimisticLock: an update or delete guarded by an expected
	// version matched no row because the version changed (or the row is
	// gone).
	ErrOptimisticLock = errors.New("momo: optimistic lock conflict")
	// ErrUnsupported: the dialect or driver cannot express the request.
	ErrUnsupported = errors.New("momo: unsupported by dialect or driver")
	// ErrUnfilteredMutation: an UPDATE or DELETE without a WHERE clause was
	// attempted without calling AllRows.
	ErrUnfilteredMutation = errors.New("momo: refusing UPDATE/DELETE without WHERE; call AllRows to confirm")
	// ErrTxDone: the transaction was already committed or rolled back.
	ErrTxDone = errors.New("momo: transaction already finished")

	// ErrValidation: a value failed validation before reaching the database.
	ErrValidation = errors.New("momo: validation failed")
	// ErrBusy: the database is busy or locked and could not serve the request.
	ErrBusy = errors.New("momo: database is busy")
	// ErrPinnedBusy: PinnedConn.Release was called while a statement was
	// still executing on the connection from another goroutine. Nothing was
	// released; call Release again once the statement has finished.
	ErrPinnedBusy = errors.New("momo: pinned connection is busy")
	// ErrLimitExceeded: a configured limit (rows, size, parameters) was exceeded.
	ErrLimitExceeded = errors.New("momo: limit exceeded")
	// ErrInvalidVector: a vector value failed validation.
	ErrInvalidVector = vector.ErrInvalidVector
)

Sentinel error kinds. Errors returned by Momo wrap one of these (match with errors.Is) and, when a database error caused them, also wrap the original driver error (reach it with errors.As, e.g. *pgconn.PgError).

Functions

func CreateGraph

func CreateGraph[P any, PP Modeler[P]](ctx context.Context, q Querier, p *P, includes ...Association[P]) error

CreateGraph inserts p and the children of the listed relations in one transaction. Listed BelongsTo children are written before p and their key is copied into p; the other listed children are written after p, with p's key (read back with RETURNING when it is generated) copied into each child and, for a polymorphic relation, the discriminator set. ManyToMany children are inserted and then linked through the join table. Relations are processed in the order listed. Unlisted relations are not written, and the children's own relations are not followed.

On a *DB the graph runs in a new transaction; on a *Tx it runs in a savepoint, so a failure undoes the whole graph and leaves the caller's transaction usable (a dialect without savepoints returns ErrUnsupported). A model that appears as its own child fails with ErrValidation.

CreateGraph needs a transaction, so a *DB pinned with OnConn fails with ErrUnsupported (Op "begin"); run it on the pool or inside a *Tx. After a failure the database is unchanged, but p and the children keep the generated keys and foreign keys already written into them; do not reuse them as if they were unsaved.

Example

CreateGraph inserts a parent and the children of the listed relations in one transaction.

package main

import (
	"context"
	"fmt"

	"github.com/azrtydxb/momo"
	"github.com/azrtydxb/momo/driver/pgxdriver"
	"github.com/azrtydxb/momo/internal/relfixture/document"
	"github.com/azrtydxb/momo/internal/relfixture/models"
)

func main() {
	var (
		ctx = context.Background()
		db  = momo.New(pgxdriver.New(nil))
		doc = models.Document{
			Title:    "Hello",
			Comments: []models.Comment{{Body: "first"}},
		}
	)
	err := momo.CreateGraph(ctx, db, &doc, document.Comments.Include())
	fmt.Println(err)
}

func EscapeLike

func EscapeLike(s string) string

EscapeLike escapes %, _ and the escape character ! (each is prefixed with !) so the result matches literally inside a LIKE pattern rendered with ESCAPE '!'. The escape character is deliberately not a backslash: the meaning of a backslash in a string literal depends on server settings (MySQL NO_BACKSLASH_ESCAPES, PostgreSQL standard_conforming_strings), that of ! does not.

func Exec

func Exec(ctx context.Context, q Querier, sql string, args ...any) (int64, error)

Exec runs a raw statement. See SQL for the placeholder and safety contract: '?' placeholders, "??" for a literal '?'.

func FindOrCreate

func FindOrCreate[T any, PT Modeler[T]](ctx context.Context, q Querier, key []ColumnRef, m *T) (created bool, err error)

FindOrCreate inserts m unless a row with the same key columns exists; then m holds the stored row. The statement is INSERT ... ON CONFLICT (key) DO NOTHING, so the unique constraint on key (which must exist) decides the winner and concurrent callers never create duplicates: the losers re-select the winner's row. created reports whether this call inserted. When the row exists, all of m's mapped columns are overwritten by the stored values; its relation fields are reset (the row is loaded as a plain model, soft-deleted rows included, since they still hold the key).

Under READ COMMITTED the conflicting insert waits for a concurrent transaction and the re-select then sees its row. Under REPEATABLE READ or SERIALIZABLE the re-select can miss a row committed after the snapshot and returns ErrNotFound. Dialects without ON CONFLICT return ErrUnsupported.

Example

FindOrCreate needs a unique key; UpsertByPK inserts or updates by primary key.

package main

import (
	"context"
	"fmt"

	"github.com/azrtydxb/momo"
	"github.com/azrtydxb/momo/driver/pgxdriver"
	"github.com/azrtydxb/momo/internal/relfixture/author"
	"github.com/azrtydxb/momo/internal/relfixture/models"
)

func main() {
	var (
		ctx = context.Background()
		db  = momo.New(pgxdriver.New(nil))
		a   = models.Author{ID: 7, Name: "Ada"}
	)
	created, err := momo.FindOrCreate(ctx, db, []momo.ColumnRef{author.ID}, &a)
	fmt.Println(created, err)
	fmt.Println(momo.UpsertByPK(ctx, db, &a))
}

func Ident

func Ident(name string) any

Ident renders name as a quoted identifier in the dialect's style. It is never bound as a value.

func In

func In(values any) any

In expands a slice argument into a comma-separated list of bind markers, one per element, for a raw placeholder written inside the parentheses of IN (?). An empty slice, or a value that is not a slice, is a build error, as is an element that is an Ident, In, Named or Expr argument. A []byte is a slice like any other: In([]byte) expands to one bind per byte.

func Load

func Load[P, C any](ctx context.Context, q Querier, parents []P, rel Relation[P, C], opts ...LoadOption[C]) error

Load loads rel for parents and assigns the result onto each parent. Parent order and length are unchanged. Parents with a NULL key get an empty association (nil pointer or nil slice).

Children of a model with a soft-delete column are excluded by default, as in Select; LoadWithDeleted and LoadOnlyDeleted change that.

Associations are cleared up front, so a load that fails part-way leaves the parents with empty associations rather than stale or partial ones. Children are copied per parent: a to-one child shared by several parents is copied shallowly for each, so pointers inside it (including relations loaded with Then) are shared among those copies.

Example

Load fills an association for a slice of parents with one statement per bind-limit chunk. Options scope the child statement.

package main

import (
	"context"
	"fmt"

	"github.com/azrtydxb/momo"
	"github.com/azrtydxb/momo/driver/pgxdriver"
	"github.com/azrtydxb/momo/internal/relfixture/comment"
	"github.com/azrtydxb/momo/internal/relfixture/document"
	"github.com/azrtydxb/momo/internal/relfixture/models"
)

func main() {
	var (
		ctx  = context.Background()
		db   = momo.New(pgxdriver.New(nil))
		docs []models.Document
	)
	err := momo.Load(ctx, db, docs, document.Comments,
		momo.LoadWhere[models.Comment](comment.Approved.Eq(true)),
		momo.LoadOrderBy[models.Comment](comment.ID.Desc()),
		momo.LoadPerParentLimit[models.Comment](3),
	)
	fmt.Println(err)
}

func LoadAll

func LoadAll[P any](ctx context.Context, q Querier, parents []P, loaders ...Loader[P]) error

LoadAll loads several relations for the same parents, in order. The top level of each relation is one statement per bind-limit chunk; nested loads run once over all children of the level above.

On a connection whose driver implements Batcher (pgx), the statements of all relations of a level, and every bind-limit chunk, are pipelined in one round trip; other drivers load sequentially. Results are the same either way; hook event order differs (see pipeline.go). A pipelined level buffers all of its child rows in memory before assigning them (roughly twice the peak of sequential loading). There is no per-call opt-out: a driver or connection without Batcher loads sequentially.

Example

LoadAll loads several relations together.

package main

import (
	"context"
	"fmt"

	"github.com/azrtydxb/momo"
	"github.com/azrtydxb/momo/driver/pgxdriver"
	"github.com/azrtydxb/momo/internal/relfixture/document"
	"github.com/azrtydxb/momo/internal/relfixture/models"
)

func main() {
	var (
		ctx  = context.Background()
		db   = momo.New(pgxdriver.New(nil))
		docs []models.Document
	)
	err := momo.LoadAll(ctx, db, docs,
		document.Author,
		document.Tags,
		document.Comments,
	)
	fmt.Println(err)
}

func RecordPoolWait

func RecordPoolWait(ctx context.Context, d time.Duration)

RecordPoolWait adds d to the pool wait of the statement whose context is ctx. Drivers call it when a connection acquisition waited (pgxdriver does so from its pgxpool AcquireTracer). It is a no-op when ctx does not belong to a hooked statement, so it costs nothing when no hook is registered.

func Scalars

func Scalars[V any](ctx context.Context, q Querier, sql string, args ...any) ([]V, error)

Scalars renders sql through the raw path (see Exec: '?' binds, "??" is a literal '?', Expr arguments render inline, an argument-count mismatch is a build error) and returns the first column of every row as V. An empty result returns a nil slice and a nil error.

func UpsertByPK

func UpsertByPK[T any, PT Modeler[T]](ctx context.Context, q Querier, m *T) error

UpsertByPK inserts m, or updates the row with the same primary key: INSERT ... ON CONFLICT (pk) DO UPDATE SET col = EXCLUDED.col for every column an update may change (the primary key, version, soft-delete and created-at columns are never overwritten; see Upsert for the exact rules and for an upsert on another conflict target). Generated columns are read back into m. It is the one-call form of Upsert(q).Model(m).

Types

type Agg

type Agg[T any] struct {
	// contains filtered or unexported fields
}

Agg is a typed aggregate expression such as COUNT(*) or SUM(col). Besides being an Expression[T] usable in a select list, it carries the comparison methods HAVING needs.

func Avg

func Avg(e Expression[float64]) Agg[float64]

Avg is AVG(expr) as float64. It is NULL over zero rows: scan it into *float64 (or sql.NullFloat64), or wrap it in Coalesce.

func CountAll

func CountAll() Agg[int64]

CountAll is COUNT(*).

func CountDistinct

func CountDistinct(e Expr) Agg[int64]

CountDistinct is COUNT(DISTINCT expr).

func CountOf

func CountOf(e Expr) Agg[int64]

CountOf is COUNT(expr), which skips NULLs.

func Max

func Max[T any](e Expression[T]) Agg[T]

Max is MAX(expr) (NULL over zero rows).

func Min

func Min[T any](e Expression[T]) Agg[T]

Min is MIN(expr) (NULL over zero rows). T is any type the database can order, such as time.Time, a decimal, or a string.

func Sum

func Sum[T Number](e Expression[T]) Agg[T]

Sum is SUM(expr). The result is NULL over zero rows, so scan it into a nullable destination such as *int64 or *float64 (sql.Null types work too), or wrap it in Coalesce. Scanning NULL into a plain int64 returns an error.

func (Agg) AppendSQL

func (f Agg) AppendSQL(b *Builder)

func (Agg[T]) Asc

func (a Agg[T]) Asc() Order

Asc orders by the aggregate ascending.

func (Agg[T]) Desc

func (a Agg[T]) Desc() Order

Desc orders by the aggregate descending.

func (Agg[T]) Eq

func (a Agg[T]) Eq(v T) Predicate

func (Agg[T]) Ge

func (a Agg[T]) Ge(v T) Predicate

func (Agg[T]) Gt

func (a Agg[T]) Gt(v T) Predicate

func (Agg[T]) Le

func (a Agg[T]) Le(v T) Predicate

func (Agg[T]) Lt

func (a Agg[T]) Lt(v T) Predicate

func (Agg[T]) Ne

func (a Agg[T]) Ne(v T) Predicate

type Aliased

type Aliased struct {
	Expr  Expr
	Alias string
}

Aliased renders "expr AS alias" in a select list.

func As

func As(e Expr, alias string) Aliased

As aliases an expression in a select list.

func (Aliased) AppendSQL

func (a Aliased) AppendSQL(b *Builder)

type Allowlist

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

Allowlist maps external names to typed column descriptors. Unknown names are an error: a name that arrives from outside (a sort key, a filter field) is only ever turned into SQL through the descriptor it resolves to, whose identifier is quoted by the builder, never by splicing the string.

func NewAllowlist

func NewAllowlist(cols ...ColumnRef) Allowlist

NewAllowlist builds an allowlist keyed by ColumnName(). A later column with the same name replaces an earlier one. nil entries are skipped.

func (Allowlist) Column

func (a Allowlist) Column(name string) (ColumnRef, error)

Column returns the descriptor for name, or a *Error with Kind ErrValidation. The error does not echo name, which may be attacker controlled.

type Assignment

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

Assignment is one explicit column write. Constructing one is the only way to include a column in a typed INSERT or UPDATE, which is how Momo keeps omitted, zero, NULL and DEFAULT distinct.

func (Assignment) ColumnName

func (a Assignment) ColumnName() string

ColumnName returns the assigned column.

type Association

type Association[P any] interface {
	// contains filtered or unexported methods
}

Association is one relation listed for CreateGraph. It is produced only by Relation.Include, so unlisted relations are never written.

type Batcher

type Batcher interface {
	SendBatch(ctx context.Context, stmts []Compiled) ([]Rows, error)
}

Batcher is implemented by drivers that send several statements in one round trip. Every statement is executed with Query; a statement without result rows yields a Rows whose Next returns false.

func BatcherOf

func BatcherOf(q Querier) (Batcher, bool)

BatcherOf returns the Batcher behind q: the driver for a *DB, the transaction for a *Tx. For a *DB made with OnConn it is the pinned connection, so the batch runs on the pinned session.

type Builder

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

Builder accumulates SQL text and bind arguments for one statement.

Identifiers go through Ident (always quoted); values go through Arg (always bound as parameters). Raw text is only written by Momo itself or by the explicit raw-SQL escape hatches (SQL, Raw), whose safety contract is documented on those functions.

func NewBuilder

func NewBuilder(d *dialect.Dialect) *Builder

NewBuilder returns a builder for the dialect.

func (*Builder) Arg

func (b *Builder) Arg(v any)

Arg appends a bind placeholder and records v as its argument.

func (*Builder) Args

func (b *Builder) Args() []any

Args returns the bind arguments built so far.

func (*Builder) Dialect

func (b *Builder) Dialect() *dialect.Dialect

Dialect returns the dialect being rendered.

func (*Builder) Err

func (b *Builder) Err() error

Err returns the first build error.

func (*Builder) Ident

func (b *Builder) Ident(name string)

Ident appends a quoted identifier.

func (*Builder) QualifiedIdent

func (b *Builder) QualifiedIdent(qualifier, name string)

QualifiedIdent appends qualifier.name, both quoted. An empty qualifier (or an unqualified context) renders the bare quoted name.

func (*Builder) SetErr

func (b *Builder) SetErr(err error)

SetErr records the first build error; rendering continues but the statement will not be executed.

func (*Builder) String

func (b *Builder) String() string

String returns the SQL text built so far.

func (*Builder) WriteByte

func (b *Builder) WriteByte(c byte) error

WriteByte appends a trusted SQL byte. It always returns nil; the error result exists only so Builder satisfies io.ByteWriter.

func (*Builder) WriteString

func (b *Builder) WriteString(s string)

WriteString appends trusted SQL text.

type Capabilities

type Capabilities struct {
	Vectors bool // sqlite-vec or pgvector functions are available
	Batch   bool
	Copy    bool
}

Capabilities describes what a driver can execute beyond the dialect.

type CapabilityReporter

type CapabilityReporter interface {
	Capabilities() Capabilities
}

CapabilityReporter is implemented by drivers that report driver-level capabilities. Vector builders check Vectors and return ErrUnsupported when it is false.

type Column

type Column[T any] struct {
	// contains filtered or unexported fields
}

Column is a typed column descriptor generated for each model field. T is the column's Go value type with nullability removed (a *string field has a Column[string]-compatible NullColumn[string]).

Column values are small, immutable and safe to share between goroutines.

func ColumnOf

func ColumnOf[T any](source string, name string) Column[T]

ColumnOf declares a typed column of an ad-hoc source (CTE, subquery alias). Momo cannot check that the column exists or has type T.

func NewColumn

func NewColumn[T any](table, name string, flags uint16) Column[T]

NewColumn is used by generated code.

func (Column[T]) AppendSQL

func (c Column[T]) AppendSQL(b *Builder)

func (Column[T]) As

func (c Column[T]) As(alias string) Column[T]

As returns the same column qualified by a table alias.

func (Column[T]) AsExpr

func (c Column[T]) AsExpr() Expression[T]

AsExpr returns the column as an Expression[T], so a projection list reads explicitly: DocTitle.As("l").AsExpr().

func (Column[T]) Asc

func (c Column[T]) Asc() Order

func (Column[T]) Between

func (c Column[T]) Between(lo, hi T) Predicate

Between is inclusive on both ends.

func (Column[T]) ColumnName

func (c Column[T]) ColumnName() string

ColumnName returns the unquoted column name.

func (Column[T]) Desc

func (c Column[T]) Desc() Order

func (Column[T]) Eq

func (c Column[T]) Eq(v T) Predicate

func (Column[T]) EqExpr

func (c Column[T]) EqExpr(e Expression[T]) Predicate

func (Column[T]) Excluded added in v0.2.0

func (c Column[T]) Excluded() Expression[T]

Excluded is the value proposed for insertion in an upsert, rendered as EXCLUDED.<col>. Use it on the right-hand side of a comparison in the DO UPDATE WHERE or ON CONFLICT predicates, for example until.LtExpr(until.Excluded()). It is only meaningful inside an ON CONFLICT clause; the database rejects it elsewhere.

func (Column[T]) Flags

func (c Column[T]) Flags() uint16

Flags returns the generated column flags.

func (Column[T]) Ge

func (c Column[T]) Ge(v T) Predicate

func (Column[T]) GeExpr

func (c Column[T]) GeExpr(e Expression[T]) Predicate

func (Column[T]) Gt

func (c Column[T]) Gt(v T) Predicate

func (Column[T]) GtExpr

func (c Column[T]) GtExpr(e Expression[T]) Predicate

func (Column[T]) In

func (c Column[T]) In(vs ...T) Predicate

In matches any of vs. An empty list renders a predicate that is always false (1 = 0) rather than invalid SQL.

func (Column[T]) InQuery

func (c Column[T]) InQuery(q Subquery) Predicate

InQuery matches the single-column result of a subquery.

func (Column[T]) IsDistinctFrom

func (c Column[T]) IsDistinctFrom(v T) Predicate

IsDistinctFrom is a NULL-safe inequality.

func (Column[T]) IsNotDistinctFrom

func (c Column[T]) IsNotDistinctFrom(v T) Predicate

IsNotDistinctFrom is a NULL-safe equality.

func (Column[T]) IsNotNull

func (c Column[T]) IsNotNull() Predicate

IsNotNull is meaningful on nullable columns and on columns from the outer side of a join.

func (Column[T]) IsNull

func (c Column[T]) IsNull() Predicate

func (Column[T]) Le

func (c Column[T]) Le(v T) Predicate

func (Column[T]) LeExpr

func (c Column[T]) LeExpr(e Expression[T]) Predicate

func (Column[T]) Lt

func (c Column[T]) Lt(v T) Predicate

func (Column[T]) LtExpr

func (c Column[T]) LtExpr(e Expression[T]) Predicate

func (Column[T]) Ne

func (c Column[T]) Ne(v T) Predicate

func (Column[T]) NeExpr

func (c Column[T]) NeExpr(e Expression[T]) Predicate

func (Column[T]) NotIn

func (c Column[T]) NotIn(vs ...T) Predicate

NotIn is the negation of In; an empty list is always true.

func (Column[T]) NotInQuery

func (c Column[T]) NotInQuery(q Subquery) Predicate

NotInQuery is the negation of InQuery. Beware SQL's NOT IN semantics when the subquery can yield NULL.

func (Column[T]) Set

func (c Column[T]) Set(v T) Assignment

Set assigns a value (including the zero value) in INSERT or UPDATE.

func (Column[T]) SetDefault

func (c Column[T]) SetDefault() Assignment

SetDefault assigns the column's database default. PostgreSQL supports it in INSERT and UPDATE; SQLite only by omitting the column from an INSERT, so an UPDATE with SetDefault fails with ErrUnsupported there.

func (Column[T]) SetExcluded

func (c Column[T]) SetExcluded() Assignment

SetExcluded assigns the value proposed for insertion in an upsert (ON CONFLICT ... DO UPDATE SET col = excluded.col).

func (Column[T]) SetExpr

func (c Column[T]) SetExpr(e Expression[T]) Assignment

SetExpr assigns an expression.

func (Column[T]) Table

func (c Column[T]) Table() string

Table returns the qualifier (table name or alias).

type ColumnMeta

type ColumnMeta struct {
	Name  string
	Flags uint16
}

ColumnMeta describes one mapped field.

type ColumnRef

type ColumnRef interface {
	Expr
	ColumnName() string
}

ColumnRef is an untyped reference to a column of a table source. It is used where only the identity of the column matters (ON CONFLICT targets, Omit lists, GROUP BY).

type Compiled

type Compiled struct {
	SQL  string
	Args []any
}

Compiled is a rendered statement ready for inspection or execution.

func (Compiled) String

func (c Compiled) String() string

type Config

type Config struct{}

Config is the receiver of a model's MomoConfig companion method. Every method is read statically by the generator from the Go source and is never executed for its effect; at runtime they are all no-ops. Field arguments are pointers taken from the model receiver (&d.Field); their value is ignored.

func (*Config) Check

func (c *Config) Check(name, expr string)

Check declares a named check constraint. Read statically by the generator; never executed for its effect.

func (*Config) Collate

func (c *Config) Collate(field any, collation string)

Collate sets a column collation. Read statically by the generator; never executed for its effect.

func (*Config) Comment

func (c *Config) Comment(field any, text string)

Comment sets a column comment. Read statically by the generator; never executed for its effect.

func (*Config) Default

func (c *Config) Default(field any, expr string)

Default sets a column default expression. Read statically by the generator; never executed for its effect.

func (*Config) Enum

func (c *Config) Enum(name string, values ...string)

Enum declares a named enum type with its values. Read statically by the generator; never executed for its effect.

func (*Config) Function

func (c *Config) Function(f schema.Function)

Function declares a function. The argument must be a composite literal; read statically by the generator, never executed.

func (*Config) Index

func (c *Config) Index(name string, cols ...any) *IndexOpts

Index declares a non-unique index over the given field references. Read statically by the generator; never executed for its effect.

func (*Config) IndexExpr

func (c *Config) IndexExpr(name string, exprs ...string) *IndexOpts

IndexExpr declares an expression index. Read statically by the generator; never executed for its effect.

func (*Config) Partition

func (c *Config) Partition(p schema.Partition)

Partition declares the table's partitioning. The argument must be a composite literal; read statically by the generator, never executed.

func (*Config) PartitionOf

func (c *Config) PartitionOf(p schema.PartitionOf)

PartitionOf declares the table as a partition of a parent. The argument must be a composite literal; read statically by the generator, never executed.

func (*Config) Policy

func (c *Config) Policy(p schema.Policy)

Policy declares a row-level security policy. The argument must be a composite literal; read statically by the generator, never executed.

func (*Config) RLS

func (c *Config) RLS(r schema.RLS)

RLS declares the table's row-level security flags. The argument must be a composite literal; read statically by the generator, never executed.

func (*Config) Trigger

func (c *Config) Trigger(t schema.Trigger)

Trigger declares a trigger. The argument must be a composite literal; read statically by the generator, never executed.

func (*Config) Unique

func (c *Config) Unique(name string, cols ...any) *IndexOpts

Unique declares a unique index over the given field references. Read statically by the generator; never executed for its effect.

func (*Config) View

func (c *Config) View(v schema.View)

View declares a view or materialized view. The argument must be a composite literal; read statically by the generator, never executed.

type ConflictQuery

type ConflictQuery[T any] struct {
	// contains filtered or unexported fields
}

ConflictQuery is an INSERT awaiting its ON CONFLICT action.

func (ConflictQuery[T]) DoNothing

func (c ConflictQuery[T]) DoNothing() InsertQuery[T]

DoNothing skips rows that conflict.

func (ConflictQuery[T]) DoUpdate

func (c ConflictQuery[T]) DoUpdate(assigns ...Assignment) InsertQuery[T]

DoUpdate updates the existing row on conflict. Use SetExcluded to take the value proposed for insertion.

func (ConflictQuery[T]) Where added in v0.2.0

func (c ConflictQuery[T]) Where(preds ...Predicate) ConflictQuery[T]

Where adds predicates to the conflict target, rendered as ON CONFLICT (cols) WHERE <preds>. PostgreSQL and SQLite need it to infer a partial unique index: the predicate must imply the index predicate, and without it the database rejects the statement. Several calls AND together. It needs target columns in OnConflict (otherwise the build fails with ErrValidation) and works with both DoNothing and DoUpdate. Columns render without a table qualifier, as the database requires in an index predicate. MySQL has no ON CONFLICT and fails with ErrUnsupported.

type Copier

type Copier interface {
	CopyFrom(ctx context.Context, table string, cols []string, rows [][]any) (int64, error)
}

Copier is implemented by drivers with a native bulk-copy path.

func CopierOf

func CopierOf(q Querier) (Copier, bool)

CopierOf returns the Copier behind q: the driver for a *DB, the transaction for a *Tx. For a *DB made with OnConn it is the pinned connection, so COPY runs on the pinned session.

type CreateOnlyColumn

type CreateOnlyColumn[T any] struct {
	// contains filtered or unexported fields
}

CreateOnlyColumn is the descriptor of a column writable in INSERT only. Its Set returns an InsertAssignment, which Update.Set does not accept.

func NewCreateOnlyColumn

func NewCreateOnlyColumn[T any](table, name string, flags uint16) CreateOnlyColumn[T]

NewCreateOnlyColumn is used by generated code.

func (CreateOnlyColumn[T]) AppendSQL

func (c CreateOnlyColumn[T]) AppendSQL(b *Builder)

func (CreateOnlyColumn[T]) As

func (c CreateOnlyColumn[T]) As(alias string) CreateOnlyColumn[T]

As returns the same column qualified by a table alias.

func (CreateOnlyColumn[T]) AsExpr

func (c CreateOnlyColumn[T]) AsExpr() Expression[T]

func (CreateOnlyColumn[T]) Asc

func (c CreateOnlyColumn[T]) Asc() Order

func (CreateOnlyColumn[T]) Between

func (c CreateOnlyColumn[T]) Between(lo, hi T) Predicate

func (CreateOnlyColumn[T]) ColumnName

func (c CreateOnlyColumn[T]) ColumnName() string

func (CreateOnlyColumn[T]) Desc

func (c CreateOnlyColumn[T]) Desc() Order

func (CreateOnlyColumn[T]) Eq

func (c CreateOnlyColumn[T]) Eq(v T) Predicate

func (CreateOnlyColumn[T]) EqExpr

func (c CreateOnlyColumn[T]) EqExpr(e Expression[T]) Predicate

func (CreateOnlyColumn[T]) Excluded added in v0.2.0

func (c CreateOnlyColumn[T]) Excluded() Expression[T]

Excluded is the value proposed for insertion, for an upsert condition.

func (CreateOnlyColumn[T]) Flags

func (c CreateOnlyColumn[T]) Flags() uint16

func (CreateOnlyColumn[T]) Ge

func (c CreateOnlyColumn[T]) Ge(v T) Predicate

func (CreateOnlyColumn[T]) GeExpr

func (c CreateOnlyColumn[T]) GeExpr(e Expression[T]) Predicate

func (CreateOnlyColumn[T]) Gt

func (c CreateOnlyColumn[T]) Gt(v T) Predicate

func (CreateOnlyColumn[T]) GtExpr

func (c CreateOnlyColumn[T]) GtExpr(e Expression[T]) Predicate

func (CreateOnlyColumn[T]) In

func (c CreateOnlyColumn[T]) In(vs ...T) Predicate

func (CreateOnlyColumn[T]) InQuery

func (c CreateOnlyColumn[T]) InQuery(q Subquery) Predicate

func (CreateOnlyColumn[T]) IsDistinctFrom

func (c CreateOnlyColumn[T]) IsDistinctFrom(v T) Predicate

func (CreateOnlyColumn[T]) IsNotDistinctFrom

func (c CreateOnlyColumn[T]) IsNotDistinctFrom(v T) Predicate

func (CreateOnlyColumn[T]) IsNotNull

func (c CreateOnlyColumn[T]) IsNotNull() Predicate

func (CreateOnlyColumn[T]) IsNull

func (c CreateOnlyColumn[T]) IsNull() Predicate

func (CreateOnlyColumn[T]) Le

func (c CreateOnlyColumn[T]) Le(v T) Predicate

func (CreateOnlyColumn[T]) LeExpr

func (c CreateOnlyColumn[T]) LeExpr(e Expression[T]) Predicate

func (CreateOnlyColumn[T]) Lt

func (c CreateOnlyColumn[T]) Lt(v T) Predicate

func (CreateOnlyColumn[T]) LtExpr

func (c CreateOnlyColumn[T]) LtExpr(e Expression[T]) Predicate

func (CreateOnlyColumn[T]) Ne

func (c CreateOnlyColumn[T]) Ne(v T) Predicate

func (CreateOnlyColumn[T]) NeExpr

func (c CreateOnlyColumn[T]) NeExpr(e Expression[T]) Predicate

func (CreateOnlyColumn[T]) NotIn

func (c CreateOnlyColumn[T]) NotIn(vs ...T) Predicate

func (CreateOnlyColumn[T]) NotInQuery

func (c CreateOnlyColumn[T]) NotInQuery(q Subquery) Predicate

func (CreateOnlyColumn[T]) Set

func (c CreateOnlyColumn[T]) Set(v T) InsertAssignment

Set assigns v on insert. The result is an InsertAssignment, which only Insert accepts.

func (CreateOnlyColumn[T]) Table

func (c CreateOnlyColumn[T]) Table() string

type CreateOnlyVectorColumn

type CreateOnlyVectorColumn[V VectorValue] struct {
	// contains filtered or unexported fields
}

CreateOnlyVectorColumn is a vector column writable in INSERT only. Its Set returns an InsertAssignment, which Update.Set does not accept.

func NewCreateOnlyVectorColumn

func NewCreateOnlyVectorColumn[V VectorValue](table, name string, dim int, flags uint16) CreateOnlyVectorColumn[V]

NewCreateOnlyVectorColumn is used by generated code.

func (CreateOnlyVectorColumn) AppendSQL

func (c CreateOnlyVectorColumn) AppendSQL(b *Builder)

AppendSQL renders the qualified, quoted column.

func (CreateOnlyVectorColumn[V]) As

As returns the same column qualified by a table alias.

func (CreateOnlyVectorColumn) AsExpr

func (c CreateOnlyVectorColumn) AsExpr() Expression[V]

AsExpr returns the column as an Expression[V].

func (CreateOnlyVectorColumn) Asc

func (c CreateOnlyVectorColumn) Asc() Order

func (CreateOnlyVectorColumn) ColumnName

func (c CreateOnlyVectorColumn) ColumnName() string

ColumnName returns the unquoted column name.

func (CreateOnlyVectorColumn) Desc

func (c CreateOnlyVectorColumn) Desc() Order

func (CreateOnlyVectorColumn) Dim

func (c CreateOnlyVectorColumn) Dim() int

Dim returns the declared dimension (0 when unchecked).

func (CreateOnlyVectorColumn) Eq

func (c CreateOnlyVectorColumn) Eq(v V) Predicate

func (CreateOnlyVectorColumn) EqExpr

func (c CreateOnlyVectorColumn) EqExpr(e Expression[V]) Predicate

func (CreateOnlyVectorColumn) Flags

func (c CreateOnlyVectorColumn) Flags() uint16

Flags returns the generated column flags.

func (CreateOnlyVectorColumn) In

func (c CreateOnlyVectorColumn) In(vs ...V) Predicate

func (CreateOnlyVectorColumn) InQuery

func (c CreateOnlyVectorColumn) InQuery(q Subquery) Predicate

func (CreateOnlyVectorColumn) IsDistinctFrom

func (c CreateOnlyVectorColumn) IsDistinctFrom(v V) Predicate

IsDistinctFrom is a NULL-safe inequality.

func (CreateOnlyVectorColumn) IsNotDistinctFrom

func (c CreateOnlyVectorColumn) IsNotDistinctFrom(v V) Predicate

IsNotDistinctFrom is a NULL-safe equality.

func (CreateOnlyVectorColumn) IsNotNull

func (c CreateOnlyVectorColumn) IsNotNull() Predicate

IsNotNull is true when the column holds a value.

func (CreateOnlyVectorColumn) IsNull

func (c CreateOnlyVectorColumn) IsNull() Predicate

IsNull is true when the column holds SQL NULL.

func (CreateOnlyVectorColumn) Ne

func (c CreateOnlyVectorColumn) Ne(v V) Predicate

func (CreateOnlyVectorColumn) NeExpr

func (c CreateOnlyVectorColumn) NeExpr(e Expression[V]) Predicate

func (CreateOnlyVectorColumn) NotIn

func (c CreateOnlyVectorColumn) NotIn(vs ...V) Predicate

func (CreateOnlyVectorColumn) NotInQuery

func (c CreateOnlyVectorColumn) NotInQuery(q Subquery) Predicate

func (CreateOnlyVectorColumn[V]) Set

Set assigns v on insert; only Insert accepts the result.

func (CreateOnlyVectorColumn) Table

func (c CreateOnlyVectorColumn) Table() string

Table returns the qualifier (table name or alias).

type DB

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

DB is a Momo handle over a driver. It is safe for concurrent use and holds no state besides configuration: no background goroutines, no implicit migrations, no caches.

func New

func New(drv Driver, opts ...Option) *DB

New wraps a driver. The caller keeps ownership of the underlying pool and closes it.

func (*DB) Begin

func (db *DB) Begin(ctx context.Context, opts TxOptions) (*Tx, error)

Begin starts an explicit transaction. The caller must Commit or Rollback; a deferred Rollback after Commit is a harmless no-op returning ErrTxDone.

func (*DB) Dialect

func (db *DB) Dialect() *dialect.Dialect

func (*DB) Driver

func (db *DB) Driver() Driver

Driver returns the underlying driver, e.g. to reach native features.

func (*DB) OnConn

func (db *DB) OnConn(pc PinnedConn) *DB

OnConn returns a handle whose statements all run on pc, a connection pinned with Pinner.Pin. It is a copy of db: it keeps db's hooks, error classifier and options (WithHook, WithArgLogging, ...). Begin and Tx on it return ErrUnsupported with Op "begin": a pinned connection is not a pool, so transactions are not started on it. Non-transactional Go migrations and session-scoped work use it. The caller still owns pc and releases it.

func (*DB) Tx

func (db *DB) Tx(ctx context.Context, opts TxOptions, fn func(tx *Tx) error) (err error)

Tx runs fn in a transaction. It commits if fn returns nil and rolls back if fn returns an error or panics. A panic is re-raised after the rollback completes (the rollback uses a context detached from ctx's cancellation so it still reaches the database). fn is called exactly once: Momo never retries it.

func (*DB) WithSearch

func (db *DB) WithSearch(ctx context.Context, s SearchSettings, fn func(*Tx) error) error

WithSearch runs fn in a transaction with s applied to it. The settings end with the transaction.

Example

SearchSettings are applied with set_config(..., is_local = true), so they end with the transaction and never reach a pooled connection.

package main

import (
	"context"
	"fmt"

	"github.com/azrtydxb/momo"
	"github.com/azrtydxb/momo/internal/testmodels/example"
	"github.com/azrtydxb/momo/internal/testmodels/example/document"
	"github.com/azrtydxb/momo/vector"
)

func main() {
	var db *momo.DB // opened with momo.New(pgxdriver.New(pool))
	ctx := context.Background()

	settings := momo.SearchSettings{
		HNSWEfSearch:      100,
		HNSWIterativeScan: momo.IterativeRelaxedOrder, // needs pgvector 0.8
		HNSWMaxScanTuples: 20000,
	}
	err := db.WithSearch(ctx, settings, func(tx *momo.Tx) error {
		_, err := momo.Select[example.Document](tx).
			OrderBy(momo.CosineDistance(document.Embedding, make(vector.Vector, 1536)).Asc()).
			Limit(10).
			All(ctx)
		return err
	})
	fmt.Println(err)
}

type DeleteQuery

type DeleteQuery[T any] struct {
	// contains filtered or unexported fields
}

DeleteQuery builds a DELETE of one model table. It is a value: every method returns a copy, so a base query can be reused safely.

A statement without an effective WHERE clause is refused with ErrUnfilteredMutation unless AllRows confirms it. An empty And or Or is not an effective filter.

func Delete

func Delete[T any, PT Modeler[T]](q Querier) DeleteQuery[T]

Delete starts a DELETE from T's table.

func ForceDelete

func ForceDelete[T any, PT Modeler[T]](q Querier) DeleteQuery[T]

ForceDelete is the hard delete for soft-deleting models. It removes rows whether or not they are soft-deleted, and bypasses the refusal a plain Delete gives on such models. The mutation guard still applies.

func (DeleteQuery[T]) AllRows

func (d DeleteQuery[T]) AllRows() DeleteQuery[T]

AllRows confirms that a DELETE without WHERE is intended.

func (DeleteQuery[T]) Exec

func (d DeleteQuery[T]) Exec(ctx context.Context) (Result, error)

Exec runs the DELETE.

func (DeleteQuery[T]) Model

func (d DeleteQuery[T]) Model(m *T) DeleteQuery[T]

Model deletes the row identified by m's primary key. The version column of a versioned model is not checked: unlike a Model update, Model delete removes the row whatever version m carries. Add a Where on the version column for a version-checked delete.

func (DeleteQuery[T]) Returning

func (d DeleteQuery[T]) Returning(cols ...ColumnRef) DeleteQuery[T]

Returning appends a RETURNING list; read the rows with Rows.

func (DeleteQuery[T]) ReturningAll

func (d DeleteQuery[T]) ReturningAll() DeleteQuery[T]

ReturningAll returns every column mapped by T, in metadata order.

func (DeleteQuery[T]) Rows

func (d DeleteQuery[T]) Rows(ctx context.Context) (*ModelRows[T], error)

Rows runs the DELETE and returns its RETURNING rows. It fails with ErrValidation when no Returning list was set.

func (DeleteQuery[T]) SQL

func (d DeleteQuery[T]) SQL() (Compiled, error)

SQL renders the statement without executing it. Guardrail violations are reported here, before anything can reach the database.

func (DeleteQuery[T]) Where

func (d DeleteQuery[T]) Where(ps ...Predicate) DeleteQuery[T]

Where adds predicates combined with AND.

type DialectValuer

type DialectValuer interface {
	DialectValue(d dialect.Name) (any, error)
}

DialectValuer is implemented by values whose wire encoding depends on the dialect, such as vectors (pgvector text vs. sqlite-vec float32 blobs).

type Direction

type Direction uint8

Direction orders by relation count. DirAsc and DirDesc avoid the names Asc(e Expr) and Desc(e Expr) already used in expr.go.

const (
	// DirAsc orders by ascending count.
	DirAsc Direction = iota
	// DirDesc orders by descending count.
	DirDesc
)

type Discarder

type Discarder interface {
	Discard() error
}

Discarder is implemented by pinned connections that can be thrown away instead of returned to the pool. Discard closes the underlying connection and then releases it, so the pool destroys it and no session state (advisory locks, SET values, an open transaction) can reach the next user. Like Release it closes open Rows first, returns an error matching ErrPinnedBusy while a Query, Exec or Begin call is executing (leaving the connection pinned), and returns nil after a successful Release or Discard. Callers that cannot restore a session (for example a failed unlock or RESET) type-assert a PinnedConn to Discarder and call it.

type Discriminator

type Discriminator struct {
	Column string
	Value  any
}

Discriminator restricts a polymorphic has-one/has-many to rows whose type column equals Value.

type DistanceExpr

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

DistanceExpr is a vector distance between a column and a bound query vector. It is an Expression[float64], so it works in the select list, in comparisons against float columns and in ORDER BY, where it orders ascending (nearest first) unless Desc is used.

func CosineDistance

func CosineDistance[V denseVector, C VectorSource[V]](c C, v V) DistanceExpr

CosineDistance is the cosine distance (pgvector <=>).

Example

A nearest-neighbour query orders by a typed distance expression. The query vector is bound as an argument and checked against the column dimension when the SQL is built.

package main

import (
	"fmt"

	"github.com/azrtydxb/momo"
	"github.com/azrtydxb/momo/driver/pgxdriver"
	"github.com/azrtydxb/momo/internal/testmodels/example"
	"github.com/azrtydxb/momo/internal/testmodels/example/document"
	"github.com/azrtydxb/momo/vector"
)

func main() {
	// SQL() only compiles the statement, so no connection is needed here.
	db := momo.New(pgxdriver.New(nil))
	query := make(vector.Vector, 1536)
	query[0] = 1

	c, err := momo.Select[example.Document](db).
		OrderBy(momo.CosineDistance(document.Embedding, query).Asc()).
		Limit(5).
		SQL()
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(c.SQL)
}
Output:
SELECT "documents"."id", "documents"."tenant_id", "documents"."title", "documents"."body", "documents"."embedding", "documents"."status", "documents"."created_at" FROM "documents" ORDER BY ("documents"."embedding" <=> $1) ASC LIMIT $2

func HammingDistance

func HammingDistance[V bitVector, C VectorSource[V]](c C, v V) DistanceExpr

HammingDistance is the Hamming distance of bit vectors (pgvector <~>).

Example

Bit vectors take the Hamming and Jaccard distances.

package main

import (
	"fmt"
	"strings"

	"github.com/azrtydxb/momo"
	"github.com/azrtydxb/momo/driver/pgxdriver"
	"github.com/azrtydxb/momo/internal/testmodels/example"
	"github.com/azrtydxb/momo/vector"
)

func main() {
	db := momo.New(pgxdriver.New(nil))
	col := momo.NewVectorColumn[vector.Bits]("documents", "bits", 8, 0)

	expr := momo.HammingDistance(col, vector.Bits{Len: 8, Data: []byte{0xF0}})
	c, err := momo.Select[example.Document](db).
		OrderBy(expr.Asc()).Limit(3).SQL()
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(strings.Contains(c.SQL, `("documents"."bits" <~> $1) ASC`))
}
Output:
true

func InnerProductDistance

func InnerProductDistance[V denseVector, C VectorSource[V]](c C, v V) DistanceExpr

InnerProductDistance is the negative inner product (pgvector <#>).

func JaccardDistance

func JaccardDistance[V bitVector, C VectorSource[V]](c C, v V) DistanceExpr

JaccardDistance is the Jaccard distance of bit vectors (pgvector <%>).

func L1Distance

func L1Distance[V denseVector, C VectorSource[V]](c C, v V) DistanceExpr

L1Distance is the taxicab distance (pgvector <+>).

func L2Distance

func L2Distance[V denseVector, C VectorSource[V]](c C, v V) DistanceExpr

L2Distance is the Euclidean distance (pgvector <->).

Example

A query vector of the wrong dimension is a build error, not a server error.

package main

import (
	"fmt"

	"github.com/azrtydxb/momo"
	"github.com/azrtydxb/momo/driver/pgxdriver"
	"github.com/azrtydxb/momo/internal/testmodels/example"
	"github.com/azrtydxb/momo/internal/testmodels/example/document"
	"github.com/azrtydxb/momo/vector"
)

func main() {
	db := momo.New(pgxdriver.New(nil))

	_, err := momo.Select[example.Document](db).
		OrderBy(momo.L2Distance(document.Embedding, vector.Vector{1, 2, 3}).Asc()).
		SQL()
	fmt.Println(err != nil)
}
Output:
true

func (DistanceExpr) AppendSQL

func (e DistanceExpr) AppendSQL(b *Builder)

AppendSQL renders the distance, or records a build error when the dialect lacks the metric or the argument does not fit the column.

func (DistanceExpr) Asc

func (e DistanceExpr) Asc() Order

Asc orders nearest first.

func (DistanceExpr) Desc

func (e DistanceExpr) Desc() Order

Desc orders farthest first.

type Driver

type Driver interface {
	Dialect() *dialect.Dialect
	Query(ctx context.Context, sql string, args []any) (Rows, error)
	Exec(ctx context.Context, sql string, args []any) (int64, error)
	Begin(ctx context.Context, opts TxOptions) (DriverTx, error)
}

Driver is the connection-adapter contract. Adapters live in github.com/azrtydxb/momo/driver/... (pgx native, database/sql). The adapter owns the connection pool; Momo never opens or closes connections on its own.

type DriverTx

type DriverTx interface {
	Query(ctx context.Context, sql string, args []any) (Rows, error)
	Exec(ctx context.Context, sql string, args []any) (int64, error)
	Commit(ctx context.Context) error
	Rollback(ctx context.Context) error
}

DriverTx is a driver-level transaction.

type Error

type Error struct {
	Kind       error  // one of the Err* sentinels
	Op         string // "select", "insert", "update", "delete", "exec", "build", ...
	Table      string // when known
	Column     string // when known
	Constraint string // when known
	Err        error  // underlying (often driver) error; may be nil
}

Error is a structured Momo error.

func (*Error) Detail

func (e *Error) Detail() string

Detail returns the underlying error text, or "" when there is none.

func (*Error) Error

func (e *Error) Error() string

func (*Error) Is

func (e *Error) Is(target error) bool

Is reports whether target is this error's kind.

func (*Error) Unwrap

func (e *Error) Unwrap() error

Unwrap returns the underlying error.

type ErrorClassifier

type ErrorClassifier interface {
	ClassifyError(err error) error
}

ErrorClassifier converts a raw driver error into a structured *Error when it recognizes it, and returns err unchanged otherwise. Drivers implement it; the root package stays free of driver imports.

type Expr

type Expr interface {
	AppendSQL(b *Builder)
}

Expr is any SQL expression fragment. Implementations must write identifiers through Builder.Ident and values through Builder.Arg.

type Expression

type Expression[T any] interface {
	Expr
	// contains filtered or unexported methods
}

Expression is an Expr whose SQL result type corresponds to the Go type T. The type parameter lets the compiler reject comparisons between incompatible operands, e.g. an int64 column against a string.

func Coalesce

func Coalesce[T any](e Expression[T], fallback T) Expression[T]

Coalesce returns the first non-NULL argument.

func TypedSQL

func TypedSQL[T any](sql string, args ...any) Expression[T]

TypedSQL is SQL with a declared result type, for use in projections and typed comparisons. Momo cannot verify that the declaration is truthful.

func Value

func Value[T any](v T) Expression[T]

Value wraps a Go value as a typed bound parameter.

type FieldHook

type FieldHook[T any] struct {
	// Column is the index into Meta.Columns.
	Column int
	// Default supplies the value of the column when a write omits it: a
	// Values or ValuesRows row that does not assign the column (an explicit
	// SetDefault, SetNull or zero value counts as assigned and wins), or a
	// Model insert that lists the column in Omit. It never replaces a zero
	// value, because a Model insert writes every column it does not omit. For
	// Values and ValuesRows rows there is no model, so m points to a zero T.
	Default func(m *T) any
	// OnUpdate supplies the value of the column on every update statement
	// that does not assign the column itself with Set; an explicit Set wins.
	// For a Model update m is the model, otherwise a zero T. It overrides the
	// value Model would write from the struct.
	OnUpdate func(m *T) any
	// Validate checks the value the statement will write for the column and
	// runs before SQL is built. v is the Go value bound for the column; it is
	// nil when the statement writes NULL or DEFAULT, or (insert only) omits
	// the column without a Default, so a required-field validator should
	// reject nil. A SQL expression assignment is not validated, and an
	// update validates only the columns it sets. A non-nil error becomes
	// *Error{Kind: ErrValidation, Op, Table, Column} wrapping it.
	Validate func(v any) error
}

FieldHook attaches Go-side behavior to one column of Meta[T].

Hooks are declared in Meta.Fields (generated or hand-written) or per DB with WithFieldHooks. Write builders run them before the statement is built, in this order: Default (insert), OnUpdate (update), Validate. A failing validator makes SQL, Exec and Rows return an *Error of kind ErrValidation before anything reaches the database.

A hook for a column the statement cannot write is handled per statement:

  • Insert: Default is ignored for a column that is not assignable on INSERT (identity or read-only; see columnAssignable with writeInsert).
  • Update: OnUpdate is refused with ErrValidation for the key, version, soft-delete, created-at and updated-at columns (updated_at is already maintained automatically; see WithClock) and for a column that is not assignable on UPDATE (identity, read-only, create-only or immutable).

OnUpdate is not a trigger: it adds a value to an update that already sets something. An update with only OnUpdate hooks and no Set still fails with "no columns to update".

A Default or OnUpdate function runs once per row and statement: a chunked bulk insert computes and validates every value before the first chunk and binds those same values, so a random default is safe.

type Hook

type Hook interface {
	BeforeQuery(ctx context.Context, e *QueryEvent) context.Context
	AfterQuery(ctx context.Context, e *QueryEvent)
}

Hook observes statements. BeforeQuery may return a derived context (for tracing spans); AfterQuery receives that context. Hooks run synchronously on the calling goroutine and must be safe for concurrent use.

type ImmutableColumn

type ImmutableColumn[T any] struct {
	// contains filtered or unexported fields
}

ImmutableColumn is the descriptor of a column that is set on insert and never changed. Its Set returns an InsertAssignment, which Update.Set does not accept.

func NewImmutableColumn

func NewImmutableColumn[T any](table, name string, flags uint16) ImmutableColumn[T]

NewImmutableColumn is used by generated code.

func (ImmutableColumn[T]) AppendSQL

func (c ImmutableColumn[T]) AppendSQL(b *Builder)

func (ImmutableColumn[T]) As

func (c ImmutableColumn[T]) As(alias string) ImmutableColumn[T]

As returns the same column qualified by a table alias.

func (ImmutableColumn[T]) AsExpr

func (c ImmutableColumn[T]) AsExpr() Expression[T]

func (ImmutableColumn[T]) Asc

func (c ImmutableColumn[T]) Asc() Order

func (ImmutableColumn[T]) Between

func (c ImmutableColumn[T]) Between(lo, hi T) Predicate

func (ImmutableColumn[T]) ColumnName

func (c ImmutableColumn[T]) ColumnName() string

func (ImmutableColumn[T]) Desc

func (c ImmutableColumn[T]) Desc() Order

func (ImmutableColumn[T]) Eq

func (c ImmutableColumn[T]) Eq(v T) Predicate

func (ImmutableColumn[T]) EqExpr

func (c ImmutableColumn[T]) EqExpr(e Expression[T]) Predicate

func (ImmutableColumn[T]) Excluded added in v0.2.0

func (c ImmutableColumn[T]) Excluded() Expression[T]

Excluded is the value proposed for insertion, for an upsert condition.

func (ImmutableColumn[T]) Flags

func (c ImmutableColumn[T]) Flags() uint16

func (ImmutableColumn[T]) Ge

func (c ImmutableColumn[T]) Ge(v T) Predicate

func (ImmutableColumn[T]) GeExpr

func (c ImmutableColumn[T]) GeExpr(e Expression[T]) Predicate

func (ImmutableColumn[T]) Gt

func (c ImmutableColumn[T]) Gt(v T) Predicate

func (ImmutableColumn[T]) GtExpr

func (c ImmutableColumn[T]) GtExpr(e Expression[T]) Predicate

func (ImmutableColumn[T]) In

func (c ImmutableColumn[T]) In(vs ...T) Predicate

func (ImmutableColumn[T]) InQuery

func (c ImmutableColumn[T]) InQuery(q Subquery) Predicate

func (ImmutableColumn[T]) IsDistinctFrom

func (c ImmutableColumn[T]) IsDistinctFrom(v T) Predicate

func (ImmutableColumn[T]) IsNotDistinctFrom

func (c ImmutableColumn[T]) IsNotDistinctFrom(v T) Predicate

func (ImmutableColumn[T]) IsNotNull

func (c ImmutableColumn[T]) IsNotNull() Predicate

func (ImmutableColumn[T]) IsNull

func (c ImmutableColumn[T]) IsNull() Predicate

func (ImmutableColumn[T]) Le

func (c ImmutableColumn[T]) Le(v T) Predicate

func (ImmutableColumn[T]) LeExpr

func (c ImmutableColumn[T]) LeExpr(e Expression[T]) Predicate

func (ImmutableColumn[T]) Lt

func (c ImmutableColumn[T]) Lt(v T) Predicate

func (ImmutableColumn[T]) LtExpr

func (c ImmutableColumn[T]) LtExpr(e Expression[T]) Predicate

func (ImmutableColumn[T]) Ne

func (c ImmutableColumn[T]) Ne(v T) Predicate

func (ImmutableColumn[T]) NeExpr

func (c ImmutableColumn[T]) NeExpr(e Expression[T]) Predicate

func (ImmutableColumn[T]) NotIn

func (c ImmutableColumn[T]) NotIn(vs ...T) Predicate

func (ImmutableColumn[T]) NotInQuery

func (c ImmutableColumn[T]) NotInQuery(q Subquery) Predicate

func (ImmutableColumn[T]) Set

func (c ImmutableColumn[T]) Set(v T) InsertAssignment

Set assigns v on insert. The result is an InsertAssignment, which only Insert accepts.

func (ImmutableColumn[T]) Table

func (c ImmutableColumn[T]) Table() string

type ImmutableVectorColumn

type ImmutableVectorColumn[V VectorValue] struct {
	// contains filtered or unexported fields
}

ImmutableVectorColumn is a vector column set on insert and never changed. Its Set returns an InsertAssignment, which Update.Set does not accept.

func NewImmutableVectorColumn

func NewImmutableVectorColumn[V VectorValue](table, name string, dim int, flags uint16) ImmutableVectorColumn[V]

NewImmutableVectorColumn is used by generated code.

func (ImmutableVectorColumn) AppendSQL

func (c ImmutableVectorColumn) AppendSQL(b *Builder)

AppendSQL renders the qualified, quoted column.

func (ImmutableVectorColumn[V]) As

As returns the same column qualified by a table alias.

func (ImmutableVectorColumn) AsExpr

func (c ImmutableVectorColumn) AsExpr() Expression[V]

AsExpr returns the column as an Expression[V].

func (ImmutableVectorColumn) Asc

func (c ImmutableVectorColumn) Asc() Order

func (ImmutableVectorColumn) ColumnName

func (c ImmutableVectorColumn) ColumnName() string

ColumnName returns the unquoted column name.

func (ImmutableVectorColumn) Desc

func (c ImmutableVectorColumn) Desc() Order

func (ImmutableVectorColumn) Dim

func (c ImmutableVectorColumn) Dim() int

Dim returns the declared dimension (0 when unchecked).

func (ImmutableVectorColumn) Eq

func (c ImmutableVectorColumn) Eq(v V) Predicate

func (ImmutableVectorColumn) EqExpr

func (c ImmutableVectorColumn) EqExpr(e Expression[V]) Predicate

func (ImmutableVectorColumn) Flags

func (c ImmutableVectorColumn) Flags() uint16

Flags returns the generated column flags.

func (ImmutableVectorColumn) In

func (c ImmutableVectorColumn) In(vs ...V) Predicate

func (ImmutableVectorColumn) InQuery

func (c ImmutableVectorColumn) InQuery(q Subquery) Predicate

func (ImmutableVectorColumn) IsDistinctFrom

func (c ImmutableVectorColumn) IsDistinctFrom(v V) Predicate

IsDistinctFrom is a NULL-safe inequality.

func (ImmutableVectorColumn) IsNotDistinctFrom

func (c ImmutableVectorColumn) IsNotDistinctFrom(v V) Predicate

IsNotDistinctFrom is a NULL-safe equality.

func (ImmutableVectorColumn) IsNotNull

func (c ImmutableVectorColumn) IsNotNull() Predicate

IsNotNull is true when the column holds a value.

func (ImmutableVectorColumn) IsNull

func (c ImmutableVectorColumn) IsNull() Predicate

IsNull is true when the column holds SQL NULL.

func (ImmutableVectorColumn) Ne

func (c ImmutableVectorColumn) Ne(v V) Predicate

func (ImmutableVectorColumn) NeExpr

func (c ImmutableVectorColumn) NeExpr(e Expression[V]) Predicate

func (ImmutableVectorColumn) NotIn

func (c ImmutableVectorColumn) NotIn(vs ...V) Predicate

func (ImmutableVectorColumn) NotInQuery

func (c ImmutableVectorColumn) NotInQuery(q Subquery) Predicate

func (ImmutableVectorColumn[V]) Set

Set assigns v on insert; only Insert accepts the result.

func (ImmutableVectorColumn) Table

func (c ImmutableVectorColumn) Table() string

Table returns the qualifier (table name or alias).

type IndexOpts

type IndexOpts struct{}

IndexOpts carries the optional parts of an index declaration. Its methods are read statically by the generator and are no-ops at runtime.

func (*IndexOpts) Concurrently

func (o *IndexOpts) Concurrently() *IndexOpts

Concurrently marks the index to be built concurrently. Read statically; no-op at runtime.

func (*IndexOpts) Desc

func (o *IndexOpts) Desc(field any) *IndexOpts

Desc marks one indexed field descending. Read statically; no-op at runtime.

func (*IndexOpts) Method

func (o *IndexOpts) Method(m string) *IndexOpts

Method sets the index access method. Read statically; no-op at runtime.

func (*IndexOpts) Nulls

func (o *IndexOpts) Nulls(field any, order string) *IndexOpts

Nulls sets NULLS ordering ("first" or "last") for one indexed field. Read statically; no-op at runtime.

func (*IndexOpts) Opclass

func (o *IndexOpts) Opclass(field any, opclass string) *IndexOpts

Opclass sets the operator class of one indexed field. Read statically; no-op at runtime.

func (*IndexOpts) Where

func (o *IndexOpts) Where(pred string) *IndexOpts

Where sets the partial-index predicate. Read statically; no-op at runtime.

func (*IndexOpts) With

func (o *IndexOpts) With(key, value string) *IndexOpts

With sets one index storage parameter. Read statically; no-op at runtime.

type InsertAssignment

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

InsertAssignment is one explicit column write that is valid in an INSERT only. Create-only and immutable columns produce it from Set; it is not an Assignment, so Update.Set rejects it at compile time. Raw SQL is outside this guarantee.

func (InsertAssignment) ColumnName

func (a InsertAssignment) ColumnName() string

ColumnName returns the assigned column.

type InsertQuery

type InsertQuery[T any] struct {
	// contains filtered or unexported fields
}

InsertQuery builds and runs an INSERT into T's table.

Columns come from T's generated metadata only, so no caller-supplied string becomes an identifier. Identity columns and FlagReadOnly columns are never written, and values are always bound. InsertQuery is an immutable value: every method returns a new query.

func Insert

func Insert[T any, PT Modeler[T]](q Querier) InsertQuery[T]

Insert starts an INSERT for model T. Use Model or Models to write whole models, or Values to write explicit columns.

func Upsert

func Upsert[T any, PT Modeler[T]](q Querier) InsertQuery[T]

Upsert starts an INSERT ... ON CONFLICT DO UPDATE for model T. Unless the caller chooses otherwise with OnConflict, the conflict target is T's primary key, and DoUpdate (when not given, or given without assignments) assigns every written column that an update may change with SetExcluded. The primary key, version, soft-delete and created-at columns are never part of DO UPDATE SET, and an explicit assignment to one (or to updated-at) is refused with ErrValidation. When the model has an updated-at column, DO UPDATE SET always ends with updated_at = EXCLUDED.updated_at, also after an explicit DoUpdate list, so a conflict refreshes the timestamp just as Update does; created_at is never overwritten.

The version column is neither advanced nor checked on conflict: an upsert overwrites the existing row without optimistic-lock detection. Explicit identity values inserted with OVERRIDING SYSTEM VALUE do not advance the PostgreSQL identity sequence, so later generated identities may collide with them; the caller must setval the sequence.

When the conflict target is the primary key and every model carries a non-zero identity value, the identity column joins the INSERT column list (PostgreSQL renders OVERRIDING SYSTEM VALUE), so the statement can hit an existing row. A model whose identity is zero is inserted with a generated identity. Mixing both in one Models call is refused with ErrValidation. Dialects without ON CONFLICT (MySQL) fail with ErrUnsupported.

func (InsertQuery[T]) Exec

func (q InsertQuery[T]) Exec(ctx context.Context) (Result, error)

Exec runs the statement and reports the rows it inserted.

A Models call whose rows would exceed the dialect's bind-parameter limit is split into several multi-row statements, run in order, and the rows affected are summed. The chunks are atomic: on a DB they run in one transaction, on a Tx inside a savepoint (when the dialect has them), so a failing chunk undoes the earlier ones. A Tx on a dialect without savepoints runs the chunks directly in the caller's transaction, which stays atomic as a whole. A DB pinned to one connection by OnConn cannot begin a transaction, so a chunked insert there fails with ErrUnsupported before any statement runs. A panic during a chunk rolls the scope back and is re-raised. All chunks share one clock read, so every row gets the same automatic timestamp. A single row with more bound columns than the limit fails with ErrLimitExceeded.

func (InsertQuery[T]) Model

func (q InsertQuery[T]) Model(m *T) InsertQuery[T]

Model adds one model, writing every column except identity, read-only and omitted columns. Zero values are written, never skipped.

func (InsertQuery[T]) Models

func (q InsertQuery[T]) Models(ms []T) InsertQuery[T]

Models adds every element of ms as one row of a multi-row VALUES list. With an empty list the query fails with ErrValidation.

func (InsertQuery[T]) Omit

func (q InsertQuery[T]) Omit(cols ...ColumnRef) InsertQuery[T]

Omit excludes columns from a model insert, leaving their database default. Combined with Values or ValuesRows the query fails with ErrValidation: those already list exactly the columns they write.

func (InsertQuery[T]) OnConflict

func (q InsertQuery[T]) OnConflict(cols ...ColumnRef) ConflictQuery[T]

OnConflict starts an ON CONFLICT clause with the given conflict target. With no columns the target is omitted, which only DoNothing allows.

func (InsertQuery[T]) Returning

func (q InsertQuery[T]) Returning(cols ...ColumnRef) InsertQuery[T]

Returning appends a RETURNING list; read the rows with Rows.

func (InsertQuery[T]) ReturningAll

func (q InsertQuery[T]) ReturningAll() InsertQuery[T]

ReturningAll returns every column mapped by T, in metadata order.

func (InsertQuery[T]) Rows

func (q InsertQuery[T]) Rows(ctx context.Context) (*ModelRows[T], error)

Rows runs the statement and returns its RETURNING rows. It fails with ErrValidation when no RETURNING list was set. A chunked bulk insert (see Exec) returns the rows of all chunks in insertion order. The first chunk runs inside Rows, so its errors are returned here; the remaining chunks run as the rows are consumed, and Close runs any that are left so the insert stays whole. The caller MUST check the error from Close (and Err): a later chunk can fail there, which rolls the whole insert back.

func (InsertQuery[T]) SQL

func (q InsertQuery[T]) SQL() (Compiled, error)

SQL compiles the statement without executing it.

func (InsertQuery[T]) Values

func (q InsertQuery[T]) Values(assigns ...InsertValue) InsertQuery[T]

Values adds one row of explicit column writes. A column that is not assigned is omitted from the statement, so its database default applies.

func (InsertQuery[T]) ValuesRows

func (q InsertQuery[T]) ValuesRows(rows ...[]Assignment) InsertQuery[T]

ValuesRows adds one row per element to a multi-row VALUES list; each row is a list of assignments, like the argument of Values. The column list is the union of the columns the rows assign, in order of first appearance. A column a row does not assign takes its database default for that row (DEFAULT), and SetDefault renders DEFAULT inline. A dialect that cannot put DEFAULT inside VALUES (SQLite) omits a column that is default in every row and fails with ErrUnsupported when a column is default in some rows and given in others. ValuesRows cannot be combined with Values, Model, Models or Omit, and a statement over the dialect's bind-parameter limit fails with ErrLimitExceeded (DEFAULT and NULL bind no parameter).

func (InsertQuery[T]) Where added in v0.2.0

func (q InsertQuery[T]) Where(preds ...Predicate) InsertQuery[T]

Where adds a condition to the DO UPDATE action: the existing row is updated only when the predicates hold, rendered as DO UPDATE SET ... WHERE <preds>. When they do not, the conflicting row is left untouched and the statement affects 0 rows (with Returning, no row comes back). Several calls AND together.

Columns are qualified by the table name, so they mean the existing row; use Column.Excluded for the value proposed for insertion, for example until.LtExpr(until.Excluded()). It is valid only when the conflict action is DoUpdate, including Upsert[T], whose default update set is kept; after DoNothing, or without any conflict clause, the build fails with ErrValidation. MySQL fails with ErrUnsupported.

type InsertValue

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

InsertValue is an explicit column write accepted by Insert.Values: an Assignment, or an InsertAssignment from a create-only or immutable column.

type IterativeScan

type IterativeScan string

IterativeScan is the value of an hnsw.iterative_scan or ivfflat.iterative_scan setting.

const (
	// IterativeOff disables iterative index scans.
	IterativeOff IterativeScan = "off"
	// IterativeRelaxedOrder keeps scanning until enough rows pass the filter,
	// allowing slightly out-of-order results.
	IterativeRelaxedOrder IterativeScan = "relaxed_order"
	// IterativeStrictOrder keeps exact distance order while scanning (hnsw only).
	IterativeStrictOrder IterativeScan = "strict_order"
)

type LoadOption

type LoadOption[C any] func(*loadConfig)

LoadOption configures one relation load.

func LoadOnlyDeleted

func LoadOnlyDeleted[C any]() LoadOption[C]

LoadOnlyDeleted loads only soft-deleted children, like SelectQuery.OnlyDeleted.

func LoadOrderBy

func LoadOrderBy[C any](items ...Orderable) LoadOption[C]

LoadOrderBy sets the child order. Stable tie-breakers (relation key columns, then child primary key) are always appended after it.

func LoadPerParentLimit

func LoadPerParentLimit[C any](n int) LoadOption[C]

LoadPerParentLimit keeps at most n children per parent, in load order (LoadOrderBy, then the relation key and child primary key), using a window function so the load stays one statement per bind-limit chunk. It applies to has-many and many-to-many relations; on belongs-to and has-one the load fails with ErrUnsupported, and a negative n fails with ErrValidation (both with Op "load", before any statement runs). Zero leaves the load unlimited. Nested loads (Then) run over the kept children only. A many-to-many relation whose child has a primary key ranks with DENSE_RANK, so duplicated join-table rows share a rank and cannot use up the limit; without a child primary key (Relation.ChildPK empty) it ranks with ROW_NUMBER, so duplicated join rows each take a rank and can count against the limit before duplicates collapse. Window functions need PostgreSQL, MySQL 8 / MariaDB 10.2 or SQLite 3.25 and later.

func LoadWhere

func LoadWhere[C any](ps ...Predicate) LoadOption[C]

LoadWhere adds predicates on the child table to the child statement.

func LoadWithDeleted

func LoadWithDeleted[C any]() LoadOption[C]

LoadWithDeleted includes soft-deleted children for child models with a soft-delete column, like SelectQuery.WithDeleted. They are excluded by default.

type Loader

type Loader[P any] interface {
	// contains filtered or unexported methods
}

Loader is one relation load request, optionally scoped and nested. It is sealed: only Relation.Scope, Relation.Then and Relation itself implement it.

type Meta

type Meta[T any] struct {
	// Table is empty for projections.
	Table string
	// Schema is the database schema qualifying Table; empty for the default.
	Schema  string
	Columns []ColumnMeta
	// Ptr returns the address of the field for column i, for scanning.
	Ptr func(m *T, i int) any
	// Value returns the value of the field for column i, for binding.
	Value func(m *T, i int) any
	// TableSchema is the normalized table definition; nil for projections.
	TableSchema *schema.Table

	// Derived column indexes, filled by generated code. -1 means absent for
	// Identity, Version, SoftDelete, CreatedAt and UpdatedAt.
	Identity   int
	Version    int
	SoftDelete int
	// SoftDeleteKind is the type of the soft-delete marker column; the zero
	// value is a nullable timestamp.
	SoftDeleteKind SoftDeleteKind
	CreatedAt      int
	UpdatedAt      int
	PK             []int

	// Fields are Go-side defaults and validators per column, run by the
	// write builders before SQL is built. See FieldHook.
	Fields []FieldHook[T]
}

Meta is the generated, reflection-free description of a model or projection type T. Generated code returns a pointer to a package-level Meta value; Momo never mutates it.

type Model

type Model struct{}

Model is embedded in a struct to declare it a Momo model. The struct tag on the embedded field carries table-level options:

type Document struct {
    momo.Model `momo:"table:documents"`
    ...
}

It is zero-sized and has no behavior at runtime.

type ModelRows

type ModelRows[T any] struct {
	// contains filtered or unexported fields
}

ModelRows is the cursor of a write statement with a RETURNING list. Each row scans into the model fields named by the list; other fields are left untouched.

func (*ModelRows[T]) Close

func (r *ModelRows[T]) Close() error

Close releases the cursor. It is safe to call more than once.

func (*ModelRows[T]) Err

func (r *ModelRows[T]) Err() error

Err returns the error that ended iteration, if any.

func (*ModelRows[T]) Next

func (r *ModelRows[T]) Next() bool

Next advances to the next returned row.

func (*ModelRows[T]) ScanModel

func (r *ModelRows[T]) ScanModel(m *T) error

ScanModel scans the current row into the fields of m named by RETURNING.

type Modeler

type Modeler[T any] interface {
	*T
	MomoMeta() *Meta[T]
}

Modeler is implemented (by generated code) on pointers to model and projection types. It ties T to its metadata at compile time.

type NamedArg

type NamedArg struct {
	Name  string
	Value any
}

NamedArg is a named argument for an @name placeholder in raw SQL. Build it with Named.

func Named

func Named(name string, value any) NamedArg

Named binds value to every @name placeholder in a raw fragment. An @name with no matching argument is a build error, except on MySQL where it is a user variable and is left verbatim. A Named argument that no @name in the fragment references is a build error on every dialect, so a typo cannot run silently.

Raw scanner assumptions: MySQL NO_BACKSLASH_ESCAPES and PostgreSQL standard_conforming_strings=off are not supported (MySQL strings always honour backslash escapes, PostgreSQL ones only after E). MySQL /*! ... */ executable comments are treated as ordinary comments. On PostgreSQL the operators ?| and ?& are operators, not binds followed by | or &: write "? |" with a space for a bind followed by OR. A "<@" on PostgreSQL is the containment operator, never an @name. A bare native $n placeholder on PostgreSQL is left alone only when the fragment has no ? or @name bind; mixing them is a build error. An unterminated string, quoted identifier, block comment or dollar quote is a build error.

type NullColumn

type NullColumn[T any] struct{ Column[T] }

NullColumn is the descriptor for a nullable column. It adds SetNull.

func NewNullColumn

func NewNullColumn[T any](table, name string, flags uint16) NullColumn[T]

NewNullColumn is used by generated code.

func (NullColumn[T]) As

func (c NullColumn[T]) As(alias string) NullColumn[T]

As returns the same column qualified by a table alias.

func (NullColumn[T]) SetNull

func (c NullColumn[T]) SetNull() Assignment

SetNull assigns SQL NULL.

type NullReadOnlyColumn

type NullReadOnlyColumn[T any] struct {
	// contains filtered or unexported fields
}

NullReadOnlyColumn is the descriptor of a nullable read-only column. It has predicate and ordering methods only.

func NewNullReadOnlyColumn

func NewNullReadOnlyColumn[T any](table, name string, flags uint16) NullReadOnlyColumn[T]

NewNullReadOnlyColumn is used by generated code.

func (NullReadOnlyColumn[T]) AppendSQL

func (c NullReadOnlyColumn[T]) AppendSQL(b *Builder)

func (NullReadOnlyColumn[T]) As

func (c NullReadOnlyColumn[T]) As(alias string) NullReadOnlyColumn[T]

As returns the same column qualified by a table alias.

func (NullReadOnlyColumn[T]) AsExpr

func (c NullReadOnlyColumn[T]) AsExpr() Expression[T]

func (NullReadOnlyColumn[T]) Asc

func (c NullReadOnlyColumn[T]) Asc() Order

func (NullReadOnlyColumn[T]) Between

func (c NullReadOnlyColumn[T]) Between(lo, hi T) Predicate

func (NullReadOnlyColumn[T]) ColumnName

func (c NullReadOnlyColumn[T]) ColumnName() string

func (NullReadOnlyColumn[T]) Desc

func (c NullReadOnlyColumn[T]) Desc() Order

func (NullReadOnlyColumn[T]) Eq

func (c NullReadOnlyColumn[T]) Eq(v T) Predicate

func (NullReadOnlyColumn[T]) EqExpr

func (c NullReadOnlyColumn[T]) EqExpr(e Expression[T]) Predicate

func (NullReadOnlyColumn[T]) Excluded added in v0.2.0

func (c NullReadOnlyColumn[T]) Excluded() Expression[T]

Excluded is the value proposed for insertion, for an upsert condition.

func (NullReadOnlyColumn[T]) Flags

func (c NullReadOnlyColumn[T]) Flags() uint16

func (NullReadOnlyColumn[T]) Ge

func (c NullReadOnlyColumn[T]) Ge(v T) Predicate

func (NullReadOnlyColumn[T]) GeExpr

func (c NullReadOnlyColumn[T]) GeExpr(e Expression[T]) Predicate

func (NullReadOnlyColumn[T]) Gt

func (c NullReadOnlyColumn[T]) Gt(v T) Predicate

func (NullReadOnlyColumn[T]) GtExpr

func (c NullReadOnlyColumn[T]) GtExpr(e Expression[T]) Predicate

func (NullReadOnlyColumn[T]) In

func (c NullReadOnlyColumn[T]) In(vs ...T) Predicate

func (NullReadOnlyColumn[T]) InQuery

func (c NullReadOnlyColumn[T]) InQuery(q Subquery) Predicate

func (NullReadOnlyColumn[T]) IsDistinctFrom

func (c NullReadOnlyColumn[T]) IsDistinctFrom(v T) Predicate

func (NullReadOnlyColumn[T]) IsNotDistinctFrom

func (c NullReadOnlyColumn[T]) IsNotDistinctFrom(v T) Predicate

func (NullReadOnlyColumn[T]) IsNotNull

func (c NullReadOnlyColumn[T]) IsNotNull() Predicate

func (NullReadOnlyColumn[T]) IsNull

func (c NullReadOnlyColumn[T]) IsNull() Predicate

func (NullReadOnlyColumn[T]) Le

func (c NullReadOnlyColumn[T]) Le(v T) Predicate

func (NullReadOnlyColumn[T]) LeExpr

func (c NullReadOnlyColumn[T]) LeExpr(e Expression[T]) Predicate

func (NullReadOnlyColumn[T]) Lt

func (c NullReadOnlyColumn[T]) Lt(v T) Predicate

func (NullReadOnlyColumn[T]) LtExpr

func (c NullReadOnlyColumn[T]) LtExpr(e Expression[T]) Predicate

func (NullReadOnlyColumn[T]) Ne

func (c NullReadOnlyColumn[T]) Ne(v T) Predicate

func (NullReadOnlyColumn[T]) NeExpr

func (c NullReadOnlyColumn[T]) NeExpr(e Expression[T]) Predicate

func (NullReadOnlyColumn[T]) NotIn

func (c NullReadOnlyColumn[T]) NotIn(vs ...T) Predicate

func (NullReadOnlyColumn[T]) NotInQuery

func (c NullReadOnlyColumn[T]) NotInQuery(q Subquery) Predicate

func (NullReadOnlyColumn[T]) Table

func (c NullReadOnlyColumn[T]) Table() string

type NullVectorColumn

type NullVectorColumn[V VectorValue] struct{ VectorColumn[V] }

NullVectorColumn is the descriptor of a nullable vector column. It adds SetNull.

func NewNullVectorColumn

func NewNullVectorColumn[V VectorValue](table, name string, dim int, flags uint16) NullVectorColumn[V]

NewNullVectorColumn is used by generated code.

func (NullVectorColumn) AppendSQL

func (c NullVectorColumn) AppendSQL(b *Builder)

AppendSQL renders the qualified, quoted column.

func (NullVectorColumn[V]) As

func (c NullVectorColumn[V]) As(alias string) NullVectorColumn[V]

As returns the same column qualified by a table alias.

func (NullVectorColumn) AsExpr

func (c NullVectorColumn) AsExpr() Expression[V]

AsExpr returns the column as an Expression[V].

func (NullVectorColumn) Asc

func (c NullVectorColumn) Asc() Order

func (NullVectorColumn) ColumnName

func (c NullVectorColumn) ColumnName() string

ColumnName returns the unquoted column name.

func (NullVectorColumn) Desc

func (c NullVectorColumn) Desc() Order

func (NullVectorColumn) Dim

func (c NullVectorColumn) Dim() int

Dim returns the declared dimension (0 when unchecked).

func (NullVectorColumn) Eq

func (c NullVectorColumn) Eq(v V) Predicate

func (NullVectorColumn) EqExpr

func (c NullVectorColumn) EqExpr(e Expression[V]) Predicate

func (NullVectorColumn) Flags

func (c NullVectorColumn) Flags() uint16

Flags returns the generated column flags.

func (NullVectorColumn) In

func (c NullVectorColumn) In(vs ...V) Predicate

func (NullVectorColumn) InQuery

func (c NullVectorColumn) InQuery(q Subquery) Predicate

func (NullVectorColumn) IsDistinctFrom

func (c NullVectorColumn) IsDistinctFrom(v V) Predicate

IsDistinctFrom is a NULL-safe inequality.

func (NullVectorColumn) IsNotDistinctFrom

func (c NullVectorColumn) IsNotDistinctFrom(v V) Predicate

IsNotDistinctFrom is a NULL-safe equality.

func (NullVectorColumn) IsNotNull

func (c NullVectorColumn) IsNotNull() Predicate

IsNotNull is true when the column holds a value.

func (NullVectorColumn) IsNull

func (c NullVectorColumn) IsNull() Predicate

IsNull is true when the column holds SQL NULL.

func (NullVectorColumn) Ne

func (c NullVectorColumn) Ne(v V) Predicate

func (NullVectorColumn) NeExpr

func (c NullVectorColumn) NeExpr(e Expression[V]) Predicate

func (NullVectorColumn) NotIn

func (c NullVectorColumn) NotIn(vs ...V) Predicate

func (NullVectorColumn) NotInQuery

func (c NullVectorColumn) NotInQuery(q Subquery) Predicate

func (NullVectorColumn[V]) SetNull

func (c NullVectorColumn[V]) SetNull() Assignment

SetNull assigns SQL NULL.

func (NullVectorColumn) Table

func (c NullVectorColumn) Table() string

Table returns the qualifier (table name or alias).

type Nulls

type Nulls uint8

Nulls placement for ORDER BY.

const (
	NullsDefault Nulls = iota
	NullsFirst
	NullsLast
)

type Number

type Number interface{ ~int64 | ~float64 }

Number is the constraint of the numeric aggregates SUM.

type Option

type Option func(*DB)

Option configures a DB.

func WithArgLogging

func WithArgLogging() Option

WithArgLogging includes bind-parameter values in QueryEvent.Args. It is off by default because parameters often contain personal data.

func WithClock

func WithClock(now func() time.Time) Option

WithClock sets the clock used for automatic timestamps and soft-delete markers. The clock is read once per statement. A nil clock keeps the default, time.Now.

func WithFieldHooks

func WithFieldHooks[T any, PT Modeler[T]](hooks []FieldHook[T]) Option

WithFieldHooks registers Go-side defaults and validators for model T on this DB. They apply in addition to T's Meta.Fields, after them, and are looked up by the Meta pointer, so no reflection or tag inspection runs per query. Several calls for the same T accumulate. Hooks are keyed by the *Meta[T] that T's MomoMeta returns, so MomoMeta must return the same pointer on every call (a package-level variable); a method that builds a fresh Meta each time never matches the registered hooks.

func WithHook

func WithHook(h Hook) Option

WithHook registers an observability hook. Hooks run in registration order.

A hook that also implements RelationHook receives relation load events.

func WithMaxParams

func WithMaxParams(n int) Option

WithMaxParams lowers the dialect bind-parameter limit for this DB to n. It is for tests and for drivers or proxies with tighter limits than the dialect default.

n must be positive: zero or a negative n would read as "no limit" in every guard, so WithMaxParams panics at New with a message naming the value (a programmer error, like a bad regexp.MustCompile). An n above the dialect's own limit is clamped to it, because the wire protocol rejects more parameters than that; a dialect that declares no limit takes n as given.

The limit applies to everything the DB runs: relation-load chunking (Load, LoadAll), bulk-insert chunking, raw SQL bind checks including In, transactions begun from the DB (Tx), and handles from OnConn. The dialect value is copied; the shared dialect is never mutated.

type Order

type Order struct {
	Expr  Expr
	Desc  bool
	Nulls Nulls
}

Order is one ORDER BY item.

func Asc

func Asc(e Expr) Order

Asc orders by an arbitrary expression ascending.

func Desc

func Desc(e Expr) Order

Desc orders by an arbitrary expression descending.

func (Order) NullsFirst

func (o Order) NullsFirst() Order

NullsFirst returns o with explicit NULLS FIRST.

func (Order) NullsLast

func (o Order) NullsLast() Order

NullsLast returns o with explicit NULLS LAST.

type Orderable

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

Orderable is accepted by OrderBy: an Order or any Expression.

type PinnedConn

type PinnedConn interface {
	Query(ctx context.Context, sql string, args []any) (Rows, error)
	Exec(ctx context.Context, sql string, args []any) (int64, error)
	Begin(ctx context.Context, opts TxOptions) (DriverTx, error)
	Release() error
}

PinnedConn is one database connection checked out of a driver's pool. Statements on it run on the same session. Begin starts a transaction on that session; Release returns the connection to the pool.

Release contract: Release closes any Rows from Query that are still open (a forgotten Rows.Close cannot leak the connection), then returns the connection; those Rows are unusable afterwards. Rows must not be in use by another goroutine at that moment. If a Query, Exec or Begin call is still executing in another goroutine, Release does nothing and returns an error matching ErrPinnedBusy (errors.Is); call it again once that call has returned. Release after a successful Release returns nil. Callers must finish transactions begun on the connection before Release; whether one left open is rolled back or discarded with the session is driver-specific (the database/sql adapter rolls it back; the pgx pool adapter discards the session).

type Pinner

type Pinner interface {
	Pin(ctx context.Context) (PinnedConn, error)
}

Pinner is implemented by drivers that can pin one connection.

type PoolStater

type PoolStater interface {
	PoolStats() PoolStats
}

PoolStater is implemented by drivers that expose pool statistics.

type PoolStats

type PoolStats struct {
	MaxConns           int
	TotalConns         int
	AcquiredConns      int
	IdleConns          int
	AcquireCount       int64
	EmptyAcquireCount  int64
	EmptyAcquireWaitNS int64
}

PoolStats is a snapshot of a driver's connection pool.

type Predicate

type Predicate = Expression[bool]

Predicate is a boolean expression usable in WHERE, ON and HAVING.

func And

func And(ps ...Predicate) Predicate

And joins predicates with AND; an empty And is true.

func ContainsLiteral

func ContainsLiteral(e Expression[string], s string) Predicate

ContainsLiteral matches rows whose string expression contains s literally. s is escaped with EscapeLike, wrapped in % and bound as a parameter; the statement carries ESCAPE '!' on every dialect. Case sensitivity follows the dialect and the column collation (PostgreSQL LIKE is case-sensitive, SQLite LIKE is ASCII case-insensitive, MySQL depends on the collation).

func Exists

func Exists(q Subquery) Predicate

Exists is true when the subquery yields any row.

func ILike

func ILike(e Expression[string], pattern string) Predicate

ILike is PostgreSQL's case-insensitive LIKE. On SQLite, whose LIKE is already ASCII case-insensitive by default, it is ErrUnsupported to avoid implying Unicode case folding SQLite does not perform.

func Like

func Like(e Expression[string], pattern string) Predicate

Like matches a string expression against a LIKE pattern. The pattern is bound as a parameter; escape wildcards in user input yourself.

func Not

func Not(p Predicate) Predicate

Not negates a predicate.

func NotExists

func NotExists(q Subquery) Predicate

NotExists is true when the subquery yields no row.

func Or

func Or(ps ...Predicate) Predicate

Or joins predicates with OR; an empty Or is false.

func SQL

func SQL(sql string, args ...any) Predicate

SQL is an explicit escape hatch: a raw SQL fragment usable anywhere an expression or predicate is accepted.

Safety contract: sql is inserted verbatim and must be a trusted constant. Every '?' is replaced by a bound parameter for the next argument (an Expr argument is rendered inline instead). Write "??" for a literal '?'. Never build sql from user input; pass user input as arguments.

Raw arguments: In(slice) expands to a bind list (an empty slice is a build error), Ident(name) renders a quoted identifier, and @name binds Named(name, value). A ? or @name inside a string literal, quoted identifier, comment or dollar-quoted string is text, as are the PostgreSQL operators ?| ?& @? @@ @>.

type Projection

type Projection struct{}

Projection is embedded in a struct to declare it a result type for typed projections. Its fields are scanned positionally from the select list.

type Querier

type Querier interface {
	Dialect() *dialect.Dialect
	// contains filtered or unexported methods
}

Querier is implemented by *DB and *Tx. Query builders take a Querier so the same code runs inside or outside a transaction. It is sealed.

type QueryEvent

type QueryEvent struct {
	Op    string // "select", "insert", "update", "delete", "exec", "query", "begin", "commit", "rollback", "savepoint"
	Table string // model table when known
	// Relation is set while eager-loading a relation ("documents.author").
	Relation string
	SQL      string
	// Args is nil unless the DB was opened with WithArgLogging. Treat
	// parameter values as sensitive.
	Args []any
	// NumArgs is always set: the number of bound parameters.
	NumArgs int
	// InTx reports that the statement ran on a *Tx.
	InTx bool
	// PoolWait is the summed RecordPoolWait for this statement.
	PoolWait time.Duration
	// Dialect is the dialect of the DB the statement ran on.
	Dialect *dialect.Dialect
	// Querier is the *DB or *Tx the statement ran on.
	Querier Querier
	Start   time.Time
	// Duration is filled for AfterQuery. For reads it covers execution
	// and iteration until the rows are closed.
	Duration time.Duration
	// Rows is the number of rows read (queries) or affected (exec);
	// -1 when unknown.
	Rows int64
	Err  error
	// contains filtered or unexported fields
}

QueryEvent describes one statement for observability hooks.

type ReadOnlyColumn

type ReadOnlyColumn[T any] struct {
	// contains filtered or unexported fields
}

ReadOnlyColumn is the descriptor of a read-only column (DB generated or computed). It has predicate and ordering methods only; no write method exists, so a Set on it does not compile.

func NewReadOnlyColumn

func NewReadOnlyColumn[T any](table, name string, flags uint16) ReadOnlyColumn[T]

NewReadOnlyColumn is used by generated code.

func (ReadOnlyColumn[T]) AppendSQL

func (c ReadOnlyColumn[T]) AppendSQL(b *Builder)

func (ReadOnlyColumn[T]) As

func (c ReadOnlyColumn[T]) As(alias string) ReadOnlyColumn[T]

As returns the same column qualified by a table alias.

func (ReadOnlyColumn[T]) AsExpr

func (c ReadOnlyColumn[T]) AsExpr() Expression[T]

func (ReadOnlyColumn[T]) Asc

func (c ReadOnlyColumn[T]) Asc() Order

func (ReadOnlyColumn[T]) Between

func (c ReadOnlyColumn[T]) Between(lo, hi T) Predicate

func (ReadOnlyColumn[T]) ColumnName

func (c ReadOnlyColumn[T]) ColumnName() string

func (ReadOnlyColumn[T]) Desc

func (c ReadOnlyColumn[T]) Desc() Order

func (ReadOnlyColumn[T]) Eq

func (c ReadOnlyColumn[T]) Eq(v T) Predicate

func (ReadOnlyColumn[T]) EqExpr

func (c ReadOnlyColumn[T]) EqExpr(e Expression[T]) Predicate

func (ReadOnlyColumn[T]) Excluded added in v0.2.0

func (c ReadOnlyColumn[T]) Excluded() Expression[T]

Excluded is the value proposed for insertion, for an upsert condition.

func (ReadOnlyColumn[T]) Flags

func (c ReadOnlyColumn[T]) Flags() uint16

func (ReadOnlyColumn[T]) Ge

func (c ReadOnlyColumn[T]) Ge(v T) Predicate

func (ReadOnlyColumn[T]) GeExpr

func (c ReadOnlyColumn[T]) GeExpr(e Expression[T]) Predicate

func (ReadOnlyColumn[T]) Gt

func (c ReadOnlyColumn[T]) Gt(v T) Predicate

func (ReadOnlyColumn[T]) GtExpr

func (c ReadOnlyColumn[T]) GtExpr(e Expression[T]) Predicate

func (ReadOnlyColumn[T]) In

func (c ReadOnlyColumn[T]) In(vs ...T) Predicate

func (ReadOnlyColumn[T]) InQuery

func (c ReadOnlyColumn[T]) InQuery(q Subquery) Predicate

func (ReadOnlyColumn[T]) IsDistinctFrom

func (c ReadOnlyColumn[T]) IsDistinctFrom(v T) Predicate

func (ReadOnlyColumn[T]) IsNotDistinctFrom

func (c ReadOnlyColumn[T]) IsNotDistinctFrom(v T) Predicate

func (ReadOnlyColumn[T]) IsNotNull

func (c ReadOnlyColumn[T]) IsNotNull() Predicate

func (ReadOnlyColumn[T]) IsNull

func (c ReadOnlyColumn[T]) IsNull() Predicate

func (ReadOnlyColumn[T]) Le

func (c ReadOnlyColumn[T]) Le(v T) Predicate

func (ReadOnlyColumn[T]) LeExpr

func (c ReadOnlyColumn[T]) LeExpr(e Expression[T]) Predicate

func (ReadOnlyColumn[T]) Lt

func (c ReadOnlyColumn[T]) Lt(v T) Predicate

func (ReadOnlyColumn[T]) LtExpr

func (c ReadOnlyColumn[T]) LtExpr(e Expression[T]) Predicate

func (ReadOnlyColumn[T]) Ne

func (c ReadOnlyColumn[T]) Ne(v T) Predicate

func (ReadOnlyColumn[T]) NeExpr

func (c ReadOnlyColumn[T]) NeExpr(e Expression[T]) Predicate

func (ReadOnlyColumn[T]) NotIn

func (c ReadOnlyColumn[T]) NotIn(vs ...T) Predicate

func (ReadOnlyColumn[T]) NotInQuery

func (c ReadOnlyColumn[T]) NotInQuery(q Subquery) Predicate

func (ReadOnlyColumn[T]) Table

func (c ReadOnlyColumn[T]) Table() string

type ReadOnlyVectorColumn

type ReadOnlyVectorColumn[V VectorValue] struct {
	// contains filtered or unexported fields
}

ReadOnlyVectorColumn is a read-only vector column (database generated or computed). It can be searched and filtered but has no write method, so a Set on it does not compile. Nullability comes from the flags.

func NewReadOnlyVectorColumn

func NewReadOnlyVectorColumn[V VectorValue](table, name string, dim int, flags uint16) ReadOnlyVectorColumn[V]

NewReadOnlyVectorColumn is used by generated code.

func (ReadOnlyVectorColumn) AppendSQL

func (c ReadOnlyVectorColumn) AppendSQL(b *Builder)

AppendSQL renders the qualified, quoted column.

func (ReadOnlyVectorColumn[V]) As

As returns the same column qualified by a table alias.

func (ReadOnlyVectorColumn) AsExpr

func (c ReadOnlyVectorColumn) AsExpr() Expression[V]

AsExpr returns the column as an Expression[V].

func (ReadOnlyVectorColumn) Asc

func (c ReadOnlyVectorColumn) Asc() Order

func (ReadOnlyVectorColumn) ColumnName

func (c ReadOnlyVectorColumn) ColumnName() string

ColumnName returns the unquoted column name.

func (ReadOnlyVectorColumn) Desc

func (c ReadOnlyVectorColumn) Desc() Order

func (ReadOnlyVectorColumn) Dim

func (c ReadOnlyVectorColumn) Dim() int

Dim returns the declared dimension (0 when unchecked).

func (ReadOnlyVectorColumn) Eq

func (c ReadOnlyVectorColumn) Eq(v V) Predicate

func (ReadOnlyVectorColumn) EqExpr

func (c ReadOnlyVectorColumn) EqExpr(e Expression[V]) Predicate

func (ReadOnlyVectorColumn) Flags

func (c ReadOnlyVectorColumn) Flags() uint16

Flags returns the generated column flags.

func (ReadOnlyVectorColumn) In

func (c ReadOnlyVectorColumn) In(vs ...V) Predicate

func (ReadOnlyVectorColumn) InQuery

func (c ReadOnlyVectorColumn) InQuery(q Subquery) Predicate

func (ReadOnlyVectorColumn) IsDistinctFrom

func (c ReadOnlyVectorColumn) IsDistinctFrom(v V) Predicate

IsDistinctFrom is a NULL-safe inequality.

func (ReadOnlyVectorColumn) IsNotDistinctFrom

func (c ReadOnlyVectorColumn) IsNotDistinctFrom(v V) Predicate

IsNotDistinctFrom is a NULL-safe equality.

func (ReadOnlyVectorColumn) IsNotNull

func (c ReadOnlyVectorColumn) IsNotNull() Predicate

IsNotNull is true when the column holds a value.

func (ReadOnlyVectorColumn) IsNull

func (c ReadOnlyVectorColumn) IsNull() Predicate

IsNull is true when the column holds SQL NULL.

func (ReadOnlyVectorColumn) Ne

func (c ReadOnlyVectorColumn) Ne(v V) Predicate

func (ReadOnlyVectorColumn) NeExpr

func (c ReadOnlyVectorColumn) NeExpr(e Expression[V]) Predicate

func (ReadOnlyVectorColumn) NotIn

func (c ReadOnlyVectorColumn) NotIn(vs ...V) Predicate

func (ReadOnlyVectorColumn) NotInQuery

func (c ReadOnlyVectorColumn) NotInQuery(q Subquery) Predicate

func (ReadOnlyVectorColumn) Table

func (c ReadOnlyVectorColumn) Table() string

Table returns the qualifier (table name or alias).

type Relation

type Relation[P, C any] struct {
	Name          string
	Kind          RelationKind
	Parent        TableRef
	Child         TableRef
	ParentMeta    *Meta[P]
	ChildMeta     *Meta[C]
	ParentCols    []string
	ChildCols     []string
	ChildPK       []string
	Through       *Through
	Discriminator *Discriminator
	ParentKey     func(p *P) ([]any, bool)
	ChildKey      func(c *C) ([]any, bool)
	SetParentKey  func(p *P, key []any)
	SetChildKey   func(c *C, key []any)
	GetOne        func(p *P) *C
	SetOne        func(p *P, c *C)
	GetMany       func(p *P) []C
	SetMany       func(p *P, cs []C)
}

Relation describes relation Name from parent P to child C. Generated code emits one value per relation field. Relation values are immutable and safe for concurrent use.

func (Relation[P, C]) Count

func (r Relation[P, C]) Count(ctx context.Context, q Querier, p *P) (int64, error)

Count returns the number of children associated with p in one SELECT COUNT(*). A NULL parent key counts zero. For ManyToMany it counts join rows (joined to the far table only when that table has a soft-delete column, so soft-deleted children are not counted). BelongsTo returns ErrUnsupported.

func (Relation[P, C]) CountOrder

func (r Relation[P, C]) CountOrder(dir Direction) Orderable

CountOrder orders parents by the number of related children, using a correlated COUNT(*) subquery.

func (Relation[P, C]) From

func (r Relation[P, C]) From(parents SelectQuery[P]) SelectQuery[C]

From returns the children whose key is in the parents' key set. The parents query is embedded as a subquery, so no parent row is fetched. Many-to-many relations are not supported by From; the returned query fails with ErrValidation.

func (Relation[P, C]) Has

func (r Relation[P, C]) Has(scope ...Predicate) Predicate

Has returns EXISTS over the child table, correlated with the parent. The predicate is a subquery, never a JOIN, so it cannot duplicate parent rows.

Scope predicates must reference the child through the alias "momo_r": build them with As on the generated column, for example comment.Approved.As("momo_r").Eq(true). Soft-deleted children do not count. For many-to-many relations the join table gets no soft-delete filter: only the child table's marker is applied.

Example

Has filters parents by a correlated EXISTS subquery. Scope predicates name the child through the alias "momo_r".

package main

import (
	"fmt"

	"github.com/azrtydxb/momo"
	"github.com/azrtydxb/momo/driver/pgxdriver"
	"github.com/azrtydxb/momo/internal/relfixture/comment"
	"github.com/azrtydxb/momo/internal/relfixture/document"
	"github.com/azrtydxb/momo/internal/relfixture/models"
)

func main() {
	db := momo.New(pgxdriver.New(nil))
	c, err := momo.Select[models.Document](db).
		Where(document.Comments.Has(comment.Approved.As("momo_r").Eq(true))).
		OrderBy(document.Comments.CountOrder(momo.DirDesc)).
		SQL()
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(c.SQL != "")
}
Output:
true

func (Relation[P, C]) HasFrom

func (r Relation[P, C]) HasFrom(parentAlias string, scope ...Predicate) Predicate

HasFrom is Has correlated on an explicit parent alias. Self-referencing relations need it: the parent table is aliased in the outer query and the alias is passed here.

func (Relation[P, C]) HasNo

func (r Relation[P, C]) HasNo(scope ...Predicate) Predicate

HasNo returns NOT EXISTS over the child table. Scope predicates follow the rules of Has.

func (Relation[P, C]) Include

func (r Relation[P, C]) Include() Association[P]

Include lists the relation for CreateGraph: the children held in the parent's field are inserted in the same transaction. Only the listed relation is written; the children's own relations are not (a graph is one level deep), so a self-referencing relation or a cycle in memory cannot recurse.

func (Relation[P, C]) Joined

func (r Relation[P, C]) Joined() Loader[P]

Joined returns a loader that fetches the parent's belongs-to child in the parent statement itself, as a LEFT JOIN with aliased columns (momo_j<n>), so the relation costs no extra round trip. Pass it to SelectQuery.Preload; Load, LoadAll, Scope and Then do not take it.

Only belongs-to relations are joinable, and only when the relation's child key is the child's primary key or a single unique column, because then the join cannot multiply parent rows: Limit, Offset, OrderBy and Where of the parent query keep their meaning. has-one, has-many and many-to-many fail with ErrUnsupported (Op "select") when the query runs, as do GroupBy, Having and row locking on the same query (FOR UPDATE cannot lock the nullable side of an outer join), and Each and Iter.

Semantics match Load for belongs-to: a NULL (or part-NULL composite) foreign key, or a key matching no child, leaves the field nil, never a zero struct; children are excluded when soft-deleted, by a condition in the ON clause so the parent row survives; each parent gets its own copy of a shared child. The select list names parent columns by their qualifier, so a raw unqualified column in the parent's Where may become ambiguous.

A joined load runs no statement of its own, so it emits no RelationEvent; the parent statement is reported to Hook as usual. Loaders for other relations in the same Preload still load as separate statements.

func (r Relation[P, C]) Link(ctx context.Context, q Querier, p *P, cs ...*C) error

Link associates the listed children with p: HasOne and HasMany set the children's foreign key (UPDATE), ManyToMany inserts the join rows (INSERT, existing pairs are kept; the join columns need a unique or primary key). Sets larger than the bind limit are split into one statement per chunk. It writes directly: no model hooks, field hooks or UpdatedAt. BelongsTo returns ErrUnsupported.

func (Relation[P, C]) Replace

func (r Relation[P, C]) Replace(ctx context.Context, q Querier, p *P, cs ...*C) error

Replace makes the listed children the complete set associated with p, in one transaction (a savepoint on a *Tx). It first locks the parent row (SELECT 1 FROM parent WHERE key = ? FOR UPDATE) on dialects with row locks (PostgreSQL, MySQL), so concurrent Replace calls on the same parent run one after the other and each reads the set the previous one committed; without that, two callers under READ COMMITTED could produce unique violations, duplicate join rows or a mix of both sets. SQLite serialises writers and skips the lock. On a *Tx the lock is held until the caller's transaction ends, because the savepoint does not release it. The lock is a row lock, so a parent row that does not exist locks nothing: concurrent Replace calls on a missing parent are not serialised. Link and Unlink take no parent lock, so they can interleave with a concurrent Replace on the same parent. With no children it clears the whole set in one statement. Otherwise it reads the current child keys, diffs them in Go against cs, unlinks only the removed keys and links only the added keys, each in chunks that respect the bind limit, so a set of any size works and kept children are not rewritten. It writes directly: no model hooks, field hooks or UpdatedAt. HasOne/HasMany need a nullable foreign key. BelongsTo returns ErrUnsupported. Replace needs a transaction, so a *DB pinned with OnConn fails with ErrUnsupported (Op "begin"); run it on the pool or inside a *Tx. Link, Unlink and Count accept a pinned handle.

Example

The association helpers write the link between a parent and its children.

package main

import (
	"context"
	"fmt"

	"github.com/azrtydxb/momo"
	"github.com/azrtydxb/momo/driver/pgxdriver"
	"github.com/azrtydxb/momo/internal/relfixture/document"
	"github.com/azrtydxb/momo/internal/relfixture/models"
)

func main() {
	var (
		ctx  = context.Background()
		db   = momo.New(pgxdriver.New(nil))
		doc  = models.Document{ID: 1}
		tags = []*models.Tag{{ID: 1}, {ID: 2}}
	)
	_ = document.Tags.Link(ctx, db, &doc, tags...)
	_ = document.Tags.Unlink(ctx, db, &doc, tags[0])
	err := document.Tags.Replace(ctx, db, &doc, tags[1])
	n, cerr := document.Tags.Count(ctx, db, &doc)
	fmt.Println(err, n, cerr)
}

func (Relation[P, C]) Scope

func (r Relation[P, C]) Scope(opts ...LoadOption[C]) Loader[P]

Scope returns a loader for r with per-load options.

func (Relation[P, C]) Then

func (r Relation[P, C]) Then(nested ...Loader[C]) Loader[P]

Then returns a loader for r that also loads nested relations on the children.

func (r Relation[P, C]) Unlink(ctx context.Context, q Querier, p *P, cs ...*C) error

Unlink removes the association between p and the listed children: HasOne and HasMany clear the children's foreign key (UPDATE; the key columns must be nullable), ManyToMany deletes the join rows (DELETE). Children that are not associated with p are left alone. Sets larger than the bind limit are split into one statement per chunk. It writes directly: no model hooks, field hooks or UpdatedAt. BelongsTo returns ErrUnsupported.

type RelationEvent

type RelationEvent struct {
	Parent   string
	Name     string
	Kind     RelationKind
	Parents  int
	Children int64
	Queries  int
	Duration time.Duration
	Err      error
}

RelationEvent describes one relation load. Queries is the number of child statements compiled for the load (a failed load may have run fewer). Children is set only when the load's statements succeeded; on error it is zero and Err holds the error. Duration is filled before AfterRelation.

Events fire only for loads that execute statements: not when there are no parents, and not when the load fails while being prepared (validation, unsupported options), before any statement runs.

type RelationHook

type RelationHook interface {
	BeforeRelation(ctx context.Context, e *RelationEvent) context.Context
	AfterRelation(ctx context.Context, e *RelationEvent)
}

RelationHook is an optional extension of Hook. Hooks registered with WithHook that implement it receive relation events.

type RelationKind

type RelationKind uint8

RelationKind is the shape of a relation.

const (
	// BelongsTo has its foreign key on the parent; the child is matched by its key.
	BelongsTo RelationKind = iota + 1
	// HasOne has its foreign key on the child; at most one child per parent.
	HasOne
	// HasMany has its foreign key on the child; any number of children per parent.
	HasMany
	// ManyToMany goes through a join table between parent and child.
	ManyToMany
)

func (RelationKind) String

func (k RelationKind) String() string

type Result

type Result struct{ RowsAffected int64 }

Result reports the outcome of a write statement.

type ReturningList

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

ReturningList is a RETURNING column list shared by the write builders.

func Returning

func Returning(cols ...ColumnRef) ReturningList

Returning builds a RETURNING list from column descriptors.

type Rows

type Rows interface {
	Next() bool
	Scan(dest ...any) error
	Err() error
	Close() error
}

Rows is a forward-only result cursor. Close must be safe to call more than once.

type SearchSettings

type SearchSettings struct {
	// ExactSearch disables index scans and bitmap scans for the rest of the
	// transaction (enable_indexscan = off, enable_bitmapscan = off) so vector
	// ORDER BY queries scan the table and return exact nearest neighbours.
	// The switches are not specific to vector indexes: primary-key, join and
	// every other index lookup in the same transaction also fall back to
	// sequential scans. Keep the transaction short and dedicated to the exact
	// query.
	ExactSearch          bool
	HNSWEfSearch         int           // hnsw.ef_search, 1..1000
	HNSWIterativeScan    IterativeScan // hnsw.iterative_scan (pgvector >= 0.8)
	HNSWMaxScanTuples    int           // hnsw.max_scan_tuples, 1..2147483647 (pgvector >= 0.8; server default 20000)
	IVFFlatProbes        int           // ivfflat.probes, 1..32768
	IVFFlatIterativeScan IterativeScan // ivfflat.iterative_scan: off or relaxed_order only (pgvector >= 0.8)
	IVFFlatMaxProbes     int           // ivfflat.max_probes, 1..32768 (pgvector >= 0.8)
}

SearchSettings applies pgvector session settings for one transaction only. Zero values mean "do not change the server default".

HNSWIterativeScan, HNSWMaxScanTuples, IVFFlatIterativeScan and IVFFlatMaxProbes need pgvector 0.8.0 or newer. SetSearch checks the installed extension version when any of them is set and returns ErrUnsupported on an older one, because an older server may accept the unknown setting as a placeholder or fail with a generic error instead of a typed one.

func (SearchSettings) Validate

func (s SearchSettings) Validate() error

Validate reports a setting outside its pgvector range. The error wraps ErrValidation.

type SelectQuery

type SelectQuery[T any] struct {
	// contains filtered or unexported fields
}

SelectQuery builds and runs a SELECT whose rows scan into T.

SelectQuery is an immutable value: every method returns a new query and leaves the receiver unchanged, so a base query can be extended concurrently from many goroutines. Executing a query does not modify it.

func Select

func Select[T any, PT Modeler[T]](q Querier) SelectQuery[T]

Select starts a query for model or projection T:

momo.Select[models.Document](db).Where(document.Published.Eq(true)).All(ctx)

For a model, FROM and the select list default to the model's table and mapped columns. For a projection, call From and Columns.

func (SelectQuery[T]) After

func (sq SelectQuery[T]) After(values ...any) SelectQuery[T]

After restricts the result to rows strictly after the cursor given by values, one per effective ORDER BY key in order. The effective keys are the OrderBy items followed by the primary key ascending when it is not already an item, which makes the order total. Ascending keys compare with >, descending keys with <. After needs OrderBy, a value count equal to the number of effective keys (ErrValidation otherwise), and non-nullable keys (ErrUnsupported otherwise).

func (SelectQuery[T]) All

func (sq SelectQuery[T]) All(ctx context.Context) ([]T, error)

All runs the query and collects every row. An empty result is an empty (nil) slice and a nil error, never ErrNotFound.

func (SelectQuery[T]) AppendSQL

func (sq SelectQuery[T]) AppendSQL(b *Builder)

AppendSQL renders the query in parentheses, for use as a subquery.

func (SelectQuery[T]) Columns

func (sq SelectQuery[T]) Columns(es ...Expr) SelectQuery[T]

Columns replaces the select list. For model results the expressions must match the model's mapped columns one-to-one; for projections they map to the projection's fields in order.

func (SelectQuery[T]) Count

func (sq SelectQuery[T]) Count(ctx context.Context) (int64, error)

Count runs SELECT COUNT(*) over this query (as a subquery, so DISTINCT, GROUP BY, LIMIT and OFFSET keep their meaning).

func (SelectQuery[T]) CrossJoin

func (sq SelectQuery[T]) CrossJoin(src TableSource) SelectQuery[T]

CrossJoin adds a CROSS JOIN.

func (SelectQuery[T]) Distinct

func (sq SelectQuery[T]) Distinct() SelectQuery[T]

Distinct adds DISTINCT.

func (SelectQuery[T]) Each

func (sq SelectQuery[T]) Each(ctx context.Context, fn func(*T) error) error

Each streams rows to fn without collecting them. The *T passed to fn is reused for the next row; copy it to retain it. Returning an error from fn stops iteration, closes the cursor and returns that error.

func (SelectQuery[T]) Exists

func (sq SelectQuery[T]) Exists(ctx context.Context) (bool, error)

Exists reports whether the query yields any row.

func (SelectQuery[T]) Explain

func (sq SelectQuery[T]) Explain(ctx context.Context) (string, error)

Explain runs EXPLAIN (never EXPLAIN ANALYZE) for the query and returns the plan text, one line per row joined by newlines. Parameters are bound, never interpolated.

EXPLAIN plans the statement without executing it, so Explain has no side effects on data. It runs on the query's own connection, so inside a Tx the plan reflects that transaction's settings (for example SetLocal planner switches). PostgreSQL uses EXPLAIN and SQLite uses EXPLAIN QUERY PLAN. MySQL and MariaDB return ErrUnsupported: their tabular EXPLAIN has no single plan column.

Example

Explain returns the plan text without executing the query, so a test can check that a vector query uses its index.

package main

import (
	"context"
	"fmt"

	"github.com/azrtydxb/momo"
	"github.com/azrtydxb/momo/internal/testmodels/example"
	"github.com/azrtydxb/momo/internal/testmodels/example/document"
	"github.com/azrtydxb/momo/vector"
)

func main() {
	var db *momo.DB
	ctx := context.Background()

	plan, err := momo.Select[example.Document](db).
		OrderBy(momo.L2Distance(document.Embedding, make(vector.Vector, 1536)).Asc()).
		Limit(10).
		Explain(ctx)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(plan) // look for "Index Scan using documents_embedding_hnsw"
}

func (SelectQuery[T]) First

func (sq SelectQuery[T]) First(ctx context.Context) (T, error)

First applies LIMIT 1 and returns the first row, or ErrNotFound.

func (SelectQuery[T]) ForShare

func (sq SelectQuery[T]) ForShare(opt ...string) SelectQuery[T]

ForShare adds FOR SHARE (PostgreSQL).

func (SelectQuery[T]) ForUpdate

func (sq SelectQuery[T]) ForUpdate(opt ...string) SelectQuery[T]

ForUpdate adds FOR UPDATE (PostgreSQL). Options: "NOWAIT", "SKIP LOCKED".

func (SelectQuery[T]) From

func (sq SelectQuery[T]) From(src TableSource) SelectQuery[T]

From replaces the FROM source (a table, aliased table, CTE or subquery).

func (SelectQuery[T]) GroupBy

func (sq SelectQuery[T]) GroupBy(es ...Expr) SelectQuery[T]

GroupBy sets GROUP BY expressions.

func (SelectQuery[T]) Having

func (sq SelectQuery[T]) Having(ps ...Predicate) SelectQuery[T]

Having adds HAVING predicates combined with AND.

func (SelectQuery[T]) IndexedBy

func (sq SelectQuery[T]) IndexedBy(index string) SelectQuery[T]

IndexedBy renders SQLite's INDEXED BY hint after the FROM table. The index name is quoted, never spliced. PostgreSQL and MySQL have no such clause and return ErrUnsupported when the query is built. The FROM source must be a plain table (TableRef).

func (SelectQuery[T]) Iter

func (sq SelectQuery[T]) Iter(ctx context.Context) iter.Seq2[T, error]

Iter streams rows as a range-over-func sequence. Breaking out of the loop closes the cursor. An error is yielded once, as the final element.

func (SelectQuery[T]) Join

func (sq SelectQuery[T]) Join(src TableSource, on ...Predicate) SelectQuery[T]

Join adds an INNER JOIN.

func (SelectQuery[T]) LeftJoin

func (sq SelectQuery[T]) LeftJoin(src TableSource, on ...Predicate) SelectQuery[T]

LeftJoin adds a LEFT JOIN. Columns of src may be NULL in the result: project them into nullable fields (pointer or sql.Null types). Scanning a NULL into a non-nullable field fails with a driver error; it never silently yields a zero value.

func (SelectQuery[T]) Limit

func (sq SelectQuery[T]) Limit(n int64) SelectQuery[T]

Limit sets LIMIT.

func (SelectQuery[T]) Offset

func (sq SelectQuery[T]) Offset(n int64) SelectQuery[T]

Offset sets OFFSET. Prefer keyset pagination (After) for deep pages.

func (SelectQuery[T]) One

func (sq SelectQuery[T]) One(ctx context.Context) (T, error)

One returns the single matching row: ErrNotFound when none matches and ErrTooManyRows when more than one does. It adds no LIMIT.

func (SelectQuery[T]) OnlyDeleted

func (sq SelectQuery[T]) OnlyDeleted() SelectQuery[T]

OnlyDeleted narrows a select to soft-deleted rows of a model with a soft-delete column. A later WithDeleted overrides it.

func (SelectQuery[T]) OrderBy

func (sq SelectQuery[T]) OrderBy(items ...Orderable) SelectQuery[T]

OrderBy appends ORDER BY items. Pass Column.Asc()/Desc(), or a typed expression (e.g. a vector distance), which orders ascending.

func (SelectQuery[T]) Preload

func (sq SelectQuery[T]) Preload(loaders ...Loader[T]) SelectQuery[T]

Preload returns a copy of the query that loads the given relations for every row that All, One and First return. The receiver is unchanged, and queries derived from one base never share their preload lists.

The relations load with LoadAll after the parent rows are read: one statement per relation (per bind-limit chunk), run on the query's Querier, so inside a transaction they run in that transaction. A result with no rows issues no relation statement, and an error from a relation load is returned instead of the rows. Relation hooks fire as they do for LoadAll.

Loaders from Relation.Joined are not loaded afterwards but joined into the query itself; see Joined for their rules.

Each and Iter stream rows without collecting them, so they cannot batch relation loads: with preloads they return an error matching ErrUnsupported.

Example

Preload runs the loads after the query's rows are read; Joined fetches a belongs-to relation in the parent statement itself.

package main

import (
	"context"
	"fmt"

	"github.com/azrtydxb/momo"
	"github.com/azrtydxb/momo/driver/pgxdriver"
	"github.com/azrtydxb/momo/internal/relfixture/document"
	"github.com/azrtydxb/momo/internal/relfixture/models"
)

func main() {
	var (
		ctx = context.Background()
		db  = momo.New(pgxdriver.New(nil))
	)
	docs, err := momo.Select[models.Document](db).
		Preload(document.Author.Joined(), document.Comments).
		Limit(20).
		All(ctx)
	fmt.Println(docs, err)
}

func (SelectQuery[T]) SQL

func (sq SelectQuery[T]) SQL() (Compiled, error)

SQL compiles the query without executing it.

func (SelectQuery[T]) Where

func (sq SelectQuery[T]) Where(ps ...Predicate) SelectQuery[T]

Where adds predicates combined with AND.

func (SelectQuery[T]) With

func (sq SelectQuery[T]) With(name string, q Subquery, cols ...string) SelectQuery[T]

With adds a common table expression. cols optionally names its columns.

func (SelectQuery[T]) WithDeleted

func (sq SelectQuery[T]) WithDeleted() SelectQuery[T]

WithDeleted includes soft-deleted rows for models with a soft-delete column (they are excluded by default).

func (SelectQuery[T]) WithRecursive

func (sq SelectQuery[T]) WithRecursive(name string, q Subquery, cols ...string) SelectQuery[T]

WithRecursive adds a recursive CTE; q is typically a UNION ALL built with SQL or Union.

func (SelectQuery[T]) WithTableName

func (sq SelectQuery[T]) WithTableName(name string) SelectQuery[T]

WithTableName replaces the FROM table name at runtime, for example to run one query shape over several identically shaped tables. The name is quoted; it is never spliced. The replacement is aliased to the model's table name, so the model's column expressions and the soft-delete filter keep resolving. It needs a plain table as the FROM source.

type ServerVersioner

type ServerVersioner interface {
	ServerVersion(ctx context.Context) (int, error)
}

ServerVersioner is implemented by drivers that report the server version (PostgreSQL server_version_num, e.g. 170002). MERGE gates read it.

type SoftDeleteKind

type SoftDeleteKind uint8

SoftDeleteKind is the type of a model's soft-delete marker column. The zero value is the nullable timestamp, so metadata that predates the field keeps working. Generated code records the kind from the field's Go type: *time.Time, *int64 or bool.

const (
	// SoftDeleteTimestamp is a nullable timestamp, set to the statement time.
	SoftDeleteTimestamp SoftDeleteKind = iota
	// SoftDeleteUnix is a nullable integer, set to the statement time in Unix seconds.
	SoftDeleteUnix
	// SoftDeleteBool is a boolean, set to true.
	SoftDeleteBool
)

type Subquery

type Subquery interface {
	Expr
	// contains filtered or unexported methods
}

Subquery is a query usable as an expression: IN (...), EXISTS (...), scalar subqueries and FROM sources. *SelectQuery satisfies it.

func RawSubquery

func RawSubquery(sql string, args ...any) Subquery

RawSubquery is an explicit escape hatch: a raw SELECT usable as a Subquery (IN, EXISTS, CTE body, set operation member, FromSubquery source).

Safety contract: as for SQL, sql is inserted verbatim and must be a trusted constant; '?' binds the next argument and "??" is a literal '?'. Momo cannot check which tables the fragment references. Only '?' placeholders bind; never write native '$1' style placeholders, which would collide with momo's own numbering. For a scalar subquery as a typed comparison operand use TypedSQL[T]("?", sub): the subquery renders its own parentheses.

func Union

func Union(all bool, qs ...Subquery) Subquery

Union combines queries with UNION (all=false) or UNION ALL (all=true). The result is a Subquery, usable in WithRecursive, FromSubquery and IN.

type TableRef

type TableRef struct {
	Name   string
	Alias  string
	Schema string
}

TableRef names a table source in FROM and JOIN clauses, optionally aliased. Generated per-model packages embed it in their Table type.

func CTE

func CTE(name string) TableRef

CTE references a common table expression (or any table not described by a model) by name. Reference its columns with ColumnOf.

func (TableRef) Qualifier

func (t TableRef) Qualifier() string

Qualifier returns the name columns of this source are qualified with. An unaliased schema-qualified table ("tenant"."documents") is exposed under its bare name, so its columns render as "documents"."id"; alias one side to join two same-named tables from different schemas.

func (TableRef) Source

func (t TableRef) Source() TableRef

Source returns the table reference itself; it satisfies TableSource.

type TableSource

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

TableSource is anything usable in FROM / JOIN.

func FromSubquery

func FromSubquery(q Subquery, alias string) TableSource

FromSubquery uses a subquery as a FROM/JOIN source under alias. Reference its columns with ColumnOf.

type Through

type Through struct {
	Table      TableRef
	ParentCols []string // join columns referencing Relation.ParentCols, same order
	ChildCols  []string // join columns referencing Relation.ChildCols, same order
}

Through names the join table of a ManyToMany relation.

type Tx

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

Tx is an explicit transaction handle. It is not safe for concurrent use: a transaction is bound to one connection, which processes one statement at a time.

func (*Tx) Commit

func (tx *Tx) Commit(ctx context.Context) error

Commit commits the transaction.

func (*Tx) Dialect

func (tx *Tx) Dialect() *dialect.Dialect

func (*Tx) Release

func (tx *Tx) Release(ctx context.Context, name string) error

Release releases a named savepoint.

func (*Tx) Rollback

func (tx *Tx) Rollback(ctx context.Context) error

Rollback aborts the transaction.

func (*Tx) RollbackTo

func (tx *Tx) RollbackTo(ctx context.Context, name string) error

RollbackTo rolls back to a named savepoint, keeping it defined.

func (*Tx) Savepoint

func (tx *Tx) Savepoint(ctx context.Context, name string) error

Savepoint creates a named savepoint.

func (*Tx) SetLocal

func (tx *Tx) SetLocal(ctx context.Context, name, value string) error

SetLocal sets a run-time parameter for the rest of this transaction only (set_config with is_local = true). Name and value are bound parameters, so the setting reverts at commit or rollback and never reaches a pooled connection, which makes it safe behind PgBouncer in transaction mode. It returns ErrUnsupported on dialects without transaction-scoped settings.

name must never come from untrusted input. It is bound, so it cannot inject SQL, but set_config can change security-relevant parameters such as role, session_authorization or search_path for the rest of the transaction.

func (*Tx) SetSearch

func (tx *Tx) SetSearch(ctx context.Context, s SearchSettings) error

SetSearch applies s to the rest of this transaction. Settings that need pgvector 0.8 return ErrUnsupported on an older extension.

Dialect behavior: on SQLite, ExactSearch alone is a no-op (SQLite has no approximate vector index to bypass, so every search is already exact) and any other non-zero field returns ErrUnsupported. On MySQL, SetLocal is unsupported, so any non-zero setting including ExactSearch alone returns ErrUnsupported; there is no transaction-scoped switch to apply.

Example

ExactSearch bypasses the vector index for the rest of the transaction so the result is the exact nearest neighbours.

package main

import (
	"context"
	"fmt"

	"github.com/azrtydxb/momo"
)

func main() {
	var db *momo.DB
	ctx := context.Background()

	err := db.Tx(ctx, momo.TxOptions{}, func(tx *momo.Tx) error {
		if err := tx.SetSearch(ctx, momo.SearchSettings{ExactSearch: true}); err != nil {
			return err
		}
		// Queries here scan the table. Keep this transaction short: the
		// planner switches also affect every other index lookup in it.
		return nil
	})
	fmt.Println(err)
}

func (*Tx) Tx

func (tx *Tx) Tx(ctx context.Context, fn func(tx *Tx) error) error

Tx runs fn inside a savepoint of this transaction: an error or panic rolls back to the savepoint (and the panic is re-raised); success releases it. The outer transaction stays open either way.

type TxOptions

type TxOptions struct {
	// Isolation is passed through to the driver. SQLite supports only
	// serializable semantics; any other non-default level is rejected
	// with ErrUnsupported rather than silently ignored.
	Isolation sql.IsolationLevel
	ReadOnly  bool
}

TxOptions configures a transaction.

type UpdateQuery

type UpdateQuery[T any] struct {
	// contains filtered or unexported fields
}

UpdateQuery builds an UPDATE of one model table. It is a value: every method returns a copy, so a base query can be reused safely.

Column identifiers come only from typed column descriptors and the model's MomoMeta, are always quoted, and every value is bound. A statement without a WHERE clause is refused with ErrUnfilteredMutation unless AllRows confirms it.

func SoftDelete

func SoftDelete[T any, PT Modeler[T]](q Querier) UpdateQuery[T]

SoftDelete returns an UpdateQuery that marks rows deleted instead of removing them. The marker follows the model's soft-delete column: a nullable timestamp (set to the statement time from the Go-side clock, WithClock or time.Now, bound as UTC; it is not the database's now()), a Unix integer (seconds) or a boolean (set true). Rows already soft-deleted are not touched, and updated_at is not modified.

It keeps the mutation guard: without a Where (or Model) that restricts the rows it fails with ErrUnfilteredMutation unless AllRows confirms it. Set is refused, and a model without a soft-delete column fails with ErrValidation. Model identifies the row by primary key only; no column is written from it.

func Update

func Update[T any, PT Modeler[T]](q Querier) UpdateQuery[T]

Update starts an UPDATE of T's table.

func (UpdateQuery[T]) AllRows

func (u UpdateQuery[T]) AllRows() UpdateQuery[T]

AllRows confirms that an UPDATE without WHERE is intended.

func (UpdateQuery[T]) Exec

func (u UpdateQuery[T]) Exec(ctx context.Context) (Result, error)

Exec runs the UPDATE. A version-checked Model update that matches no row looks the row up inside the same transaction (one is started when q is a *DB) to tell ErrOptimisticLock from ErrNotFound.

func (UpdateQuery[T]) Model

func (u UpdateQuery[T]) Model(m *T) UpdateQuery[T]

Model updates the row identified by m's primary key with m's writable column values. When the model has a version column the statement is version-checked (WHERE pk = ? AND version = ?, SET version = version + 1): a stale m fails with ErrOptimisticLock and a missing row with ErrNotFound. After a successful versioned update m's version field is advanced. The identity, read-only, create-only, immutable, soft-delete, created-at and updated-at columns are not written from m; use Set for explicit writes.

func (UpdateQuery[T]) Returning

func (u UpdateQuery[T]) Returning(cols ...ColumnRef) UpdateQuery[T]

Returning appends a RETURNING list; read the rows with Rows.

func (UpdateQuery[T]) ReturningAll

func (u UpdateQuery[T]) ReturningAll() UpdateQuery[T]

ReturningAll returns every column mapped by T, in metadata order.

func (UpdateQuery[T]) Rows

func (u UpdateQuery[T]) Rows(ctx context.Context) (*ModelRows[T], error)

Rows runs the UPDATE and returns its RETURNING rows. It fails with ErrValidation when no Returning list was set, and for a version-checked Model update, whose stale-row classification needs the affected-row count (use Exec for that).

func (UpdateQuery[T]) SQL

func (u UpdateQuery[T]) SQL() (Compiled, error)

SQL renders the statement without executing it. Guardrail violations are reported here, before anything can reach the database.

func (UpdateQuery[T]) Set

func (u UpdateQuery[T]) Set(assigns ...Assignment) UpdateQuery[T]

Set adds explicit column assignments. An assignment to a column that is read-only, identity, create-only or immutable in the model's metadata is refused with ErrValidation when the statement is built. A later assignment to the same column replaces an earlier one.

func (UpdateQuery[T]) Where

func (u UpdateQuery[T]) Where(ps ...Predicate) UpdateQuery[T]

Where adds predicates combined with AND.

type VectorColumn

type VectorColumn[V VectorValue] struct {
	// contains filtered or unexported fields
}

VectorColumn is a typed, writable vector column descriptor. Its dimension is known at generation time so distance arguments are validated at build time. It offers the same predicate, ordering and write methods as Column except the range comparisons.

Example

The metrics and index methods each dialect supports come from dialect.Dialect.Vector. A distance the dialect lacks fails at build time with ErrUnsupported.

package main

import (
	"fmt"

	"github.com/azrtydxb/momo/dialect"
)

func main() {
	for _, name := range []dialect.Name{dialect.Postgres, dialect.SQLite} {
		caps := dialect.For(name).Vector
		fmt.Println(name, caps.Metrics, caps.Indexes)
	}
}
Output:
postgres [l2 ip cosine l1 hamming jaccard] [hnsw ivfflat]
sqlite [l2 l1 cosine hamming] []

func NewVectorColumn

func NewVectorColumn[V VectorValue](table, name string, dim int, flags uint16) VectorColumn[V]

NewVectorColumn is used by generated code.

func (VectorColumn) AppendSQL

func (c VectorColumn) AppendSQL(b *Builder)

AppendSQL renders the qualified, quoted column.

func (VectorColumn[V]) As

func (c VectorColumn[V]) As(alias string) VectorColumn[V]

As returns the same column qualified by a table alias.

func (VectorColumn) AsExpr

func (c VectorColumn) AsExpr() Expression[V]

AsExpr returns the column as an Expression[V].

func (VectorColumn) Asc

func (c VectorColumn) Asc() Order

func (VectorColumn) ColumnName

func (c VectorColumn) ColumnName() string

ColumnName returns the unquoted column name.

func (VectorColumn) Desc

func (c VectorColumn) Desc() Order

func (VectorColumn) Dim

func (c VectorColumn) Dim() int

Dim returns the declared dimension (0 when unchecked).

func (VectorColumn) Eq

func (c VectorColumn) Eq(v V) Predicate

func (VectorColumn) EqExpr

func (c VectorColumn) EqExpr(e Expression[V]) Predicate

func (VectorColumn[V]) Excluded added in v0.2.0

func (c VectorColumn[V]) Excluded() Expression[V]

Excluded is the vector proposed for insertion in an upsert, rendered as EXCLUDED.<col>. See Column.Excluded.

func (VectorColumn) Flags

func (c VectorColumn) Flags() uint16

Flags returns the generated column flags.

func (VectorColumn) In

func (c VectorColumn) In(vs ...V) Predicate

func (VectorColumn) InQuery

func (c VectorColumn) InQuery(q Subquery) Predicate

func (VectorColumn) IsDistinctFrom

func (c VectorColumn) IsDistinctFrom(v V) Predicate

IsDistinctFrom is a NULL-safe inequality.

func (VectorColumn) IsNotDistinctFrom

func (c VectorColumn) IsNotDistinctFrom(v V) Predicate

IsNotDistinctFrom is a NULL-safe equality.

func (VectorColumn) IsNotNull

func (c VectorColumn) IsNotNull() Predicate

IsNotNull is true when the column holds a value.

func (VectorColumn) IsNull

func (c VectorColumn) IsNull() Predicate

IsNull is true when the column holds SQL NULL.

func (VectorColumn) Ne

func (c VectorColumn) Ne(v V) Predicate

func (VectorColumn) NeExpr

func (c VectorColumn) NeExpr(e Expression[V]) Predicate

func (VectorColumn) NotIn

func (c VectorColumn) NotIn(vs ...V) Predicate

func (VectorColumn) NotInQuery

func (c VectorColumn) NotInQuery(q Subquery) Predicate

func (VectorColumn[V]) Set

func (c VectorColumn[V]) Set(v V) Assignment

Set assigns a value in INSERT or UPDATE.

func (VectorColumn[V]) SetDefault

func (c VectorColumn[V]) SetDefault() Assignment

SetDefault assigns the column's database default.

func (VectorColumn[V]) SetExcluded

func (c VectorColumn[V]) SetExcluded() Assignment

SetExcluded assigns the value proposed for insertion in an upsert.

func (VectorColumn[V]) SetExpr

func (c VectorColumn[V]) SetExpr(e Expression[V]) Assignment

SetExpr assigns an expression.

func (VectorColumn) Table

func (c VectorColumn) Table() string

Table returns the qualifier (table name or alias).

type VectorSource

type VectorSource[V VectorValue] interface {
	// contains filtered or unexported methods
}

VectorSource is any vector column descriptor a distance builder accepts: VectorColumn and its nullable and permission variants (ReadOnlyVectorColumn, CreateOnlyVectorColumn, ImmutableVectorColumn). A write restriction does not stop a column from being searched.

type VectorValue

type VectorValue interface {
	vector.Vector | vector.HalfVector | vector.SparseVector | vector.Bits
}

VectorValue is the set of Go types a vector column can hold.

Directories

Path Synopsis
cmd
momo command
Command momo is the momo command line tool.
Command momo is the momo command line tool.
Package dialect describes the SQL language differences between the databases Momo supports.
Package dialect describes the SQL language differences between the databases Momo supports.
driver
pgxdriver
Package pgxdriver adapts jackc/pgx v5 to momo.
Package pgxdriver adapts jackc/pgx v5 to momo.
examples
adopt-existing-schema command
Command adopt-existing-schema reads a database that began as plain SQL (legacy.sql) and was then adopted with `momo migrate baseline`.
Command adopt-existing-schema reads a database that began as plain SQL (legacy.sql) and was then adopted with `momo migrate baseline`.
adopt-existing-schema/models
Package models describes a schema that already exists in a database.
Package models describes a schema that already exists in a database.
adopt-existing-schema/models/order
Package order holds typed column descriptors and relation descriptors for the Order model.
Package order holds typed column descriptors and relation descriptors for the Order model.
adopt-existing-schema/models/user
Package user holds typed column descriptors and relation descriptors for the User model.
Package user holds typed column descriptors and relation descriptors for the User model.
crud-zero-null command
Command crud-zero-null shows insert, select, update and delete, and above all how Momo tells a zero value from NULL from "leave it alone".
Command crud-zero-null shows insert, select, update and delete, and above all how Momo tells a zero value from NULL from "leave it alone".
crud-zero-null/models
Package models is the schema of the crud-zero-null example: one table of tasks that shows how a write tells zero from NULL from "not mentioned".
Package models is the schema of the crud-zero-null example: one table of tasks that shows how a write tells zero from NULL from "not mentioned".
crud-zero-null/models/task
Package task holds typed column descriptors and relation descriptors for the Task model.
Package task holds typed column descriptors and relation descriptors for the Task model.
custom-sql-migration command
Command custom-sql-migration looks accounts up by email, ignoring case, on a database whose migrations (generated and hand-written) are applied.
Command custom-sql-migration looks accounts up by email, ignoring case, on a database whose migrations (generated and hand-written) are applied.
custom-sql-migration/models
Package models is the schema of the custom-sql-migration example.
Package models is the schema of the custom-sql-migration example.
custom-sql-migration/models/account
Package account holds typed column descriptors and relation descriptors for the Account model.
Package account holds typed column descriptors and relation descriptors for the Account model.
internal/exampletest
Package exampletest is the harness every examples/<name> test uses.
Package exampletest is the harness every examples/<name> test uses.
projection-pagination command
Command projection-pagination shows how to read only the columns a screen needs, page through a large table with a keyset cursor, and count.
Command projection-pagination shows how to read only the columns a screen needs, page through a large table with a keyset cursor, and count.
projection-pagination/models
Package models is the schema of the projection-pagination example: a table of articles, plus two small result types that read only some of its columns.
Package models is the schema of the projection-pagination example: a table of articles, plus two small result types that read only some of its columns.
projection-pagination/models/article
Package article holds typed column descriptors and relation descriptors for the Article model.
Package article holds typed column descriptors and relation descriptors for the Article model.
quickstart command
Command quickstart is the compiled source of the snippets in the root README.
Command quickstart is the compiled source of the snippets in the root README.
quickstart/models
Package models is the schema of the quickstart: authors and their posts, each post carrying an embedding for similarity search.
Package models is the schema of the quickstart: authors and their posts, each post carrying an embedding for similarity search.
quickstart/models/author
Package author holds typed column descriptors and relation descriptors for the Author model.
Package author holds typed column descriptors and relation descriptors for the Author model.
quickstart/models/post
Package post holds typed column descriptors and relation descriptors for the Post model.
Package post holds typed column descriptors and relation descriptors for the Post model.
relations command
Command relations shows how to declare relations (see models/models.go), load them without N+1 queries, filter by them, change them and write whole graphs.
Command relations shows how to declare relations (see models/models.go), load them without N+1 queries, filter by them, change them and write whole graphs.
relations/models
Package models is the schema of the relations example: authors write documents, documents have comments and are tagged with many tags.
Package models is the schema of the relations example: authors write documents, documents have comments and are tagged with many tags.
relations/models/author
Package author holds typed column descriptors and relation descriptors for the Author model.
Package author holds typed column descriptors and relation descriptors for the Author model.
relations/models/comment
Package comment holds typed column descriptors and relation descriptors for the Comment model.
Package comment holds typed column descriptors and relation descriptors for the Comment model.
relations/models/document
Package document holds typed column descriptors and relation descriptors for the Document model.
Package document holds typed column descriptors and relation descriptors for the Document model.
relations/models/tag
Package tag holds typed column descriptors and relation descriptors for the Tag model.
Package tag holds typed column descriptors and relation descriptors for the Tag model.
transactions command
Command transactions shows how to group writes so they all happen or none do: db.Tx, rollback on error, savepoints for a step that may fail, read-only and isolation options, and a retry loop for serialization failures.
Command transactions shows how to group writes so they all happen or none do: db.Tx, rollback on error, savepoints for a step that may fail, read-only and isolation options, and a retry loop for serialization failures.
transactions/models
Package models is the schema of the transactions example: bank accounts whose balance the database refuses to take below zero.
Package models is the schema of the transactions example: bank accounts whose balance the database refuses to take below zero.
transactions/models/account
Package account holds typed column descriptors and relation descriptors for the Account model.
Package account holds typed column descriptors and relation descriptors for the Account model.
upgrade-database command
Command upgrade-database reads the customers of a database that was upgraded from schema v1 to the latest version with `momo migrate apply`.
Command upgrade-database reads the customers of a database that was upgraded from schema v1 to the latest version with `momo migrate apply`.
upgrade-database/models
Package models is the schema of the upgrade-database example, at its latest version.
Package models is the schema of the upgrade-database example, at its latest version.
upgrade-database/models/customer
Package customer holds typed column descriptors and relation descriptors for the Customer model.
Package customer holds typed column descriptors and relation descriptors for the Customer model.
vector-search command
Command vector-search shows nearest-neighbour search with pgvector: the column and HNSW index (see models/models.go and the migration), a query ordered by a distance, filters, a limit, and per-search settings.
Command vector-search shows nearest-neighbour search with pgvector: the column and HNSW index (see models/models.go and the migration), a query ordered by a distance, filters, a limit, and per-search settings.
vector-search/models
Package models is the schema of the vector-search example: items with an embedding, indexed for nearest-neighbour search.
Package models is the schema of the vector-search example: items with an embedding, indexed for nearest-neighbour search.
vector-search/models/item
Package item holds typed column descriptors and relation descriptors for the Item model.
Package item holds typed column descriptors and relation descriptors for the Item model.
gen
Package gen implements the momo code generator: it loads model packages with full type information, builds a model graph and emits code.
Package gen implements the momo code generator: it loads model packages with full type information, builds a model graph and emits code.
config
Package config reads momo.yaml and converts it into a gen.Config.
Package config reads momo.yaml and converts it into a gen.Config.
naming
Package naming maps Go model and field names to SQL identifiers through named presets.
Package naming maps Go model and field names to SQL identifiers through named presets.
tags
Package tags parses the `momo` struct tag grammar: a comma-separated list of `key` or `key:value` options.
Package tags parses the `momo` struct tag grammar: a comma-separated list of `key` or `key:value` options.
typemap
Package typemap maps the Go types the generator understands to SQL column types per dialect.
Package typemap maps the Go types the generator understands to SQL column types per dialect.
internal
fakedriver
Package fakedriver is a database-free momo.Driver used by unit tests.
Package fakedriver is a database-free momo.Driver used by unit tests.
golden
Package golden compares generated output with golden files.
Package golden compares generated output with golden files.
obstest
Package obstest is test support for the observability tests: a recording hook that keeps the whole QueryEvent.
Package obstest is test support for the observability tests: a recording hook that keeps the whole QueryEvent.
relfixture
Package relfixture is the shared relation test fixture: models, descriptors (see the momo-06 plan's generator contract) and seeded test databases.
Package relfixture is the shared relation test fixture: models, descriptors (see the momo-06 plan's generator contract) and seeded test databases.
relfixture/models
Package models holds the relation fixture's model structs.
Package models holds the relation fixture's model structs.
suite
Package suite holds the integration fixtures and the feature suites that every dialect's tests run.
Package suite holds the integration fixtures and the feature suites that every dialect's tests run.
testdb
Package testdb returns connection URLs and paths for integration tests.
Package testdb returns connection URLs and paths for integration tests.
testdb/testopen
Package testopen opens the integration-test databases as *momo.DB.
Package testopen opens the integration-test databases as *momo.DB.
testmodels/example
Package example is the reference schema for momo: a multi-tenant document store with comments, tags and a pgvector embedding.
Package example is the reference schema for momo: a multi-tenant document store with comments, tags and a pgvector embedding.
testmodels/example/comment
Package comment holds typed column descriptors and relation descriptors for the Comment model.
Package comment holds typed column descriptors and relation descriptors for the Comment model.
testmodels/example/document
Package document holds typed column descriptors and relation descriptors for the Document model.
Package document holds typed column descriptors and relation descriptors for the Document model.
testmodels/example/tag
Package tag holds typed column descriptors and relation descriptors for the Tag model.
Package tag holds typed column descriptors and relation descriptors for the Tag model.
testmodels/example/tenant
Package tenant holds typed column descriptors and relation descriptors for the Tenant model.
Package tenant holds typed column descriptors and relation descriptors for the Tenant model.
Package migrate runs schema migrations.
Package migrate runs schema migrations.
migratetest
Package migratetest holds helpers shared by the migrate engine tests.
Package migratetest holds helpers shared by the migrate engine tests.
postgres
Package postgres is the PostgreSQL migration dialect: it reads a live database into a schema.Schema through catalog queries.
Package postgres is the PostgreSQL migration dialect: it reads a live database into a schema.Schema through catalog queries.
Package schema is Momo's normalized, dialect-aware schema representation.
Package schema is Momo's normalized, dialect-aware schema representation.
Package types holds the custom column types Momo understands: each is a Codec, so database/sql can scan it, drivers can bind it and the schema layer can name its SQL type per dialect.
Package types holds the custom column types Momo understands: each is a Codec, so database/sql can scan it, drivers can bind it and the schema layer can name its SQL type per dialect.
Package vector holds the vector value types.
Package vector holds the vector value types.

Jump to

Keyboard shortcuts

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