momo

package module
v0.10.0 Latest Latest
Warning

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

Go to latest
Published: Nov 13, 2026 License: Apache-2.0 Imports: 33 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.10.0, pre-v1, with no compatibility promise between releases. This release is tested against PostgreSQL with pgvector. The databases below are generated from the driver packages; see capabilities for what each one supports.

A database is usable when a driver package for it exists under driver/:

  • PostgreSQL: usable through driver/pgxdriver (vector builders: yes). Migrations: migrate/postgres, run by momo migrate.
  • SQLite: usable through driver/sqlitedriver (needs cgo, not on Windows, vector builders: yes) and driver/sqlitepure (vector builders: no). Migrations: migrate/sqlite, library only (momo migrate does not select it).
  • MySQL and MariaDB: usable through driver/mysqldriver (vector storage and builders depend on the server version). Migrations: migrate/mysql, library only (momo migrate does not select it).

Momo needs Go 1.26 or newer (see go.mod for the exact patch floor, 1.26.10).

Install

Add the library to your module and install the CLI:

go get github.com/azrtydxb/momo@v0.10.0
go install github.com/azrtydxb/momo/cmd/momo@v0.10.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/vecop"
	"github.com/azrtydxb/momo/vecsearch"
	"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.10.0 writes forward migrations only. momo migrate rollback runs a .down.sql you wrote by hand; otherwise undo a change with a new migration.

Run momo migrate help for the other commands: plan, status, validate, lint, rebase, 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, bool, integer, finite float and string values in the target predicate render inline ("live" = 1) because SQLite matches the expression, so write the same literal in the index; any other value, and Excluded(), is ErrValidation. Write the index predicate with the same literal (WHERE live = 1); this is golden-tested and run against a real SQLite partial unique index. The values appear in the SQL text, so they show up in logs and traces.

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.

vecsearch.With (package github.com/azrtydxb/momo/vecsearch) runs a function in a transaction with pgvector settings applied, here hnsw.ef_search. The settings end with the transaction. vecsearch.Settings also covers iterative scans for filtered searches, IVFFlat probes, and ExactSearch.

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

The distance builders live in package github.com/azrtydxb/momo/vecop. For dense vectors the functions are vecop.L2, vecop.InnerProduct, vecop.Cosine, and vecop.L1. For bit vectors they are vecop.Hamming and vecop.Jaccard. Match the function to the operator class of your index: this example indexes with vector_cosine_ops, so it searches with vecop.Cosine.

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; momo generate writes its MomoMeta, so there is nothing to maintain by hand. When From is a table with a soft-delete column, soft-deleted rows are excluded automatically, as for a model; WithDeleted() and OnlyDeleted() work the same way. See docs/projections.md, examples/projection-pagination and the Hit type in examples/vector-search.

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 generate --from-db <url|-|ENVVAR> inspects a PostgreSQL database and writes the models and the init snapshot, so an existing schema is adopted without writing the structs by hand; see adopting an existing database.
  • A team with its own generator plugins builds its own binary: a main that calls cli.Main(plugins...) (package github.com/azrtydxb/momo/cli) gets every command, flag and exit code of momo, and the plugins are enabled by name in momo.yaml.
  • 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 ¶

Package momo is a typed Go ORM. A generator turns plain Go structs into typed column and relation descriptors, and this package builds and runs SQL from them: select, insert, update, delete, soft delete, upserts, subqueries and CTEs, relations with eager loading, transactions with savepoints, and vector search.

Momo supports PostgreSQL 16 and 17 with pgvector, through the pgx driver in the driver/pgxdriver package. The SQLite and MySQL dialect descriptors exist but have no driver yet. The module is pre-v1, with no compatibility promise between releases.

A program opens a database with New and a driver, then runs queries against the DB or a Tx, both of which implement Querier:

db := momo.New(pgxdriver.New(pool))
docs, err := momo.Select[models.Document](db).
	Where(document.Published.Eq(true)).
	All(ctx)

The descriptors (document.Published above) come from the momo command, which reads the model structs and writes a momo_gen.go file next to them. The same command manages versioned SQL migrations.

Documentation for people and for AI coding agents:

  • docs/ai-guide.md is the guide to read first: setup, modeling, migrations, queries, relations, vectors, errors and the known pitfalls.
  • docs/capabilities.md and docs/supported-versions.md say what is supported and tested.
  • docs/ARCHITECTURE.md describes the packages and their contracts, and lists what is only planned.

All paths are relative to the module root, https://github.com/azrtydxb/momo.

Example (VectorDimensionMismatch) ¶

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/vecop"
	"github.com/azrtydxb/momo/vector"
)

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

	_, err := momo.Select[example.Document](db).
		OrderBy(vecop.L2(document.Embedding, vector.Vector{1, 2, 3}).Asc()).
		SQL()
	fmt.Println(err != nil)
}
Output:
true
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)
Example (VectorNearest) ¶

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/vecop"
	"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(vecop.Cosine(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

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
	// FlagDBDefault: a Model or Models insert leaves the column out when the
	// field holds its zero value, so the database default applies, and reads
	// the stored value back. Set by the dbdefault tag.
	FlagDBDefault
)

Column flags carried by generated column descriptors and ColumnMeta.

View Source
const (
	// DefaultHybridCandidates is the candidate count of each leg when
	// HybridSpec.TextCandidates or VectorCandidates is zero.
	DefaultHybridCandidates = 100
	// DefaultHybridRRFK is the rank constant used when HybridSpec.RRFK is
	// zero (Cormack, Clarke and Buettcher, 2009).
	DefaultHybridRRFK = 60
)

Defaults and limits of HybridSpec.

View Source
const RedactedArg = redact.Arg

RedactedArg replaces, in QueryEvent.Args, every value bound from a column tagged sensitive. Only the copy handed to hooks is masked.

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")

	// ErrAdoptedTx: Commit or Rollback was called on a Tx from DB.AdoptTx.
	// The caller owns that transaction and finishes it itself; nothing was
	// committed or rolled back.
	ErrAdoptedTx = errors.New("momo: transaction is owned by the caller")

	// 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")
	// ErrCanceled: a streaming read stopped because its context ended. The
	// context's own error (context.Canceled or context.DeadlineExceeded) is
	// wrapped too, so errors.Is matches it as well.
	ErrCanceled = errors.New("momo: operation canceled")
	// 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 AllowStaleReads ¶ added in v0.4.0

func AllowStaleReads(ctx context.Context) context.Context

AllowStaleReads returns a context that opts reads of a Replica handle back to the replica even after a write in the same unit of work.

func Batches ¶ added in v0.4.0

func Batches[T any](ctx context.Context, sq SelectQuery[T], n int, fn func([]T) error) error

Batches visits every row sq matches in batches of at most n rows, in primary-key order, calling fn for each batch. It pages with keyset pagination on the primary key, never OFFSET, so each row is visited exactly once even while rows with higher keys are inserted, and a deep batch costs the same as the first. sq must not set OrderBy, After, Limit, Offset or MaxRows: Batches owns the order and the page window. An error from fn stops the iteration and is returned.

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.

The children of a HasMany or ManyToMany relation are inserted with the chunked bulk insert (InsertQuery.Models): one multi-row statement per bind-limit chunk instead of one statement per child, so 1000 children cost one statement, not 1000. A HasOne or BelongsTo child is a single insert.

Every listed child is inserted as a new row, including a ManyToMany child that is already stored: it is not linked, it is inserted again and fails with ErrUniqueViolation (undoing the whole graph) when its primary key exists. To associate children that already exist, use Relation.Link (or insert them first and Link them) instead of listing the relation here.

A bulk insert returns its rows in insertion order, and CreateGraph matches them to the children by position. If the database returns a different number of rows than were inserted (for example a BEFORE INSERT trigger skipped one), the graph fails with ErrValidation and is undone, rather than leaving the remaining children without their generated keys.

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.

CreateGraph needs RETURNING to read generated keys back: a dialect without it (MySQL) fails with ErrUnsupported before a transaction is opened.

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 '!', which Like, ILike and ContainsLiteral always emit. 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 '?', and on PostgreSQL native $n placeholders.

func Explain ¶ added in v0.5.0

func Explain(ctx context.Context, q Querier, c Compiled) ([]string, error)

Explain returns the database's plan for the compiled statement c without executing it: EXPLAIN on PostgreSQL, EXPLAIN FORMAT=JSON on MySQL and MariaDB (one text column) and EXPLAIN QUERY PLAN on SQLite. Any statement can be explained, writes included; c.Args are bound, never interpolated. Hooks see the statement with Op "explain". Momo never calls Explain on its own.

SelectQuery.Explain is the shorthand for a select; Explain takes the Compiled value of any query's SQL method.

func ExplainAnalyze ¶ added in v0.5.0

func ExplainAnalyze(ctx context.Context, q Querier, c Compiled) ([]string, error)

ExplainAnalyze runs c under EXPLAIN (ANALYZE, FORMAT TEXT) and returns the plan with actual timings. ANALYZE executes the statement, so every call runs inside a transaction (or, on a *Tx, a savepoint) that is always rolled back: a write leaves no row behind, and a SELECT that calls a writing function does not keep its writes. A rollback cannot undo effects outside the transaction: sequence values taken by nextval, NOTIFY sent by a function, work done through dblink or a foreign data wrapper, and anything else the statement does outside the database. PostgreSQL only: other dialects return ErrUnsupported and issue nothing. Momo never calls ExplainAnalyze on its own.

func FindAndCount ¶ added in v0.4.0

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

FindAndCount returns the rows of sq (with its LIMIT and OFFSET) and the total number of rows it matches without them (and without After, ordering and any row lock), using two statements. The two are not one snapshot: run them in a transaction when a consistent pair is required.

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

A soft-deleted row does not count as found: FindOrCreate never returns or restores a row the application deleted. The insert is attempted. When the unique constraint on key covers soft-deleted rows (a plain unique constraint or index, the case FindOrCreate supports), it still holds the deleted row's key, the insert conflicts and the call fails with ErrUniqueViolation (Op "find or create") instead of returning the deleted row: a soft-deleted match is an error, not a surprise resurrection. To reuse the old row, restore it first (update the soft-delete marker back) or pick a new key. A partial unique index (WHERE deleted_at IS NULL) would let the new row in, but FindOrCreate cannot target it: ON CONFLICT (key) without the index predicate does not match a partial index and the database rejects the statement. For a key that soft-deleted rows may release, use Insert(...).OnConflict(key).Where(indexPredicate).DoNothing() and re-select yourself. A model without a soft-delete column is unaffected.

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. A dialect without ON CONFLICT or RETURNING (MySQL) returns ErrUnsupported before any statement runs.

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 FirstID ¶ added in v0.4.0

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

FirstID returns the primary key of the first row sq matches, or ErrNotFound when none matches.

func FlagsOf ¶ added in v0.4.0

func FlagsOf(e Expr) uint16

FlagsOf returns the column flags of e, or 0 when e does not implement Flagged. Pass the result to Builder.ArgFor, Builder.RenderSensitive or Builder.ExprFor so values compared with a sensitive column are masked in hook events. FlagsOf is an extension point; see Ops.

func IDs ¶ added in v0.4.0

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

IDs returns the primary-key value of every row sq matches, in the query's order. The model needs a single-column integer primary key.

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 IsBound ¶ added in v0.4.0

func IsBound(e Expr) bool

IsBound reports whether e is a Go value bound as a parameter by Value, possibly wrapped by Typed. A feature package uses it to cast a bound operand whose type the server cannot infer (a timestamp inside EXTRACT on PostgreSQL). It is false for a column, a function call or any other expression. IsBound is an extension point; see Ops.

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 rel_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 LoadCounts ¶ added in v0.4.0

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

LoadCounts counts the children of every parent in parents with one grouped statement per bind-limit chunk, instead of one Relation.Count call per parent. The result has one entry per parent, in parents order; a parent with no children, or with a NULL key, counts zero. Parents sharing a key share the count.

counts, err := momo.LoadCounts(ctx, db, docs, document.Comments,
	momo.LoadWhere[models.Comment](comment.Approved.Eq(true)))

It supports has-one, has-many and many-to-many relations, including composite keys and polymorphic (Discriminator) relations; belongs-to fails with ErrUnsupported, as Count does. Load options narrow what is counted: LoadWhere filters the children (it may reference the child table), and soft-deleted children are excluded unless LoadWithDeleted or LoadOnlyDeleted says otherwise, exactly like Load. LoadOrderBy has no effect, and LoadPerParentLimit fails with ErrValidation.

Many-to-many counts join-table rows, as Count does: the join to the far table is added only when the child has a soft-delete column or a filter applies. Statements are SELECT key, COUNT(*) ... GROUP BY key.

func MapRows ¶ added in v0.4.0

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

MapRows renders sql through the raw path (see Exec) and returns every row as a map from column name to value. Names are as the driver reports them (for PostgreSQL, the folded column name or alias), and values have the driver's native Go types. Two result columns with the same name are ambiguous in a map, so they fail with ErrValidation: alias them in the SQL. It needs a driver whose rows implement ColumnNamer and returns ErrUnsupported otherwise. MapRows buffers the whole result in memory and ignores MaxRows, which is a SelectQuery option; bound the SQL with LIMIT. An empty result is a nil slice and a nil error.

func MySQLVectorType ¶ added in v0.4.0

func MySQLVectorType(flavor dialect.Flavor, serverVersion string, dim int) (string, error)

MySQLVectorType returns the column type for a vector of dim elements on a MySQL or MariaDB server: "VECTOR(N)". serverVersion is the result of SELECT VERSION(). MySQL has the type from 9.0 and MariaDB from 11.7; an older server, a dimensionless column and an unreadable version fail with an error that wraps ErrUnsupported and, for a version problem, names the version. Migration code calls this when it renders a vector column.

func NewUnitOfWork ¶ added in v0.4.0

func NewUnitOfWork(ctx context.Context) context.Context

NewUnitOfWork returns a context in which every write made through any handle is recorded. A Replica handle reads from the primary once a write has been recorded in that context, so a request reads its own writes.

func Pluck ¶ added in v0.4.0

func Pluck[T, V any](ctx context.Context, sq SelectQuery[T], c Expression[V]) ([]V, error)

Pluck runs sq selecting only the expression c and returns that column of every row. The query keeps its WHERE, ORDER BY, LIMIT and MaxRows; Preload does not apply. An empty result is a nil slice and a nil error.

func Query ¶ added in v0.5.0

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

Query renders sql through the raw path (see Exec: '?' binds, "??" is a literal '?', Expr arguments render inline, an argument-count mismatch is a build error; on PostgreSQL native $n placeholders bind argument n, see SQL) and scans every row into a T. T is a model, a projection struct, one of the tuple rows Row1 to Row10, or a plain struct. It is the multi-column counterpart of Scalars for a hand-written SELECT or a statement with RETURNING:

rows, err := momo.Query[momo.Row2[string, int64]](ctx, db,
	`SELECT title, count(*) FROM documents WHERE tenant_id = $1 GROUP BY title`, tenant)

Columns are matched as for a typed select. A tuple row scans positionally, and the result must have exactly as many columns as the row has fields.

A plain struct, one with no generated metadata, scans by column name too, with no generator run: its exported fields are the columns, named by a `momo:"column:x"` tag or else the column name the DB's naming preset gives the field name (WithNaming; snake_case without one, so UserID is user_id); `momo:"-"` skips a field; the fields of an embedded struct count as the outer struct's own, unless it is a time.Time, a sql.Scanner or carries a column tag; an embedded pointer struct is allocated for every row and stays non-nil even when all of its columns are NULL (an embedding cycle is an ErrValidation); a pointer field receives NULL as nil (a non-pointer field that meets NULL is the driver's scan error). The same rule as below applies: a result column no field maps to, or a field no result column fills, is an ErrValidation that names it. The reflection plan is built once per type and cached, and the generated metadata of a model, projection or tuple row is always preferred when the type has it.

A model or projection scans by column name, whatever order the SQL lists them (names compare exactly, then case-insensitively, since PostgreSQL folds unquoted names): a result column that no field maps to, a mapped column the result lacks, or two result columns with the same name is an ErrValidation. A driver whose rows cannot report column names falls back to the positional order of the type's columns.

The statement runs with the same hooks, interceptors, error classification and argument redaction as Exec, on a *DB, a *Tx or a pinned handle; its hook event has the op "raw". The rows bypass soft-delete filters, AfterLoad hooks and timestamps. Query buffers the whole result in memory and ignores MaxRows, so bound the SQL with LIMIT or use QueryRows. An empty result is a nil slice and a nil error.

func QueryRows ¶ added in v0.5.0

func QueryRows[T any](ctx context.Context, q Querier, sql string, args ...any) iter.Seq2[T, error]

QueryRows is the streaming form of Query: it yields one T per row without buffering the result. Breaking out of the loop closes the cursor, and an error (a build, driver, scan or column-matching error, or a canceled context as ErrCanceled) is yielded once, as the final element. The placeholder, matching, hook and redaction rules are those of Query.

for row, err := range momo.QueryRows[momo.Row2[int64, string]](ctx, db,
	`SELECT id, title FROM documents ORDER BY id`) {
	if err != nil {
		return err
	}
	use(row.C1, row.C2)
}

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 RefreshMaterializedView ¶ added in v0.6.0

func RefreshMaterializedView(ctx context.Context, q Querier, name string, concurrently bool) error

RefreshMaterializedView runs REFRESH MATERIALIZED VIEW on q. The name is the view's name, or schema.name; each part is quoted as an identifier, so a name cannot inject SQL. With concurrently set the statement is REFRESH MATERIALIZED VIEW CONCURRENTLY: readers are not blocked, and PostgreSQL requires a unique index on the view (declare it in the view's Indexes) and a view that already holds data.

PostgreSQL only: other dialects fail with ErrUnsupported, and a name that is empty, has more than two parts or has a part longer than 63 bytes fails with ErrValidation, both before any statement is sent. Declare the view in the model's MomoConfig with c.View and Materialized set; a migration creates it, empty or filled as the query returns at that moment, and this helper brings it up to date.

func RegisterQuery ¶ added in v0.5.0

func RegisterQuery(name string, build func(q Querier) Compilable)

RegisterQuery registers build under name in the DefaultQueries registry. See QueryRegistry.Register.

func RelAlias ¶ added in v0.8.0

func RelAlias(level int) string

RelAlias names one nesting level of Has, HasNo and CountOrder subqueries explicitly, for use with As in a scope predicate. Level 1 is the outermost subquery (alias momo_r), level 2 the one nested in it (momo_r2), and so on. Unlike the bare alias "momo_r", which always means the subquery that the scope belongs to, RelAlias(n) is never rewritten: it renders as the generated alias of level n. A level that does not enclose the scope fails the build with ErrValidation, as any column of a table that is not in the query does. A level below 1 is read as 1.

tag.Name.As("momo_r").Eq("db")                // the scope's own level
document.ID.As(momo.RelAlias(1)).Eq(int64(1)) // the outermost level

func SQLiteBitsType ¶ added in v0.4.0

func SQLiteBitsType(column string, n int) (string, error)

SQLiteBitsType is SQLiteVectorType for a bit vector of n bits: a BLOB whose length is the packed size, (n+7)/8 bytes. An n below one is ErrUnsupported and one above vector.MaxBits is ErrValidation.

func SQLiteVectorType ¶ added in v0.4.0

func SQLiteVectorType(column string, dim int) (string, error)

SQLiteVectorType returns the column definition of a float32 vector of dim elements on SQLite: a BLOB whose length is checked, for example BLOB CHECK (length("emb") = 12) for dim 3. sqlite-vec stores vectors in plain BLOB columns and has no declared dimension, so the CHECK is what keeps a vector of the wrong size out of the table. column is the name of the column being declared and is quoted. A dim below one is ErrUnsupported (a dimensionless column has no length to check) and one above vector.MaxDim is ErrValidation. Migration code calls this when it renders a vector column.

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; on PostgreSQL native $n placeholders bind argument n, see SQL) and returns the first column of every row as V. An empty result returns a nil slice and a nil error.

func ScanDelete ¶ added in v0.8.0

func ScanDelete[R, T any](ctx context.Context, d DeleteQuery[T]) ([]R, error)

ScanDelete runs the DELETE and scans every RETURNING row into an R, as ScanUpdate does for an UPDATE: use it when Returning lists columns of a Using source or CTE. R is a projection, a tuple row or a plain struct, matched by result column name.

func ScanUpdate ¶ added in v0.8.0

func ScanUpdate[R, T any](ctx context.Context, u UpdateQuery[T]) ([]R, error)

ScanUpdate runs the UPDATE and scans every RETURNING row into an R, which is what to use when Returning lists columns of a From source or CTE, that the model cannot hold. R is a projection, a tuple row (Row1 to Row10) or a plain struct, matched by result column name as in Query: a source column "s"."url" is the result column url, so name or tag the field accordingly, and two returned columns with the same name are an ErrValidation (leave one out). A Returning list of the target's own columns scans too. The rows are buffered, MaxRows does not apply, and the AfterLoad hook does not run. An update that matched no row returns a nil slice. The other rules are those of Rows.

claimed, err := momo.ScanUpdate[Claimed](ctx, momo.Update[Delivery](db).
	Set(...).From(momo.CTE("due"), sub).Where(...).
	Returning(DeliveryID, momo.ColumnOf[string]("s", "url")))

func SetConfigLocal ¶ added in v0.4.0

func SetConfigLocal(ctx context.Context, tx *Tx, key, value string) error

SetConfigLocal sets the configuration parameter key to value for the rest of the current transaction only (set_config with is_local = true). It is the building block for tenant scoping inside WithTxSetup. Because the setting ends with the transaction it is safe behind PgBouncer in transaction mode; never use a session-level SET for this. key must not come from untrusted input (see Tx.SetLocal). It returns ErrUnsupported on dialects without transaction-scoped settings.

func Truncate ¶ added in v0.4.0

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

Truncate removes all rows of T's table, soft-deleted rows included, with no filter. PostgreSQL and MySQL run TRUNCATE TABLE with no RESTART IDENTITY and no CASCADE: PostgreSQL keeps the identity sequence and fails when another table has a foreign key to this one, and MySQL resets its AUTO_INCREMENT counter. SQLite runs DELETE FROM and deletes the table's sqlite_sequence row when that table exists, so a SQLite autoincrement key restarts. The two SQLite statements are atomic: on a DB they run in one transaction, on a Tx in a savepoint.

MySQL TRUNCATE is DDL and commits implicitly, so inside a Tx it is refused with ErrUnsupported. On a MySQL DB made with WithImplicitTx the statement is still wrapped in the implicit BEGIN and COMMIT, and MySQL commits it at once, so that transaction (and any WithTxSetup setting in it) ends with the TRUNCATE; the wrapper is harmless but gives no atomicity. The handle's interceptors run first with the kind "truncate" and can deny it; a predicate they add is ignored, so a tenant-scoped handle should deny the kind.

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

func ValidName ¶ added in v0.4.0

func ValidName(s string) error

ValidName checks an identifier-like name: non-empty, at most 63 bytes, a letter or underscore first, then letters, digits, underscores or dollar signs. It returns a descriptive error otherwise. Code outside the package uses it to check a function name or alias before writing it with Builder.Ident. ValidName is an extension point; see Ops.

func ValidTypeName ¶ added in v0.4.0

func ValidTypeName(s string) error

ValidTypeName checks a "word word(1,2)" shaped SQL type name: words of letters, digits and underscores separated by single spaces, with an optional (n) or (n,m) suffix, such as bigint, double precision or numeric(10,2). It returns a descriptive error otherwise. Code outside the package uses it to check a type name before writing it into a CAST. ValidTypeName is an extension point; see Ops.

func WithDetail ¶ added in v0.4.0

func WithDetail(err error, detail string) error

WithDetail returns a copy of err with the raw driver detail set, so Detail can return it while Error keeps hiding it. Drivers call it from their classifier. An err that is not a *Error is returned unchanged.

func WithQueryScope ¶ added in v0.4.0

func WithQueryScope(ctx context.Context) context.Context

WithQueryScope returns a context that groups statements for the N+1 detector. Wrap one unit of work (an HTTP request, a job) and pass the context to every query in it. Contexts without a scope are not tracked.

func WithTenant ¶ added in v0.4.0

func WithTenant(ctx context.Context, tenant any) context.Context

WithTenant returns a context carrying tenant for TenantFilter only. Nothing else reads it; see TenantFilter for how to drive row-level security from it.

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]) As ¶ added in v0.3.0

func (a Agg[T]) As(name string) Aliased[T]

As names the aggregate in a select list: SUM(col) AS name.

func (Agg[T]) Filter ¶ added in v0.4.0

func (a Agg[T]) Filter(p Predicate) FilteredAgg[T]

Filter restricts the rows the aggregate sees: COUNT(*) FILTER (WHERE p). PostgreSQL and SQLite support it; MySQL has no FILTER clause and fails the build with ErrUnsupported (write SUM(CASE WHEN ...) there).

func (Agg[T]) Over ¶ added in v0.4.0

func (a Agg[T]) Over(w WindowSpec) WindowExpr[T]

Over turns an aggregate into a window function: SUM(x) OVER (...).

type Aliased ¶

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

Aliased is a select-list item "expr AS alias". T is the expression's result type, which Ref carries to the outer query.

func As ¶

func As[T any](e Expression[T], alias string) Aliased[T]

As aliases an expression in a select list: expr AS alias. The alias is quoted and must be a plain identifier (letters, digits, underscore, "$", not starting with a digit, at most 63 bytes), otherwise the build fails with ErrValidation. Use it on columns, aggregates, CASE, function calls and TypedSQL fragments; to alias a scalar subquery wrap it in TypedSQL[T] with "?". Column.As is the table alias, so use As[T](col, name) for a column.

func (Aliased[T]) AppendSQL ¶

func (a Aliased[T]) AppendSQL(b *Builder)

AppendSQL writes the expression and its alias to b.

func (Aliased[T]) Asc ¶ added in v0.3.0

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

Asc orders by the alias ascending.

func (Aliased[T]) Desc ¶ added in v0.3.0

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

Desc orders by the alias descending.

func (Aliased[T]) Ref ¶ added in v0.3.0

func (a Aliased[T]) Ref(source string) Column[T]

Ref is the typed column through which an outer query reads this alias from a subquery (FromSubquery alias) or CTE (name) called source. Momo cannot check that source exposes the alias.

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 and RawSubquery), 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) AppendFilter ¶ added in v0.4.0

func (b *Builder) AppendFilter(p Predicate)

AppendFilter writes " FILTER (WHERE p)" for an aggregate call that was just rendered. A nil predicate fails the build with ErrValidation instead of silently leaving the aggregate unfiltered; MySQL has no FILTER clause and fails with ErrUnsupported. AppendFilter is an extension point; see Ops.

func (*Builder) Arg ¶

func (b *Builder) Arg(v any)

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

func (*Builder) ArgFor ¶ added in v0.4.0

func (b *Builder) ArgFor(flags uint16, v any)

ArgFor binds v like Arg and records it as sensitive when flags carries FlagSensitive, so QueryEvent.Args shows RedactedArg instead of the value. Take flags from FlagsOf of the column the value is compared with, or from RenderSensitive. ArgFor is an extension point; see Ops.

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) ExprFor ¶ added in v0.4.0

func (b *Builder) ExprFor(flags uint16, e Expr)

ExprFor renders e with every argument it binds recorded as sensitive when flags carries FlagSensitive, for an expression compared with or assigned to such a column. ExprFor is an extension point; see Ops.

func (*Builder) Ident ¶

func (b *Builder) Ident(name string)

Ident appends a quoted identifier.

func (*Builder) MarkSensitive ¶ added in v0.4.0

func (b *Builder) MarkSensitive()

MarkSensitive records that the expression being rendered read a sensitive column, so an enclosing predicate that compares it masks its other operands in hook events. A column reference does this by itself; call MarkSensitive from a custom expression that writes a sensitive column's identifier directly with Ident or QualifiedIdent instead of rendering the column. It does not change the SQL. MarkSensitive is an extension point; see Ops.

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) RenderSensitive ¶ added in v0.4.0

func (b *Builder) RenderSensitive(e Expr, flags uint16) uint16

RenderSensitive renders e and returns flags with FlagSensitive added when e read a sensitive column. The result is the flag to bind the other operands of the predicate with, through ArgFor or ExprFor. RenderSensitive is an extension point; see Ops.

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) Unsupported ¶ added in v0.4.0

func (b *Builder) Unsupported(feature string)

Unsupported records an ErrUnsupported build error for feature, naming the dialect being rendered. Call it from a custom expression that the dialect cannot express, instead of rendering SQL that would fail at the database. Unsupported is an extension point; see Ops.

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. Build one with NewColumn, ColumnOf or the generated descriptors: the zero value Column[T]{} has no column reference, so using it in a comparison fails the build with ErrValidation.

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) AppendSQL ¶

func (c Column) 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) AsExpr ¶

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

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

func (Column) Asc ¶

func (c Column) Asc() Order

Asc orders by the column ascending. The order item is the column itself, a ColumnRef, so keyset pagination recognizes a primary-key column.

func (Column) ColumnName ¶

func (c Column) ColumnName() string

ColumnName returns the unquoted column name.

func (Column) Desc ¶

func (c Column) Desc() Order

Desc orders by the column descending.

func (Column) Excluded ¶ added in v0.2.0

func (c Column) Excluded() ExcludedExpr[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) Flags ¶

func (c Column) Flags() uint16

Flags returns the generated column flags.

func (Column) ParseCmp ¶ added in v0.4.0

func (c Column) ParseCmp(op, raw string) (Predicate, error)

ParseCmp parses raw with the column's Go type and returns the comparison predicate for op, one of eq, ne, lt, le, gt and ge. An unknown op or a value that does not parse is an *Error with Kind ErrValidation. It is meant for a filter value that arrives as text from outside; the column comes from the generated ColumnByName allowlist.

func (Column) ParseEq ¶ added in v0.4.0

func (c Column) ParseEq(raw string) (Predicate, error)

ParseEq parses raw with the column's Go type and returns the equality predicate on it. A value that does not parse is an *Error with Kind ErrValidation; the error does not echo raw, which may be attacker controlled.

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) Table ¶

func (c Column) 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 ColumnNamer ¶ added in v0.4.0

type ColumnNamer interface {
	Columns() ([]string, error)
}

ColumnNamer is implemented by Rows that can report the names of their result columns, in order, as the database reports them. MapRows needs it. Rows that cannot report names return an empty slice and a nil error (the wrapper momo puts around rows for hooks implements the interface for every driver), which makes MapRows return ErrUnsupported.

type ColumnRef ¶

type ColumnRef interface {
	Expr
	ColumnName() string
	// ParseEq parses raw with the column's Go type and returns the equality
	// predicate; a value that does not parse is an ErrValidation error.
	ParseEq(raw string) (Predicate, error)
	// ParseCmp is ParseEq for op eq, ne, lt, le, gt or ge.
	ParseCmp(op, raw string) (Predicate, error)
}

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 ColumnRows ¶ added in v0.8.0

type ColumnRows interface {
	Rows
	ColumnNamer
}

ColumnRows is the cursor RawRows returns: a Rows that also names its result columns.

func RawRows ¶ added in v0.8.0

func RawRows(ctx context.Context, q Querier, sql string, args ...any) (ColumnRows, error)

RawRows runs sql through the raw path and returns the open cursor, for a result whose shape is only known at run time: a statement whose column count or types depend on its input, which Query and QueryRows cannot type and MapRows would buffer into maps. Placeholders, hooks (op "raw"), interceptors, WithTxSetup, WithImplicitTx, error classification and argument redaction are those of Query, on a *DB, a *Tx or an AdoptTx handle.

rows, err := momo.RawRows(ctx, db, sql, args...)
if err != nil {
	return err
}
defer rows.Close()
for rows.Next() {
	if err := rows.Scan(dest...); err != nil {
		return err
	}
}
return rows.Err()

The caller closes the cursor, always: a cursor that is never closed holds its connection, and on a *DB with WithImplicitTx the transaction behind it. Close is safe to call more than once. Raw SQL may write, so under WithImplicitTx momo treats the statement as a write: the transaction commits when the rows are read to the end and Close returns nil, and it is rolled back, with Close returning ErrValidation, when Close comes before the end of the rows (including a Close with no Next), after a Scan or driver error, or after a canceled context. Without WithImplicitTx an early Close is just a Close. A canceled context ends the iteration: Next returns false, the cursor is closed and Err reports ErrCanceled; a context canceled after the last row was read can still report ErrCanceled, as in QueryRows. Scan errors go through the DB's error classifier like any other statement error and are recorded on the hook event. The rows bypass soft-delete filters, AfterLoad hooks and timestamps, and MaxRows does not apply. Columns reports the result column names as the driver gives them: an empty list means the statement returns no columns (an UPDATE without RETURNING) or the driver cannot name them, and ErrUnsupported means the cursor does not implement ColumnNamer.

type Compilable ¶ added in v0.5.0

type Compilable interface {
	SQL() (Compiled, error)
}

Compilable is a statement that renders to SQL without running it. The query builders (SelectQuery, InsertQuery, UpdateQuery, DeleteQuery, MergeQuery) implement it through their SQL method, and RawQuery wraps a hand-written statement.

func RawQuery ¶ added in v0.5.0

func RawQuery(q Querier, sql string, args ...any) Compilable

RawQuery wraps a hand-written statement as a Compilable, with the placeholder rules of Exec ('?' binds, "??" is a literal '?', on PostgreSQL native $n placeholders). It is for QueryRegistry builders:

momo.RegisterQuery("open_tasks", func(q momo.Querier) momo.Compilable {
	return momo.RawQuery(q, "SELECT id, title FROM tasks WHERE owner = $1 AND NOT done", 0)
})

A malformed statement shows as the error of SQL.

type Compiled ¶

type Compiled struct {
	SQL  string
	Args []any
	// contains filtered or unexported fields
}

Compiled is a rendered statement ready for inspection or execution.

func (Compiled) String ¶

func (c Compiled) String() string

String returns the SQL text. When arguments are bound it appends a comment that names their placeholders; it never prints the argument values.

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) FKName ¶ added in v0.5.0

func (c *Config) FKName(field any, name string)

FKName names the foreign key constraint of the belongs_to relation whose key field is field, for a name other than the derived <table>_<column>_fkey (for example _fkey1 on a partitioned table). Read statically by the generator; never executed for its effect.

func (*Config) ForeignKey ¶ added in v0.5.0

func (c *Config) ForeignKey(name string, cols []any, refTable string, refColumns []string) *ForeignKeyOpts

ForeignKey declares a named foreign key over one or more fields of the model: cols is a []any literal of field references, refTable the referenced table and refColumns its columns, which must be its primary key or the columns of a unique constraint or unique index on it. 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) Generated ¶ added in v0.8.0

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

Generated declares a column as stored generated: GENERATED ALWAYS AS (expr) STORED. The field must be read_only, so Insert and Update never write it. Use it instead of the generated tag when the expression has a comma. Read statically by the generator; never executed for its effect.

func (*Config) GeneratedVirtual ¶ added in v0.8.0

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

GeneratedVirtual declares a column as VIRTUAL generated: GENERATED ALWAYS AS (expr) VIRTUAL, computed on read. It is Generated for a VIRTUAL column; the field must be read_only. MySQL and SQLite support it; PostgreSQL has VIRTUAL columns only from version 18, which momo does not support, so a PostgreSQL target is refused. Read statically by the generator; never executed for its effect.

func (*Config) IDGen ¶ added in v0.4.0

func (c *Config) IDGen(field any, name string)

IDGen declares a Go-side id generator for a string field: an insert fills the field when it holds the zero value. The name is a string literal: cuid, uuid or ulid, or one registered with WithIDGenerator. Read statically by the generator; never executed for its effect.

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. Each argument is one key: an expression such as "lower(email)", or the exact name of a column of the table, which is a plain column key (so "company_id", "lower(name)" mixes the two). Chain Unique for a unique index and Where for a partial one. 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) PrimaryKey ¶ added in v0.5.0

func (c *Config) PrimaryKey(fields ...any)

PrimaryKey orders the columns of a composite primary key, for a key whose column order differs from the field order (the order is the btree order that lookups rely on). The fields must be exactly the fields tagged pk. Read statically by the generator; never executed for its effect.

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. On SQLite, bool, integer and string values render inline (SQLite matches the predicate against the index text, so a bound value never matches), which also puts them in logs and traces. Only bool, integer, finite float and string values (after Value()) can be inlined; any other value fails with ErrValidation. SQLite compares the literal text with the index, so write index literals as Momo renders them: integers as 2, floats in plain decimal with .0 for whole numbers (2.0) for magnitudes from 1e-6 up to 1e21, and exponent form (1e-07, 1e+21) outside that range; otherwise use momo.SQL. Excluded() in the predicate fails with ErrValidation. 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. A *DB made with WithReplicas or Replica always reports a Copier, because the router forwards COPY to the primary; its CopyFrom returns ErrUnsupported when the primary has none. Insert.Copy checks the primary itself and fails before it runs hooks or opens a transaction.

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) AppendSQL ¶

func (c CreateOnlyColumn) 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) AsExpr ¶

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

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

func (CreateOnlyColumn) Asc ¶

func (c CreateOnlyColumn) Asc() Order

Asc orders by the column ascending. The order item is the column itself, a ColumnRef, so keyset pagination recognizes a primary-key column.

func (CreateOnlyColumn) ColumnName ¶

func (c CreateOnlyColumn) ColumnName() string

ColumnName returns the unquoted column name.

func (CreateOnlyColumn) Desc ¶

func (c CreateOnlyColumn) Desc() Order

Desc orders by the column descending.

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) Flags ¶

func (c CreateOnlyColumn) Flags() uint16

Flags returns the generated column flags.

func (CreateOnlyColumn) ParseCmp ¶ added in v0.4.0

func (c CreateOnlyColumn) ParseCmp(op, raw string) (Predicate, error)

ParseCmp parses raw with the column's Go type and returns the comparison predicate for op, one of eq, ne, lt, le, gt and ge. An unknown op or a value that does not parse is an *Error with Kind ErrValidation. It is meant for a filter value that arrives as text from outside; the column comes from the generated ColumnByName allowlist.

func (CreateOnlyColumn) ParseEq ¶ added in v0.4.0

func (c CreateOnlyColumn) ParseEq(raw string) (Predicate, error)

ParseEq parses raw with the column's Go type and returns the equality predicate on it. A value that does not parse is an *Error with Kind ErrValidation; the error does not echo raw, which may be attacker controlled.

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) Table ¶

func (c CreateOnlyColumn) Table() string

Table returns the qualifier (table name or alias).

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. Zero marks a dimensionless column: a distance argument of any size is accepted and checked against itself.

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) ParseCmp ¶ added in v0.4.0

func (c CreateOnlyVectorColumn) ParseCmp(string, string) (Predicate, error)

ParseCmp always fails: a vector has no text form to filter by.

func (CreateOnlyVectorColumn) ParseEq ¶ added in v0.4.0

func (c CreateOnlyVectorColumn) ParseEq(string) (Predicate, error)

ParseEq always fails: a vector has no text form to filter by.

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 NewOffline ¶ added in v0.5.0

func NewOffline(d dialect.Name) *DB

NewOffline returns a *DB for dialect d that compiles statements but never reaches a database: every execution returns an error matching ErrUnsupported. Use it to call SQL on a query builder in a unit test. The dialect is the default one for d, without a MySQL flavor or a server version.

NewOffline panics for a dialect name that is not postgres, mysql or sqlite.

func (*DB) AdoptTx ¶ added in v0.3.0

func (db *DB) AdoptTx(dtx DriverTx) *Tx

AdoptTx binds a Tx to dtx, a driver transaction the caller already began and keeps ownership of. Statements, hooks, error classification, the dialect and savepoints (Tx.Tx, Savepoint, ...) behave as on a Tx begun by DB.Begin, but Commit and Rollback on the returned Tx (not on a savepoint scope, which still releases and rolls back its savepoint) return an error matching ErrAdoptedTx and never touch dtx: the caller commits or rolls back its own transaction. Obtain a dtx from the driver package, for example pgxdriver.SQLTx or pgxdriver.PgxTx. Once the caller finishes the transaction, further statements fail with the driver's finished-transaction error (ErrTxDone). AdoptTx panics when dtx is nil, a programming error like a nil Driver in New. It does not fire begin hooks: momo did not begin 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

Dialect returns the dialect of the connection.

func (*DB) Driver ¶

func (db *DB) Driver() Driver

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

func (*DB) IsPinned ¶ added in v0.5.0

func (db *DB) IsPinned() bool

IsPinned reports whether db is a handle from OnConn, whose statements all run on one pinned connection. A *DB over a pool reports false. Session-scoped helpers such as the session locks of package advisory use it to refuse a pooled handle, where two statements can reach two connections.

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) Replica ¶ added in v0.4.0

func (db *DB) Replica(i int) (*DB, error)

Replica returns a read handle whose plain reads go to replica i, unless the context is a unit of work that has written: then reads go to the primary. Writes, transactions, INSERT/UPDATE/DELETE ... RETURNING, locking reads (FOR UPDATE and FOR SHARE), and any statement that readOnlySQL cannot prove read-only, such as a WITH that contains DML or a call to nextval, always go to the primary, and count as a write of the unit of work. If the replica cannot be reached when a read starts, that read runs on the primary; there is no retry once rows have been returned.

It returns ErrUnsupported when replica i is not configured, and when the DB was built with WithImplicitTx: that option runs every statement in its own primary transaction, so a Replica handle could never use the replica, and failing is clearer than silently reading the primary. A read on the replica runs outside any transaction, so WithTxSetup does not apply to it; do not rely on a setting that WithTxSetup installs (for example a row-level-security tenant) for replica reads.

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.

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]) AppendSQL ¶ added in v0.5.0

func (d DeleteQuery[T]) AppendSQL(b *Builder)

AppendSQL renders the statement in parentheses as a WITH clause body. It runs the handle's interceptors with a background context, as SQL does, but not the beforeWrite checks of Exec. Only PostgreSQL accepts the result.

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]) One ¶ added in v0.7.0

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

One runs the DELETE and returns the single RETURNING row: ErrNotFound when no row matched and ErrTooManyRows when more than one did. Without a Returning list it returns every column. On a dialect without RETURNING (MySQL, and MariaDB, where momo has no RETURNING form for DELETE) it fails with ErrUnsupported.

Without WithImplicitTx or an explicit transaction the statement has already changed every matched row when One returns ErrTooManyRows; run One inside db.Tx and roll back on ErrTooManyRows. AfterLoad runs on the returned row before the write commits, so an AfterLoad error rolls an implicit transaction back, unlike SelectQuery.One.

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.

Interceptors run with a background context here; the execution methods pass the statement's own context.

func (DeleteQuery[T]) Using ¶ added in v0.5.0

func (d DeleteQuery[T]) Using(srcs ...TableSource) DeleteQuery[T]

Using adds sources, rendering DELETE FROM t USING src WHERE ...: the WHERE may reference src's columns. src is a table, aliased table, CTE or FromSubquery, as in a join; join it to the target in Where. Calling Using again replaces the sources.

Using takes more than one source (DELETE FROM t USING a, b WHERE ...), which render as a comma list; the guard then also requires Where to reference every source (see below). Returning may list columns of a source, as for UpdateQuery.From; scan it with ScanDelete.

PostgreSQL renders DELETE ... USING. MySQL and MariaDB render the multi-table form DELETE t FROM t, src WHERE .... SQLite has no such form and fails with ErrUnsupported (use a subquery in Where). Soft-deleted rows of src are not filtered: add that predicate to Where. Interceptors apply to the target table only, not to src.

With a Using source the unfiltered-mutation guard is stricter: at least one Where predicate must reference a column of the target table, otherwise the statement joins every target row to src and fails with ErrUnfilteredMutation unless AllRows confirms it. A raw SQL fragment does not count as such a reference. An Or counts only when every branch references the target, because a branch off the target can be true on its own; an And counts when any term does. Not swaps the two rules (De Morgan): a negated And needs every term on the target, a negated Or any term. JoinSQL with AND or OR follows the same rules as And and Or; with another separator it does not count. The guard checks that Where references the target, not that it restricts it: a constant-true predicate such as t.id = t.id passes. With two or more sources the same rule applies to each of them: a source that no Where predicate references is cross-joined to the target, and the statement fails with ErrUnfilteredMutation unless AllRows confirms it. A single source is not checked this way.

func (DeleteQuery[T]) Where ¶

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

Where adds predicates combined with AND.

func (DeleteQuery[T]) With ¶ added in v0.5.0

func (d DeleteQuery[T]) With(name string, q Subquery, cols ...string) DeleteQuery[T]

With prefixes the statement with a common table expression; see UpdateQuery.With for the allowed bodies and the dialect rules (MariaDB refuses the clause with ErrUnsupported).

func (DeleteQuery[T]) WithRecursive ¶ added in v0.5.0

func (d DeleteQuery[T]) WithRecursive(name string, q Subquery, cols ...string) DeleteQuery[T]

WithRecursive is With for a recursive CTE.

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 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
	// contains filtered or unexported fields
}

Error is a structured Momo error.

func (*Error) Detail ¶

func (e *Error) Detail() string

Detail returns the raw driver detail captured by the classifier (it can quote row values, so handle it as sensitive). Without a captured detail it falls back to the underlying error text, or "" when there is none.

func (*Error) Error ¶

func (e *Error) Error() string

Error returns the kind, the operation and its identifying fields, followed by the text of the wrapped error. It never renders the raw driver detail, which can quote row values.

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 ExcludedExpr ¶ added in v0.3.0

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

ExcludedExpr is the EXCLUDED.<col> reference returned by Column.Excluded. Besides being an Expression[T], it carries value comparisons for CASE and DO UPDATE WHERE predicates, for example hostname.Excluded().Ne("").

func (ExcludedExpr[T]) AppendSQL ¶ added in v0.3.0

func (e ExcludedExpr[T]) AppendSQL(b *Builder)

AppendSQL writes the EXCLUDED column reference to b.

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 ExprSelect ¶ added in v0.3.0

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

ExprSelect is a SELECT of explicit expressions that needs no result type: a FROM-less list of values or parameters with an optional WHERE, or a plain SELECT over one source. It is a Subquery, so it is the row source of InsertQuery.FromSelect and also works as an EXISTS operand:

momo.SelectExprs(momo.Value(1)).From(meters).Where(meter.Legacy.Eq(true))

ExprSelect is an immutable value: every method returns a new one.

func SelectExprs ¶ added in v0.3.0

func SelectExprs(cols ...Expr) ExprSelect

SelectExprs starts a SELECT of the given expressions with no FROM clause. Add a source with From and conditions with Where. Pass every literal as a momo.Value so it is bound as a parameter and nothing is concatenated into the SQL text.

func (ExprSelect) AppendSQL ¶ added in v0.3.0

func (s ExprSelect) AppendSQL(b *Builder)

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

func (ExprSelect) CrossJoin ¶ added in v0.8.0

func (s ExprSelect) CrossJoin(src TableSource) ExprSelect

CrossJoin adds a CROSS JOIN, like SelectQuery.CrossJoin.

func (ExprSelect) From ¶ added in v0.3.0

func (s ExprSelect) From(src TableSource) ExprSelect

From sets the FROM source. Without it the SELECT has no FROM clause, which PostgreSQL and SQLite accept and MySQL accepts only as FROM DUAL (added automatically when a WHERE is present). The soft-delete filter is added only when src is a TableRef whose SoftDelete marker is set: generated Table refs carry it, a hand-built TableRef{Name: ...} does not and then counts soft-deleted rows, so set SoftDelete and SoftDeleteKind on it.

func (ExprSelect) GroupBy ¶ added in v0.8.0

func (s ExprSelect) GroupBy(es ...Expr) ExprSelect

GroupBy adds GROUP BY expressions. An As(...) alias is rejected with ErrValidation, as in SelectQuery.GroupBy. This is what lets a grouped INSERT ... SELECT source have more columns than a typed Select can map.

func (ExprSelect) Having ¶ added in v0.8.0

func (s ExprSelect) Having(ps ...Predicate) ExprSelect

Having adds HAVING predicates combined with AND.

func (ExprSelect) Join ¶ added in v0.8.0

func (s ExprSelect) Join(src TableSource, on ...Predicate) ExprSelect

Join adds an INNER JOIN, like SelectQuery.Join.

func (ExprSelect) LeftJoin ¶ added in v0.8.0

func (s ExprSelect) LeftJoin(src TableSource, on ...Predicate) ExprSelect

LeftJoin adds a LEFT JOIN, like SelectQuery.LeftJoin.

func (ExprSelect) OrderBy ¶ added in v0.8.0

func (s ExprSelect) OrderBy(items ...Orderable) ExprSelect

OrderBy appends ORDER BY items: an Order such as Column.Asc() or any Expression, which orders ascending.

func (ExprSelect) Where ¶ added in v0.3.0

func (s ExprSelect) Where(ps ...Predicate) ExprSelect

Where adds predicates combined with AND. On a FROM-less select the WHERE guards the single row, so the select yields zero rows when it is false.

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 ArrayOf ¶ added in v0.8.0

func ArrayOf[T any](elems ...Expression[T]) Expression[[]T]

ArrayOf is the array constructor ARRAY[e1, e2, ...] of the element expressions, an Expression[[]T]. Each element renders inline: a column, a call, or a bound Value. Use it where SQL needs an array built from other expressions, such as the path argument of jsonb_set:

momo.ArrayOf(sqlfn.Concat(momo.Value("at_execution_"), momo.Value(kind)))

An empty list fails the build with ErrValidation, because PostgreSQL cannot type ARRAY[]. Arrays are PostgreSQL only; other dialects fail with ErrUnsupported. To bind a Go slice as one array parameter use Value.

func ArraySubquery ¶ added in v0.8.0

func ArraySubquery[T any](sub Subquery) Expression[[]T]

ArraySubquery is ARRAY(subquery): the rows of a one-column subquery collected into an array, an Expression[[]T] where T is the Go type of that column. A subquery of known width that selects more or fewer columns fails the build with ErrValidation (a RawSubquery has no known width, so the database checks it). The subquery may be a SelectQuery or a set operation such as a UNION, so ARRAY(SELECT a FROM x UNION SELECT b FROM y) scans into []T. The subquery renders its own parentheses. An empty result is an empty array, not NULL. Arrays are PostgreSQL only; other dialects fail with ErrUnsupported.

func Coalesce ¶

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

Coalesce returns the first non-NULL argument.

func JoinTypedSQL ¶ added in v0.4.0

func JoinTypedSQL[T any](sep string, parts ...Expr) Expression[T]

JoinTypedSQL is JoinSQL with a declared result type, for a join used in a projection or a typed comparison, for example a "+" sum of columns. Momo cannot verify that the declaration is truthful.

func Typed ¶ added in v0.4.0

func Typed[T any](e Expr) Expression[T]

Typed wraps e as an Expression[T], for an expression whose SQL is built elsewhere and whose result type the caller knows. It does not change the SQL e renders and adds no comparison methods; embed Ops[T] in your own type for those. Typed is an extension point: e must write identifiers through Builder.Ident and values through Builder.Arg.

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 FieldNamer ¶ added in v0.8.0

type FieldNamer interface {
	// ColumnName returns the column name for a Go field name.
	ColumnName(goField string) string
}

FieldNamer names the column of a Go struct field. Every preset of gen/naming (naming.Momo{}, naming.Prisma{}, ...) is one.

type Filter ¶ added in v0.4.0

type Filter struct {
	And   []Filter
	Or    []Filter
	Not   *Filter
	Field string
	Op    string
	Value any
}

Filter is a runtime-built predicate tree, for filters that arrive from outside the program (a query string, an API body). Exactly one of And, Or, Not or Field must be set on each node. Field is an external name resolved through an Allowlist; it never becomes SQL text itself. Op is one of eq, ne, lt, le, gt, ge, in, null, notnull and like; Value is bound as a parameter (null and notnull ignore it, in takes a slice, like takes a string pattern already escaped with EscapeLike).

func (Filter) Predicate ¶ added in v0.4.0

func (f Filter) Predicate(al Allowlist) (Predicate, error)

Predicate builds the predicate. A field outside the allowlist, an unknown Op, a malformed node or a Value of the wrong shape returns an error matching ErrValidation. The errors never echo the offending name or operator, which may be attacker controlled.

type FilteredAgg ¶ added in v0.4.0

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

FilteredAgg is an aggregate with a FILTER (WHERE ...) clause, built with Agg.Filter. It is an Expression[T], carries the comparison methods HAVING needs, and can be turned into a window function with Over.

func (FilteredAgg[T]) AppendSQL ¶ added in v0.4.0

func (f FilteredAgg[T]) AppendSQL(b *Builder)

AppendSQL writes the aggregate and its FILTER clause to b.

func (FilteredAgg[T]) As ¶ added in v0.4.0

func (f FilteredAgg[T]) As(alias string) Aliased[T]

As names the aggregate in a select list.

func (FilteredAgg[T]) Over ¶ added in v0.4.0

func (f FilteredAgg[T]) Over(w WindowSpec) WindowExpr[T]

Over turns the filtered aggregate into a window function.

type Flagged ¶ added in v0.4.0

type Flagged interface{ Flags() uint16 }

Flagged is implemented by column descriptors, whose flags decide masking. A custom expression that wraps a column implements it by returning FlagsOf of the wrapped expression, so a sensitive column stays masked through the wrapper. Flagged is an extension point; see Ops.

type ForeignKeyOpts ¶ added in v0.5.0

type ForeignKeyOpts struct{}

ForeignKeyOpts carries the optional referential actions of a foreign key declaration. Its methods are read statically by the generator and are no-ops at runtime.

func (*ForeignKeyOpts) OnDelete ¶ added in v0.5.0

func (o *ForeignKeyOpts) OnDelete(action string) *ForeignKeyOpts

OnDelete sets the ON DELETE action of a foreign key ("CASCADE", "RESTRICT", "SET NULL", "SET DEFAULT" or "NO ACTION"). Read statically; no-op at runtime.

func (*ForeignKeyOpts) OnUpdate ¶ added in v0.5.0

func (o *ForeignKeyOpts) OnUpdate(action string) *ForeignKeyOpts

OnUpdate sets the ON UPDATE action of a foreign key, with the same values as OnDelete. Read statically; no-op at runtime.

type FrameBound ¶ added in v0.4.0

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

FrameBound is one end of a window frame.

func CurrentRow ¶ added in v0.4.0

func CurrentRow() FrameBound

CurrentRow is CURRENT ROW.

func Following ¶ added in v0.4.0

func Following(n int) FrameBound

Following is n FOLLOWING. n must not be negative.

func Preceding ¶ added in v0.4.0

func Preceding(n int) FrameBound

Preceding is n PRECEDING. n must not be negative.

func UnboundedFollowing ¶ added in v0.4.0

func UnboundedFollowing() FrameBound

UnboundedFollowing is UNBOUNDED FOLLOWING (an end bound).

func UnboundedPreceding ¶ added in v0.4.0

func UnboundedPreceding() FrameBound

UnboundedPreceding is UNBOUNDED PRECEDING (a start bound).

type FuncExpr ¶ added in v0.3.0

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

FuncExpr is a function call whose SQL result type corresponds to T. It is an Expression[T] and can be aliased with As.

func Func ¶ added in v0.3.0

func Func[T any](name string, args ...any) FuncExpr[T]

Func is a typed call of the SQL function name. name must be a plain identifier, optionally schema-qualified (lower, pg_catalog.lower); anything else fails the build with ErrValidation, so a name cannot smuggle SQL. An argument that is an Expr (a column, another call, Value, ...) renders inline; any other Go value is bound as a parameter. Momo cannot verify that T is what the function returns.

func (FuncExpr[T]) AppendSQL ¶ added in v0.3.0

func (f FuncExpr[T]) AppendSQL(b *Builder)

AppendSQL writes the function call to b.

func (FuncExpr[T]) As ¶ added in v0.3.0

func (f FuncExpr[T]) As(alias string) Aliased[T]

As names the call in a select list: name(args) AS alias.

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.

func NewNPlusOneHook ¶ added in v0.4.0

func NewNPlusOneHook(threshold int, report func(NPlusOneFinding)) Hook

NewNPlusOneHook returns a development hook that calls report once per statement shape per scope when the shape has run more than threshold times (a threshold below 1 counts as 1). Transaction control and EXPLAIN statements are ignored. A scope tracks at most 1024 distinct shapes; later new shapes in the same scope are not counted. report runs synchronously on the querying goroutine and must not query the database; a panic in report is recovered and dropped.

type HookSet ¶ added in v0.4.0

type HookSet[T any] struct {
	BeforeInsert func(ctx context.Context, m *T) error
	BeforeUpdate func(ctx context.Context, m *T) error
	BeforeDelete func(ctx context.Context, m *T) error
	AfterLoad    func(ctx context.Context, m *T) error
}

HookSet holds the typed model hooks of Meta[T]. Generated code or hand-written metadata sets the fields; the builders call them with the statement's context.

A hook runs only for the builder kind it names and only when it is set:

  • BeforeInsert runs for each model of an Insert (Model, Models, Upsert) before id generators run and before the statement is built, so it sees a zero id and an id it sets is not written back to the caller. It does not run for Values, ValuesRows or FromSelect inserts, which have no model. The hook receives a copy of the model, so it may change the values that are written without touching the caller's value.
  • BeforeUpdate runs for a Model update, on the caller's model.
  • BeforeDelete runs for a Model delete and a Model SoftDelete, on the caller's model.
  • AfterLoad runs for every model returned by All, One, First, Each and Iter, and for every model a write scans from its RETURNING rows (ModelRows.ScanModel), after the row is scanned.

momo generate fills the hooks from the model's own methods: a method named BeforeInsert, BeforeUpdate, BeforeDelete or AfterLoad with the signature func(context.Context) error becomes the hook of that name, with the model as its receiver. A method with a hook name and another signature is a generator diagnostic, and a model without such methods keeps the zero HookSet.

Set the hooks on the metadata value that MomoMeta returns, for example in the declaration of the package-level Meta:

var documentMeta = momo.Meta[Document]{ /* ... */ Hooks: momo.HookSet[Document]{
	BeforeInsert: func(ctx context.Context, d *Document) error { /* ... */ return nil },
}}

Generated metadata is not edited by hand; assign the field once at start-up before any statement runs. Momo never mutates a Meta, so concurrent reads are safe once it is set.

Interceptors run before the Before hooks, so a statement an interceptor denies never reaches them. A Before hook can still change the caller's model (BeforeUpdate, BeforeDelete) when the statement then fails to build or run, for example on the unfiltered-mutation guard or a database error.

A returned error aborts the statement before any SQL runs (a Before hook) or ends the read (AfterLoad); it is wrapped in an *Error of kind ErrValidation that still reaches the hook's own error with errors.Is. A hook may set values through the *T pointer but cannot change the SQL. Hooks run on the execution methods (Exec, Rows, All and so on); SQL does not run them. The hook dispatch itself performs no reflection and allocates nothing (TestModelHookDispatchAllocations measures runModelHook and the AfterLoad wrapper); the builders around it do allocate, and a hook that allocates is the caller's cost.

Relations loaded with Load or Preload and joined relations are scanned by the relation machinery and do not run the AfterLoad hook of the related type.

type HybridQuery ¶ added in v0.5.0

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

HybridQuery is a prepared hybrid search; see HybridSearch.

func HybridSearch ¶ added in v0.5.0

func HybridSearch[T any, PT Modeler[T]](q Querier, spec HybridSpec) HybridQuery[T]

HybridSearch builds the fused full-text and vector query for model T. It runs on PostgreSQL with pgvector only; on another dialect SQL and All return an error that wraps ErrUnsupported. The model needs a single-column primary key. Soft-deleted rows are excluded.

The statement binds, in order: the text configuration, the query text, the text candidate count, the query vector, the vector candidate count, the RRF constant and the limit.

func (HybridQuery[T]) All ¶ added in v0.5.0

func (h HybridQuery[T]) All(ctx context.Context) ([]HybridResult[T], error)

All runs the query and returns the fused rows, best first.

func (HybridQuery[T]) SQL ¶ added in v0.5.0

func (h HybridQuery[T]) SQL() (Compiled, error)

SQL compiles the statement without running it. The result has the seven bound arguments described at HybridSearch.

type HybridResult ¶ added in v0.5.0

type HybridResult[T any] struct {
	Row T
	// Score is the sum of 1/(RRFK+rank) over the legs the row appears in.
	Score float64
}

HybridResult is one fused row.

type HybridSpec ¶ added in v0.5.0

type HybridSpec struct {
	// Text is the full-text column of the model, a text column of the same
	// table as Vector.
	Text       Column[string]
	TextConfig string // regconfig name such as "english"; bound as a parameter
	Query      string // websearch_to_tsquery input
	Vector     VectorColumn[vector.Vector]
	// QueryVector must match the column's dimension (any dimension for a
	// dimensionless column).
	QueryVector vector.Vector
	// TextCandidates and VectorCandidates are the candidates each leg
	// contributes, 1..1000; zero means DefaultHybridCandidates (100).
	TextCandidates   int
	VectorCandidates int
	// RRFK is the RRF rank constant, at least 1; zero means DefaultHybridRRFK (60).
	RRFK int
	// Limit is the number of rows returned, 1..1000.
	Limit int
}

HybridSpec describes a PostgreSQL hybrid search: a full-text leg and a vector leg over one model, fused by reciprocal rank fusion (RRF).

Each leg picks its own candidates (the best TextCandidates rows by ts_rank_cd, and the nearest VectorCandidates rows by cosine distance), ranks them, and a row scores the sum of 1/(RRFK+rank) over the legs it appears in. Ties on the score are broken by primary key ascending, within each leg's ranking and in the final order, so the result does not depend on row or insertion order. The one exception is the candidate cut-off: the vector leg orders by distance alone (so an hnsw or ivfflat index can serve it), and when rows tie on distance at the cut-off, which of them become candidates is not defined.

The vector leg takes the dense vector type only (not halfvec, sparsevec or bit) and skips rows with a NULL embedding and rows whose cosine distance is NaN (a zero vector). The text leg computes to_tsvector over the column with the bound TextConfig; a GIN expression index needs a constant configuration in its definition, and a bound regconfig parameter may not match it, so without a matching plan the text leg scans the table.

type IDGen ¶ added in v0.4.0

type IDGen struct {
	// Column is the index into Meta.Columns.
	Column int
	// Name is the generator name.
	Name string
}

IDGen declares a Go-side id generator for one column of a model: the generated code sets it from the idgen tag or the IDGen companion call. Name is a built-in generator (cuid, uuid, ulid) or one registered with WithIDGenerator.

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) AppendSQL ¶

func (c ImmutableColumn) 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) AsExpr ¶

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

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

func (ImmutableColumn) Asc ¶

func (c ImmutableColumn) Asc() Order

Asc orders by the column ascending. The order item is the column itself, a ColumnRef, so keyset pagination recognizes a primary-key column.

func (ImmutableColumn) ColumnName ¶

func (c ImmutableColumn) ColumnName() string

ColumnName returns the unquoted column name.

func (ImmutableColumn) Desc ¶

func (c ImmutableColumn) Desc() Order

Desc orders by the column descending.

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) Flags ¶

func (c ImmutableColumn) Flags() uint16

Flags returns the generated column flags.

func (ImmutableColumn) ParseCmp ¶ added in v0.4.0

func (c ImmutableColumn) ParseCmp(op, raw string) (Predicate, error)

ParseCmp parses raw with the column's Go type and returns the comparison predicate for op, one of eq, ne, lt, le, gt and ge. An unknown op or a value that does not parse is an *Error with Kind ErrValidation. It is meant for a filter value that arrives as text from outside; the column comes from the generated ColumnByName allowlist.

func (ImmutableColumn) ParseEq ¶ added in v0.4.0

func (c ImmutableColumn) ParseEq(raw string) (Predicate, error)

ParseEq parses raw with the column's Go type and returns the equality predicate on it. A value that does not parse is an *Error with Kind ErrValidation; the error does not echo raw, which may be attacker controlled.

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) Table ¶

func (c ImmutableColumn) Table() string

Table returns the qualifier (table name or alias).

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. Zero marks a dimensionless column: a distance argument of any size is accepted and checked against itself.

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) ParseCmp ¶ added in v0.4.0

func (c ImmutableVectorColumn) ParseCmp(string, string) (Predicate, error)

ParseCmp always fails: a vector has no text form to filter by.

func (ImmutableVectorColumn) ParseEq ¶ added in v0.4.0

func (c ImmutableVectorColumn) ParseEq(string) (Predicate, error)

ParseEq always fails: a vector has no text form to filter by.

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) Unique ¶ added in v0.5.0

func (o *IndexOpts) Unique() *IndexOpts

Unique makes the index a unique index. 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 InsertIDExecer ¶ added in v0.4.0

type InsertIDExecer interface {
	ExecInsertID(ctx context.Context, sql string, args []any) (rows, lastInsertID int64, err error)
}

InsertIDExecer is implemented by drivers, transactions and pinned connections that report the auto-increment key of an INSERT (MySQL and MariaDB LAST_INSERT_ID). The key is meaningful for a single-row insert only.

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, and explicit serial ids do not advance the serial sequence either, so later generated keys may collide with them; the caller must setval the sequence. FromSelect cannot supply serial ids at all.

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. On MySQL and MariaDB the statement renders ON DUPLICATE KEY UPDATE, and a conflict target that is not the table's only unique key fails with ErrUnsupported; a dialect with neither form fails with ErrUnsupported.

func (InsertQuery[T]) AppendSQL ¶ added in v0.5.0

func (q InsertQuery[T]) AppendSQL(b *Builder)

AppendSQL renders the statement in parentheses as a WITH clause body, as SQL would, without running interceptors or the BeforeInsert hook. Only PostgreSQL accepts the result.

func (InsertQuery[T]) Copy ¶ added in v0.4.0

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

Copy streams ms through the driver's Copier (PostgreSQL COPY) and reports the rows written. It writes the same columns as a model insert (Omit applies, identity and read-only columns are never written, an unset created-at or updated-at field takes the statement time, Go-side defaults and field validators run) but binds no parameters, so there is no parameter limit and no chunking: one COPY is one atomic statement.

Copy returns ErrUnsupported when the driver has no COPY path (SQLite, and PostgreSQL through FromSQLDB). Model, Models, Values, ValuesRows, FromSelect, Returning, OnConflict and Upsert cannot be combined with it (ErrValidation): COPY has no conflict handling and returns no rows.

Copy respects WithTxSetup. On a DB made with WithImplicitTx it runs inside its own short transaction, so the setup functions run before the COPY. On a DB pinned with OnConn that has WithTxSetup it fails closed with ErrUnsupported, because the setup could not run. On a Tx it uses the transaction's own COPY path. Query hooks see one event with Op "copy" and no arguments, so no value reaches a hook.

Copy runs the insert interceptors (they can deny it, but a predicate they add is ignored, as for any insert) and the BeforeInsert hook of the model over a copy of ms, so a hook that stamps a tenant applies to every row. It skips OnConflict handling, RETURNING and the chunking of Exec. It counts as a write for NewUnitOfWork, so later reads on a Replica handle in the same unit of work stay on the primary.

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]) FromSelect ¶ added in v0.3.0

func (q InsertQuery[T]) FromSelect(sel Subquery, cols ...ColumnRef) InsertQuery[T]

FromSelect makes the rows of a SELECT the source of the INSERT, rendering INSERT INTO t (cols) SELECT ... in one atomic statement. cols are the typed target columns of T, in the order of the select list; the select must yield exactly that many columns, otherwise the build fails with ErrValidation (a RawSubquery or set operation has no known width, so the database checks it).

The select is the row source, for example a FROM-less select guarded by NOT EXISTS so the row is inserted only when none exists yet:

momo.Insert[Meter](db).
	FromSelect(
		momo.SelectExprs(momo.Value(p), momo.Value(id), momo.Value("distinct")).
			Where(momo.NotExists(momo.SelectExprs(momo.Value(1)).From(meterTable).Where(meter.Product.Eq(p)))),
		meter.Product, meter.ID, meter.Kind).
	OnConflict(meter.Product, meter.ID).DoNothing().
	Exec(ctx)

FromSelect composes with OnConflict (DoNothing, DoUpdate and their Where), with Returning, ReturningAll and Rows, and with Upsert[T], whose default DO UPDATE list then covers the listed columns only. The bind parameters of the select come first, then those of the ON CONFLICT part; each dialect numbers them in order. Exec reports the rows inserted (0 when the select yields no row or every row conflicts).

Only the listed columns are written. Nothing is injected: the automatic created-at and updated-at timestamps, Go-side defaults and field validators do not apply, so list those columns and select their values yourself. A column that is an identity, read-only, or otherwise not writable by INSERT is refused with ErrValidation, as are an unmapped or repeated column, an empty list, and combining FromSelect with Model, Models, Values, ValuesRows or Omit. Calling FromSelect again replaces the source. FromSelect runs no model hooks.

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, except in a column tagged dbdefault: when that field holds its zero value (or a nil pointer) the column is left out of the INSERT so the database default applies, and Exec reads the stored value back into the model with RETURNING. See Models for several rows and the dialects that cannot do this.

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.

A dbdefault column that is zero in every row leaves the column list; when some rows set it and others do not, the unset rows write DEFAULT. SQLite and Unnest cannot put DEFAULT in a row and fail with ErrUnsupported for such a mix; MySQL has no RETURNING to read the value back and fails with ErrUnsupported when a dbdefault column is zero. Exec writes the stored values of the unset rows back into the caller's elements after the statement committed. A single Model that a conflict action skips reports 0 rows affected and stays unchanged. A bulk insert that returns fewer rows than models is matched by key (conflict target, else primary key) when the key is written from the model and not a zero dbdefault column; unmatched models stay untouched, and Exec fails only when DoNothing left a dbdefault column unread. Without a usable key a short result fails with ErrUnsupported and writes nothing back; the insert is rolled back (to a savepoint inside a Tx; a pinned OnConn handle keeps the rows and says so). A caller's own Returning list turns the automatic read-back off: Rows returns the columns it names.

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]) One ¶ added in v0.7.0

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

One runs a single-row INSERT and returns its RETURNING row: ErrNotFound when no row came back (ON CONFLICT DO NOTHING, or a DO UPDATE whose WHERE did not hold) and ErrTooManyRows when more than one did. Without a Returning list it returns every column, as ReturningAll does. On a dialect without RETURNING (MySQL, and MariaDB for UPDATE and DELETE, where momo has no RETURNING form) it fails with ErrUnsupported.

A multi-row Models insert is refused with ErrValidation before anything runs: a bulk insert can be split into chunks that commit together, which One cannot roll back after the fact. Use Rows for a bulk insert. AfterLoad runs on the returned row before the write commits, so an AfterLoad error rolls an implicit transaction back, unlike SelectQuery.One.

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]) Unnest ¶ added in v0.4.0

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

Unnest switches a Model or Models insert to one statement that binds one array parameter per column. It is a method of InsertQuery and is not the package-level FROM source Unnest (see UnnestValues), which turns bound arrays into rows of a SELECT.

INSERT INTO t (c1, c2) SELECT * FROM unnest($1::bigint[], $2::text[])

The statement has as many bind parameters as the table has written columns, however many rows are inserted, so the bind-parameter limit does not split the insert into chunks. The column array types come from the table schema recorded in the generated metadata, so a model without a table schema fails with ErrValidation. Generated keys come back through RETURNING as for any insert. PostgreSQL only: other dialects fail with ErrUnsupported. Values and ValuesRows inserts are not supported, because their columns can differ per row.

Column values are bound as typed slices (int, float, bool, string, time.Time and []byte columns, as []*T when a value is NULL). An element type this does not know (uuid, json, decimal, a named enum) falls back to []any, which pgx may reject for the array type; use Models for such a model. OnConflict, Returning, hooks, interceptors, automatic timestamps and id generators work as for Models. A sensitive column's array is redacted in query events.

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.

func (InsertQuery[T]) With ¶ added in v0.5.0

func (q InsertQuery[T]) With(name string, body Subquery, cols ...string) InsertQuery[T]

With prefixes the INSERT with a common table expression that FromSelect's select can read: WITH x AS (...) INSERT INTO t (cols) SELECT ... FROM x. cols optionally names the CTE's columns. The body may be a SelectQuery, an ExprSelect, a Union or (PostgreSQL only) another INSERT, UPDATE or DELETE with RETURNING; a data-modifying body on another dialect fails with ErrUnsupported. MySQL and MariaDB do not accept WITH before INSERT and fail with ErrUnsupported; put the WITH inside the select there. A WITH clause cannot be combined with a bulk insert that is split into chunks at the parameter limit (ErrValidation), since it would run once per chunk.

func (InsertQuery[T]) WithRecursive ¶ added in v0.5.0

func (q InsertQuery[T]) WithRecursive(name string, body Subquery, cols ...string) InsertQuery[T]

WithRecursive is With for a recursive CTE.

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 Interceptor ¶ added in v0.4.0

type Interceptor func(ctx context.Context, op *Operation) error

Interceptor may append predicates or return an error to deny the statement. It runs before SQL is built, in registration order. It is attached per handle with WithInterceptor and inherited by transactions begun from that handle only.

An interceptor is an application-level guard, not a database security boundary. It covers only the primary table of these typed statements of the handle: Select (All, One, First, Each, Iter, Count, Explain), Insert, Upsert, Update, BulkUpdate, SoftDelete, Delete, Merge, Truncate and InsertQuery.Copy. It does NOT cover: relation loading (Load, Preload, LoadCounts); the tables of joined relations (only the primary table is filtered); the association helpers (Link, Unlink, Replace and the like); subqueries and other sources rendered inside a statement (subquery operands, UPDATE ... FROM, the FromSelect source of an insert); raw SQL of any kind (Exec, MapRows, Scalars, Query, QueryRows, SQL fragments and RawSubquery); the raw driver CopyFrom (InsertQuery.Copy does run the insert interceptors, deny only); and anything run on the driver directly. A predicate an interceptor adds to an insert, a Copy or a Truncate is ignored, and one added to a Merge fails it with ErrValidation. SQL() runs the interceptors with a background context for Select, Update (BulkUpdate, SoftDelete), Delete and Merge; InsertQuery.SQL (also for an Upsert) runs neither interceptors nor hooks. The internal count that tells a stale version from a missing row after a version-checked update is a Select, so select interceptors apply to it. See docs/PRIVACY_RULES.md.

type LitExpr ¶ added in v0.8.0

type LitExpr[T Literal] struct {
	Ops[T]
	// contains filtered or unexported fields
}

LitExpr is a value written into the SQL text instead of bound. Build it with Lit.

func Lit ¶ added in v0.8.0

func Lit[T Literal](v T) LitExpr[T]

Lit renders v inline, not as a bound parameter. Use it where two occurrences of an expression must be the same text, such as the same COALESCE(col, 0) in the select list and in GROUP BY: bound parameters make them differ and PostgreSQL rejects the grouping (42803).

It accepts integers, floats, bool, string and time.Time, and the build fails with ErrValidation, on every dialect, for a value it cannot render safely:

  • a string renders as a standard SQL string literal with each ' doubled; a string containing a backslash or a NUL byte is refused, because MySQL (and PostgreSQL with standard_conforming_strings off) read a backslash as an escape inside a literal;
  • a time.Time renders as DATE 'YYYY-MM-DD' (a quoted date elsewhere) and must be exactly 00:00:00 UTC; a value with a time of day is refused rather than silently truncated;
  • a NaN or infinite float is refused.

Prefer a bound Value everywhere the text need not match.

func (LitExpr[T]) AppendSQL ¶ added in v0.8.0

func (l LitExpr[T]) AppendSQL(b *Builder)

AppendSQL writes the literal to b.

func (LitExpr[T]) As ¶ added in v0.8.0

func (l LitExpr[T]) As(alias string) Aliased[T]

As names the expression in a select list.

type Literal ¶ added in v0.8.0

type Literal interface {
	~int | ~int8 | ~int16 | ~int32 | ~int64 |
		~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 |
		~float32 | ~float64 | ~bool | ~string | time.Time
}

Literal is the constraint of Lit: the types that render safely as an SQL literal. A string is accepted, and Lit refuses the characters that would let it escape its quotes (see Lit).

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, RelationLoader (from Relation.Scope and Relation.Then) and Relation.Joined implement it.

type MergeQuery ¶ added in v0.4.0

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

MergeQuery builds a MERGE into one model table. It is a value: every method returns a copy.

func Merge ¶ added in v0.4.0

func Merge[T any, PT Modeler[T]](q Querier, source Subquery) MergeQuery[T]

Merge starts a MERGE into T from a source, which the statement names src. PostgreSQL 15 or later only (dialect.Merge); RETURNING on MERGE needs PostgreSQL 17 or later. The dialect is checked when the statement is built, before any server call, and the server version is read through ServerVersioner (or, on a Tx or an OnConn handle, on that connection) just before the statement runs. A driver that cannot report its version fails with ErrUnsupported. The version is read on every Exec or Rows call.

Matched rows are updated through WhenMatchedUpdate, which keeps the updated-at and version columns up to date like Update does; the values of WhenNotMatchedInsert come from the source, so field validators do not run on them. Nothing is written without On, and at least one WHEN clause is required.

func (MergeQuery[T]) Exec ¶ added in v0.4.0

func (m MergeQuery[T]) Exec(ctx context.Context) (Result, error)

Exec runs the MERGE and reports the rows it inserted or updated.

func (MergeQuery[T]) On ¶ added in v0.4.0

func (m MergeQuery[T]) On(ps ...Predicate) MergeQuery[T]

On sets the join condition between the target table and src. Several predicates are ANDed. Reference the source as src, for example with TypedSQL.

func (MergeQuery[T]) Returning ¶ added in v0.4.0

func (m MergeQuery[T]) Returning(cols ...ColumnRef) MergeQuery[T]

Returning appends a RETURNING list; read the rows with Rows. It needs PostgreSQL 17 or later.

func (MergeQuery[T]) Rows ¶ added in v0.4.0

func (m MergeQuery[T]) Rows(ctx context.Context) (*ModelRows[T], error)

Rows runs the MERGE and returns its RETURNING rows. It fails with ErrValidation when no Returning list was set and with ErrUnsupported on a server older than PostgreSQL 17.

func (MergeQuery[T]) SQL ¶ added in v0.4.0

func (m MergeQuery[T]) SQL() (Compiled, error)

SQL renders the statement without executing it. It checks the dialect but not the server version, which needs a connection.

func (MergeQuery[T]) WhenMatchedUpdate ¶ added in v0.4.0

func (m MergeQuery[T]) WhenMatchedUpdate(assigns ...Assignment) MergeQuery[T]

WhenMatchedUpdate adds WHEN MATCHED THEN UPDATE SET with the assignments. The primary key, version, soft-delete, created-at and updated-at columns cannot be assigned directly, as in Update.

func (MergeQuery[T]) WhenNotMatchedInsert ¶ added in v0.4.0

func (m MergeQuery[T]) WhenNotMatchedInsert(cols []ColumnRef, values ...Expr) MergeQuery[T]

WhenNotMatchedInsert adds WHEN NOT MATCHED THEN INSERT with one value expression per column. An identity column in the list is written with OVERRIDING SYSTEM VALUE.

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]

	// IDGens declares Go-side id generators per column; an insert fills the
	// column when its field holds the zero value. See WithIDGenerator.
	IDGens []IDGen
	// Hooks are the typed model hooks the write and read builders call. See
	// HookSet.
	Hooks HookSet[T]
	// View marks a model backed by a database view. Generated code sets it
	// for a model that embeds View. Insert, Upsert, Update, SoftDelete,
	// BulkUpdate, Delete and Merge refuse such a model with ErrValidation.
	View bool
}

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. Deferred directly (defer rows.Close()), it also rolls back an implicit transaction when a panic unwinds through the caller, so a RETURNING write is never committed by a panicking iteration.

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 NPlusOneFinding ¶ added in v0.4.0

type NPlusOneFinding struct {
	Shape   string // SQL with placeholders and IN lists collapsed
	Example string // the SQL text of the statement that crossed the threshold
	Count   int
}

NPlusOneFinding reports a statement shape that repeated more than the threshold within one query scope. Count is the number of executions when the threshold was first exceeded.

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. On PostgreSQL a native $n binds positional argument n (a reused $n shares one bind); a fragment uses one style, so mixing $n with ? or @name is a build error, and so is a $n off PostgreSQL. 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) AppendSQL ¶ added in v0.4.0

func (c NullColumn) AppendSQL(b *Builder)

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) AsExpr ¶ added in v0.4.0

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

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

func (NullColumn) Asc ¶ added in v0.4.0

func (c NullColumn) Asc() Order

Asc orders by the column ascending. The order item is the column itself, a ColumnRef, so keyset pagination recognizes a primary-key column.

func (NullColumn) ColumnName ¶ added in v0.4.0

func (c NullColumn) ColumnName() string

ColumnName returns the unquoted column name.

func (NullColumn) Desc ¶ added in v0.4.0

func (c NullColumn) Desc() Order

Desc orders by the column descending.

func (NullColumn[T]) EqPtr ¶ added in v0.8.0

func (c NullColumn[T]) EqPtr(v *T) Predicate

EqPtr compares the column with *v, or tests IS NULL when v is nil. It never renders "= NULL", which matches no row.

func (NullColumn) Excluded ¶ added in v0.4.0

func (c NullColumn) Excluded() ExcludedExpr[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 (NullColumn) Flags ¶ added in v0.4.0

func (c NullColumn) Flags() uint16

Flags returns the generated column flags.

func (NullColumn) ParseCmp ¶ added in v0.4.0

func (c NullColumn) ParseCmp(op, raw string) (Predicate, error)

ParseCmp parses raw with the column's Go type and returns the comparison predicate for op, one of eq, ne, lt, le, gt and ge. An unknown op or a value that does not parse is an *Error with Kind ErrValidation. It is meant for a filter value that arrives as text from outside; the column comes from the generated ColumnByName allowlist.

func (NullColumn) ParseEq ¶ added in v0.4.0

func (c NullColumn) ParseEq(raw string) (Predicate, error)

ParseEq parses raw with the column's Go type and returns the equality predicate on it. A value that does not parse is an *Error with Kind ErrValidation; the error does not echo raw, which may be attacker controlled.

func (NullColumn[T]) SetNull ¶

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

SetNull assigns SQL NULL.

func (NullColumn[T]) SetPtr ¶ added in v0.8.0

func (c NullColumn[T]) SetPtr(v *T) Assignment

SetPtr assigns NULL when v is nil and *v otherwise, so a model field of type *T can be written without branching at the call site. It is an ordinary Assignment: use it in Update.Set, Insert.Values and an upsert DoUpdate.

func (NullColumn) Table ¶ added in v0.4.0

func (c NullColumn) Table() string

Table returns the qualifier (table name or alias).

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) AppendSQL ¶

func (c NullReadOnlyColumn) 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) AsExpr ¶

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

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

func (NullReadOnlyColumn) Asc ¶

func (c NullReadOnlyColumn) Asc() Order

Asc orders by the column ascending. The order item is the column itself, a ColumnRef, so keyset pagination recognizes a primary-key column.

func (NullReadOnlyColumn) ColumnName ¶

func (c NullReadOnlyColumn) ColumnName() string

ColumnName returns the unquoted column name.

func (NullReadOnlyColumn) Desc ¶

func (c NullReadOnlyColumn) Desc() Order

Desc orders by the column descending.

func (NullReadOnlyColumn[T]) EqPtr ¶ added in v0.8.0

func (c NullReadOnlyColumn[T]) EqPtr(v *T) Predicate

EqPtr compares the column with *v, or tests IS NULL when v is nil.

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) Flags ¶

func (c NullReadOnlyColumn) Flags() uint16

Flags returns the generated column flags.

func (NullReadOnlyColumn) ParseCmp ¶ added in v0.4.0

func (c NullReadOnlyColumn) ParseCmp(op, raw string) (Predicate, error)

ParseCmp parses raw with the column's Go type and returns the comparison predicate for op, one of eq, ne, lt, le, gt and ge. An unknown op or a value that does not parse is an *Error with Kind ErrValidation. It is meant for a filter value that arrives as text from outside; the column comes from the generated ColumnByName allowlist.

func (NullReadOnlyColumn) ParseEq ¶ added in v0.4.0

func (c NullReadOnlyColumn) ParseEq(raw string) (Predicate, error)

ParseEq parses raw with the column's Go type and returns the equality predicate on it. A value that does not parse is an *Error with Kind ErrValidation; the error does not echo raw, which may be attacker controlled.

func (NullReadOnlyColumn) Table ¶

func (c NullReadOnlyColumn) Table() string

Table returns the qualifier (table name or alias).

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. Zero marks a dimensionless column: a distance argument of any size is accepted and checked against itself.

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) ParseCmp ¶ added in v0.4.0

func (c NullVectorColumn) ParseCmp(string, string) (Predicate, error)

ParseCmp always fails: a vector has no text form to filter by.

func (NullVectorColumn) ParseEq ¶ added in v0.4.0

func (c NullVectorColumn) ParseEq(string) (Predicate, error)

ParseEq always fails: a vector has no text form to filter by.

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 leaves NULL placement to the database.
	NullsDefault Nulls = iota
	// NullsFirst sorts NULLs before other values.
	NullsFirst
	// NullsLast sorts NULLs after other values.
	NullsLast
)

type Number ¶

type Number interface{ ~int64 | ~float64 }

Number is the constraint of the numeric aggregates SUM.

type Operation ¶ added in v0.4.0

type Operation struct {
	// Kind is "select", "insert", "upsert", "update", "delete" or "truncate".
	// Every write path reports one kind:
	//
	//	Insert (and Copy)                       insert
	//	Insert with OnConflict DoNothing        insert
	//	Insert with OnConflict DoUpdate, Upsert upsert
	//	Update, BulkUpdate                      update
	//	SoftDelete, Delete                      delete
	//	Merge with WhenMatchedUpdate            upsert
	//	Merge without WhenMatchedUpdate         insert
	//	Truncate                                truncate
	//
	// ON CONFLICT DO UPDATE and a matched MERGE can overwrite an existing
	// row, so a tenant rule must deny an "upsert" or constrain it (for
	// example by requiring the tenant column).
	Kind  string
	Table string
	// Where starts empty. An interceptor appends predicates to it; they are
	// ANDed to the predicates of a select, update or delete and are rendered
	// in the compiled SQL. Predicates an insert interceptor appends are
	// ignored. They do not count as a caller filter, so an update or delete
	// without a Where of its own is still refused with ErrUnfilteredMutation.
	Where []Predicate
}

Operation is what an interceptor sees and may change before a typed statement compiles.

type Ops ¶ added in v0.4.0

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

Ops gives a scalar expression of result type T the comparison and ordering methods of Column: Eq, Ne, Lt, Le, Gt, Ge, the ...Expr forms, In, NotIn, Between, IsNull, IsNotNull, Asc and Desc. FuncExpr and the expressions of the sqlfn package (CaseExpr, ArithExpr, CastExpr, ConcatExpr, LengthExpr) embed it, so each method exists once.

Ops is an extension point. Embedding momo.Ops[T] in your own struct makes that struct an Expression[T] (an Expression[bool] is a Predicate), which is how code outside this package defines custom expressions. Build the Ops value with NewOps and give it the finished host, then write the host's AppendSQL with Builder.Ident for identifiers and Builder.Arg (or ArgFor) for values, never by concatenating text into the SQL.

The zero value is not bound to a host: rendering a comparison made from it fails the build with ErrValidation.

Example ¶

A custom expression embeds momo.Ops[T], writes identifiers through the builder and binds values as arguments. The result works in Columns, Where and OrderBy like a built-in expression.

// SQL() only compiles the statement, so no connection is needed here.
db := momo.New(pgxdriver.New(nil))
title := momo.NewColumn[string]("documents", "title", 0)

c, err := momo.Select[unitTally](db).
	From(momo.TableRef{Name: "documents"}).
	Columns(newShout(title)).
	Where(newStartsWith(title, "ab")).
	SQL()
if err != nil {
	fmt.Println(err)
	return
}
fmt.Println(c.SQL)
fmt.Println(c.Args)
Output:
SELECT upper("documents"."title") FROM "documents" WHERE "documents"."title" LIKE $1
[ab%]

func NewOps ¶ added in v0.4.0

func NewOps[T any](host Expr) Ops[T]

NewOps binds the embedded comparison methods to host, the expression that embeds the returned Ops. Call it from the host's constructor on a copy of the host whose Ops field is still empty:

u := upper{src: src}
u.Ops = momo.NewOps[string](u)

NewOps is an extension point; see Ops.

func (Ops[T]) Asc ¶ added in v0.4.0

func (o Ops[T]) Asc() Order

Asc orders by the expression ascending.

func (Ops[T]) Between ¶ added in v0.4.0

func (o Ops[T]) Between(lo, hi T) Predicate

Between is inclusive on both ends.

func (Ops[T]) Desc ¶ added in v0.4.0

func (o Ops[T]) Desc() Order

Desc orders by the expression descending.

func (Ops[T]) Eq ¶ added in v0.4.0

func (o Ops[T]) Eq(v T) Predicate

Eq is true when the expression equals v.

func (Ops[T]) EqExpr ¶ added in v0.4.0

func (o Ops[T]) EqExpr(x Expression[T]) Predicate

EqExpr is true when the expression equals x.

func (Ops[T]) Ge ¶ added in v0.4.0

func (o Ops[T]) Ge(v T) Predicate

Ge is true when the expression is greater than or equal to v.

func (Ops[T]) GeExpr ¶ added in v0.4.0

func (o Ops[T]) GeExpr(x Expression[T]) Predicate

GeExpr is true when the expression is greater than or equal to x.

func (Ops[T]) Gt ¶ added in v0.4.0

func (o Ops[T]) Gt(v T) Predicate

Gt is true when the expression is greater than v.

func (Ops[T]) GtExpr ¶ added in v0.4.0

func (o Ops[T]) GtExpr(x Expression[T]) Predicate

GtExpr is true when the expression is greater than x.

func (Ops[T]) In ¶ added in v0.4.0

func (o Ops[T]) In(vs ...T) Predicate

In matches any of vs; an empty list is always false.

func (Ops[T]) InQuery ¶ added in v0.4.0

func (o Ops[T]) InQuery(q Subquery) Predicate

InQuery matches the single-column result of a subquery.

func (Ops[T]) IsDistinctFrom ¶ added in v0.4.0

func (o Ops[T]) IsDistinctFrom(v T) Predicate

IsDistinctFrom is a NULL-safe inequality.

func (Ops[T]) IsNotDistinctFrom ¶ added in v0.4.0

func (o Ops[T]) IsNotDistinctFrom(v T) Predicate

IsNotDistinctFrom is a NULL-safe equality.

func (Ops[T]) IsNotNull ¶ added in v0.4.0

func (o Ops[T]) IsNotNull() Predicate

IsNotNull is true when the expression is not NULL.

func (Ops[T]) IsNull ¶ added in v0.4.0

func (o Ops[T]) IsNull() Predicate

IsNull is true when the expression is NULL.

func (Ops[T]) Le ¶ added in v0.4.0

func (o Ops[T]) Le(v T) Predicate

Le is true when the expression is less than or equal to v.

func (Ops[T]) LeExpr ¶ added in v0.4.0

func (o Ops[T]) LeExpr(x Expression[T]) Predicate

LeExpr is true when the expression is less than or equal to x.

func (Ops[T]) Lt ¶ added in v0.4.0

func (o Ops[T]) Lt(v T) Predicate

Lt is true when the expression is less than v.

func (Ops[T]) LtExpr ¶ added in v0.4.0

func (o Ops[T]) LtExpr(x Expression[T]) Predicate

LtExpr is true when the expression is less than x.

func (Ops[T]) Ne ¶ added in v0.4.0

func (o Ops[T]) Ne(v T) Predicate

Ne is true when the expression differs from v.

func (Ops[T]) NeExpr ¶ added in v0.4.0

func (o Ops[T]) NeExpr(x Expression[T]) Predicate

NeExpr is true when the expression differs from x.

func (Ops[T]) NotIn ¶ added in v0.4.0

func (o Ops[T]) NotIn(vs ...T) Predicate

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

func (Ops[T]) NotInQuery ¶ added in v0.4.0

func (o Ops[T]) NotInQuery(q Subquery) Predicate

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

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 WithIDGenerator ¶ added in v0.4.0

func WithIDGenerator(name string, fn func() string) Option

WithIDGenerator registers the id generator name on this DB, or replaces the built-in of that name (cuid, uuid, ulid). A model column declared with idgen:name takes its id from fn when an insert writes the zero value. fn must return a non-empty string and be safe for concurrent use. An empty id, a panic in fn and a nil fn fail the insert with ErrValidation. A model that names a generator that is neither built in nor registered fails with ErrValidation when it is inserted.

func WithImplicitTx ¶ added in v0.4.0

func WithImplicitTx() Option

WithImplicitTx makes every auto-commit statement on a *DB run inside its own short transaction, so WithTxSetup applies to it too. This covers Select, Insert, Update, Delete, Exec, Scalars and the relation loads. Statements on a *Tx are unaffected, and a *DB pinned with OnConn keeps running on its connection without a transaction.

A streaming read (Iter, Each, Rows) holds its transaction, and so its connection, until the rows are exhausted or closed, then commits. It rolls back instead when iteration failed (a scan or driver error, or a cancelled context). Close the rows, or range to the end or break out of Iter, as for any rows, so the connection returns to the pool.

Each statement is its own transaction: a relation load or a multi-statement call is no longer one snapshot unless you wrap it in DB.Tx. Pipelined (Batch) relation loading and the COPY fast path are not used on the *DB, because they would bypass the setup; bulk inserts use chunked multi-row INSERTs instead. Hooks see the BEGIN and COMMIT of each implicit transaction, and its statements report InTx.

func WithInterceptor ¶ added in v0.4.0

func WithInterceptor(i Interceptor) Option

WithInterceptor attaches i to the handle. Several calls accumulate and run in order. A handle made with New from the same driver does not inherit it.

func WithMaxInList ¶ added in v0.4.0

func WithMaxInList(n int) Option

WithMaxInList caps the number of values in one typed IN or NOT IN list, and in one In argument of raw SQL, at n. A longer list fails the build with ErrLimitExceeded instead of reaching the database. Tuple IN lists that momo builds itself for relation loading are not capped here: they are chunked under the bind-parameter limit (WithMaxParams) instead. n must be positive: WithMaxInList panics otherwise, like WithMaxParams. The dialect value is copied; the shared dialect is never mutated.

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.

func WithNaming ¶ added in v0.8.0

func WithNaming(n FieldNamer) Option

WithNaming sets the naming preset that Query and QueryRows apply to the fields of a plain struct, the ones with no generated metadata. A field with no `momo:"column:x"` tag is then matched to the column the preset gives its name, so under naming.Prisma{} the field NextAttemptAt reads the column nextAttemptAt. Use the same preset as the naming: key of momo.yaml. Without the option, or with a nil n, the column is the snake_case of the field name (naming.ToSnake). Models, projections and tuple rows carry the names the generator derived, so they are not affected.

The preset may be any value, comparable or not, a preset or your own type, and it is called once per field when a type is first scanned. It must be stable: the plan of a type is built once and kept for the life of the DB (a DB with a preset keeps its own plans, so a preset built per request, with a DB per request, costs a plan per type and DB and is freed with the DB). momo reads no preset from momo.yaml or from a generated registration: this option is the only way to set one.

func WithReplicas ¶ added in v0.4.0

func WithReplicas(replicas ...Driver) Option

WithReplicas registers read-replica drivers. Replicas are used only through DB.Replica; the primary handle never reads from them.

func WithTxSetup ¶ added in v0.4.0

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

WithTxSetup registers fn to run right after every transaction momo begins, before any other statement of that transaction. It covers DB.Begin and DB.Tx, and every transaction momo opens on its own: chunked inserts, graph creation, relation writes, vecsearch.With and the short transactions of WithImplicitTx. Typical use is row-level-security scoping:

db := momo.New(drv, momo.WithTxSetup(func(ctx context.Context, tx *momo.Tx) error {
	return momo.SetConfigLocal(ctx, tx, "app.tenant_id", tenantFrom(ctx))
}))

fn runs once per real transaction. It does not run again for a savepoint (Tx.Tx, or the savepoint a chunked insert opens on a Tx), because a savepoint stays inside the transaction whose setting is already applied. It does not run for AdoptTx either: momo did not begin that transaction, so the caller owns its setup.

When fn returns an error the transaction is rolled back and Begin (or the call that needed the transaction) returns that error; nothing else runs. Without WithImplicitTx, auto-commit statements on a *DB do not run in a transaction, so fn never runs for them and they are unscoped: enable WithImplicitTx whenever the setup enforces a security boundary. Statements issued through db.Driver() bypass momo entirely and are never scoped either. fn must not commit or roll back tx. Use transaction-local settings only (SetConfigLocal, Tx.SetLocal): a session-level SET would outlive the transaction and, behind PgBouncer in transaction mode, leak to another client. With several options the functions run in registration order.

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) AppendOrder ¶ added in v0.4.0

func (o Order) AppendOrder(b *Builder)

AppendOrder renders the item as "expr ASC|DESC [NULLS FIRST|LAST]", failing the build with ErrUnsupported where the dialect has no NULLS ordering. Code outside this package that renders an ORDER BY inside its own expression (an ordered aggregate) calls it. AppendOrder is an extension point; see Ops.

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, a PredicateOrder 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. Like Like, it always carries ESCAPE '!' to match EscapeLike.

func JoinSQL ¶ added in v0.4.0

func JoinSQL(sep string, parts ...Expr) Predicate

JoinSQL composes raw SQL fragments and their arguments into one expression, the equivalent of Prisma.join: each part is typically SQL("status = ?", s), and the result can be used wherever SQL can. Because every part keeps its own arguments and placeholders are numbered as the whole statement renders, fragments can be collected in a slice and combined without hand-counting binds:

var conds []momo.Expr
if status != "" { conds = append(conds, momo.SQL("status = ?", status)) }
if owner != 0 { conds = append(conds, momo.SQL("owner_id = ?", owner)) }
where := momo.JoinSQL(" AND ", conds...)

sep must be a fixed literal and is checked against an allowlist, ignoring surrounding spaces and letter case: a comma, AND, OR, ||, + or a plain space (sep empty or only spaces). Any other separator fails the build with ErrValidation, so the separator can never carry caller-controlled SQL. For AND, OR, || and + every part and the whole join are parenthesised, which keeps operator precedence intact; a comma or a space writes the parts as they are. On MySQL, where || is logical OR, a "||" join renders CONCAT of the parenthesised parts. A NULL part makes a "||" join NULL on every dialect (wrap nullable parts in COALESCE), and on PostgreSQL every part of a "||" join must already be text: cast other types, for example SQL("?::text", n) or SQL("CAST(n AS text)"). A join of no parts renders 1 = 1 for AND and 1 = 0 for OR, like And and Or; with any other separator, or with a nil part, the build fails with ErrValidation. Use JoinTypedSQL when the join must carry a Go type.

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 and the statement always carries ESCAPE '!' on every dialect, the escape character EscapeLike uses. % and _ in the pattern stay wildcards; a ! in the pattern makes the next character literal. Escape user input with EscapeLike (or use ContainsLiteral) so it cannot act as a wildcard. A literal ! in a hand-written pattern must be written as !!.

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 ?| ?& @? @@ @>.

On PostgreSQL, native $n placeholders bind positional argument n instead of ?, and a reused $n shares one bind. A fragment uses one style: mixing $n with ? or @name is ErrValidation, a $n with no argument or an argument no $n uses is ErrValidation, and a $n on another dialect is ErrUnsupported. The number is the position within this fragment's own arguments, not the final statement's, so a fragment embedded after other binds is renumbered.

func TenantFilter ¶ added in v0.4.0

func TenantFilter[T any](ctx context.Context, col Column[T]) Predicate

TenantFilter returns col = <tenant from ctx>. When ctx has no tenant, or the tenant's dynamic type is not exactly T, building the query fails with ErrValidation ("no tenant in context"), so a missing tenant never widens a query, also not under Not or Or. The zero value of T (0, "") is a real tenant value and is filtered on like any other.

TenantFilter is an application-side convenience, NOT a security boundary. It is applied only where you add it to a Where: not automatically on Update or Delete, not to the tenant column of an Insert, not to Preload, Load or LoadCounts queries, joins or subqueries, and not to raw SQL. Enforce isolation in the database with row-level security. That is configured by your own WithTxSetup function, which reads whatever your code put in the context; to drive both from WithTenant, read the tenant there and call SetConfigLocal. Without WithImplicitTx, non-transactional queries get no setting.

type PredicateFunc ¶ added in v0.4.0

type PredicateFunc func(b *Builder)

PredicateFunc adapts a render function to a Predicate. The function writes the condition into the builder, using Builder.Ident for identifiers and Builder.Arg or Builder.ArgFor for values. PredicateFunc is an extension point; see Ops.

Example ¶

PredicateFunc turns a render function into a Predicate without defining a type.

db := momo.New(pgxdriver.New(nil))
active := momo.PredicateFunc(func(b *momo.Builder) {
	b.Ident("active")
	b.WriteString(" IS TRUE")
})

c, err := momo.Select[unitTally](db).
	From(momo.TableRef{Name: "documents"}).
	Columns(momo.NewColumn[string]("documents", "title", 0)).
	Where(active).
	SQL()
if err != nil {
	fmt.Println(err)
	return
}
fmt.Println(c.SQL)
Output:
SELECT "documents"."title" FROM "documents" WHERE "active" IS TRUE

func (PredicateFunc) AppendSQL ¶ added in v0.4.0

func (f PredicateFunc) AppendSQL(b *Builder)

AppendSQL calls f.

type PredicateOrder ¶ added in v0.8.0

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

PredicateOrder orders by a boolean predicate; see OrderByPredicate.

func OrderByPredicate ¶ added in v0.8.0

func OrderByPredicate(p Predicate) PredicateOrder

OrderByPredicate orders by the predicate p, rendered parenthesised as ORDER BY (p). A false row sorts before a true row, so (col IS NULL) ASC puts NULLs last on every dialect and (col IS NULL) DESC puts them first. The arguments p binds keep their place among the other arguments. Limits:

  • NullsFirst and NullsLast on the result of Asc or Desc are ErrUnsupported on MySQL and MariaDB, which momo does not emulate.
  • After (keyset pagination) over a predicate key is ErrValidation, and so is a predicate in the outer OrderBy of a SelectUnion, which allows unqualified result columns only.
  • A nil p is ErrValidation at build time.
  • agg.Call.OrderBy takes the Order from Asc or Desc, not the bare value.

func (PredicateOrder) Asc ¶ added in v0.8.0

func (o PredicateOrder) Asc() Order

Asc orders by the predicate ascending: false before true.

func (PredicateOrder) Desc ¶ added in v0.8.0

func (o PredicateOrder) Desc() Order

Desc orders by the predicate descending: true before false.

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", "raw", "explain", "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 QueryRegistry ¶ added in v0.5.0

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

QueryRegistry holds named query builders so a test can list the SQL an application issues without a database. It replaces the .sql files of a code generator: lint the listed SQL, freeze it in golden files (see momotest.QueryGolden), or run the previous release's list against a new schema. It is safe for concurrent use.

func DefaultQueries ¶ added in v0.5.0

func DefaultQueries() *QueryRegistry

DefaultQueries returns the process-wide registry that RegisterQuery fills.

func NewQueryRegistry ¶ added in v0.5.0

func NewQueryRegistry() *QueryRegistry

NewQueryRegistry returns an empty registry, for a test that wants one isolated from the process-wide DefaultQueries.

func (*QueryRegistry) Compile ¶ added in v0.5.0

func (r *QueryRegistry) Compile(d dialect.Name) ([]RegisteredQuery, error)

Compile renders every registered query for dialect d, sorted by name, without a database. The SQL has surrounding whitespace trimmed, so the text is stable whatever the indentation of a raw string literal. A builder that fails, returns nil or renders an error (for example ErrUnsupported on a dialect that lacks the feature) fails the whole call with an error naming the query. An unknown dialect panics, see NewOffline.

func (*QueryRegistry) Names ¶ added in v0.5.0

func (r *QueryRegistry) Names() []string

Names returns the registered names in sorted order.

func (*QueryRegistry) Register ¶ added in v0.5.0

func (r *QueryRegistry) Register(name string, build func(q Querier) Compilable)

Register adds a named query. build receives a Querier that only compiles (see NewOffline) and returns the statement, for example a SelectQuery or a RawQuery. It runs once per Compile and dialect, so it must not touch a database, and the argument values it binds are placeholders that never reach the listed SQL. Register panics when name is empty or contains a line break, build is nil or name is already registered, programming errors like a duplicate http.HandleFunc pattern.

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) AppendSQL ¶

func (c ReadOnlyColumn) 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) AsExpr ¶

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

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

func (ReadOnlyColumn) Asc ¶

func (c ReadOnlyColumn) Asc() Order

Asc orders by the column ascending. The order item is the column itself, a ColumnRef, so keyset pagination recognizes a primary-key column.

func (ReadOnlyColumn) ColumnName ¶

func (c ReadOnlyColumn) ColumnName() string

ColumnName returns the unquoted column name.

func (ReadOnlyColumn) Desc ¶

func (c ReadOnlyColumn) Desc() Order

Desc orders by the column descending.

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) Flags ¶

func (c ReadOnlyColumn) Flags() uint16

Flags returns the generated column flags.

func (ReadOnlyColumn) ParseCmp ¶ added in v0.4.0

func (c ReadOnlyColumn) ParseCmp(op, raw string) (Predicate, error)

ParseCmp parses raw with the column's Go type and returns the comparison predicate for op, one of eq, ne, lt, le, gt and ge. An unknown op or a value that does not parse is an *Error with Kind ErrValidation. It is meant for a filter value that arrives as text from outside; the column comes from the generated ColumnByName allowlist.

func (ReadOnlyColumn) ParseEq ¶ added in v0.4.0

func (c ReadOnlyColumn) ParseEq(raw string) (Predicate, error)

ParseEq parses raw with the column's Go type and returns the equality predicate on it. A value that does not parse is an *Error with Kind ErrValidation; the error does not echo raw, which may be attacker controlled.

func (ReadOnlyColumn) Table ¶

func (c ReadOnlyColumn) Table() string

Table returns the qualifier (table name or alias).

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. Zero marks a dimensionless column: a distance argument of any size is accepted and checked against itself.

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) ParseCmp ¶ added in v0.4.0

func (c ReadOnlyVectorColumn) ParseCmp(string, string) (Predicate, error)

ParseCmp always fails: a vector has no text form to filter by.

func (ReadOnlyVectorColumn) ParseEq ¶ added in v0.4.0

func (c ReadOnlyVectorColumn) ParseEq(string) (Predicate, error)

ParseEq always fails: a vector has no text form to filter by.

func (ReadOnlyVectorColumn) Table ¶

func (c ReadOnlyVectorColumn) Table() string

Table returns the qualifier (table name or alias).

type RegisteredQuery ¶ added in v0.5.0

type RegisteredQuery struct {
	// Name is the name the query was registered under.
	Name string
	// SQL is the statement text with the dialect's placeholders. Argument
	// values are not part of it.
	SQL string
}

RegisteredQuery is the compiled SQL of one named query for one dialect.

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). Has, HasNo and CountOrder nest: a nested subquery has its own alias (momo_r2, momo_r3, ...), "momo_r" in its scope names its own child, and a relation whose parent is the enclosing child correlates on the enclosing alias. 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]) RelationLoader[P, C]

Scope returns a loader for r with per-load options. The result can be scoped further and given nested loads: r.Scope(opts...).Then(nested...) filters and orders the relation and loads nested relations over exactly the children that remain.

func (Relation[P, C]) Then ¶

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

Then returns a loader for r that also loads nested relations on the children. The result can be scoped: r.Then(nested...).Scope(opts...) is the same loader as r.Scope(opts...).Then(nested...).

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

String returns the lower-case name of the relation kind.

type RelationLoader ¶ added in v0.3.0

type RelationLoader[P, C any] struct {
	// contains filtered or unexported fields
}

RelationLoader is a loader for one relation that carries per-load options (Scope) and nested loads (Then) together, so "approved comments, newest first, each with its author" is one loader:

document.Comments.Scope(
	momo.LoadWhere[models.Comment](comment.Approved.Eq(true)),
	momo.LoadOrderBy[models.Comment](comment.ID.Desc()),
).Then(comment.Author)

It is a value: Scope and Then return a new loader and leave the receiver unchanged, so a base loader can be shared. Scope may be called several times (predicates AND together, order items append, a later LoadPerParentLimit replaces an earlier one). Then appends nested loaders. The options apply to this relation's children only; nested loaders carry their own options. Nested loads run once over exactly the children that survive the options, so the statement count stays one per bind-limit chunk at the top and one per level below it.

A RelationLoader with no options and no nested loaders behaves like the plain Relation. It implements Loader[P]; Loader stays sealed.

func (RelationLoader[P, C]) Scope ¶ added in v0.3.0

func (l RelationLoader[P, C]) Scope(opts ...LoadOption[C]) RelationLoader[P, C]

Scope returns a copy of l with opts applied after its existing options.

func (RelationLoader[P, C]) Then ¶ added in v0.3.0

func (l RelationLoader[P, C]) Then(nested ...Loader[C]) RelationLoader[P, C]

Then returns a copy of l with nested appended after its existing nested loads.

type Result ¶

type Result struct {
	RowsAffected int64
	// InsertID is the auto-increment key a single-row insert generated, on
	// dialects whose driver reports it (MySQL and MariaDB); zero otherwise.
	InsertID 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 Row1 ¶ added in v0.3.0

type Row1[A any] struct {
	Projection
	C1 A
}

Row1 is a result row of 1 column, scanned positionally into C1. Row1 to Row10 are the tuple rows: generic result types for queries that do not read a whole model: aggregates, joins and column subsets. They replace a hand-written projection struct and Meta per result shape:

rows, err := momo.Select[momo.Row2[string, int64]](db).
	From(document.Table).
	Columns(document.Title, momo.CountAll()).
	GroupBy(document.Title).
	All(ctx)
// rows[i].C1 is the title, rows[i].C2 the count

Row1 to Row10 take 1 to 10 columns. The Columns list is scanned positionally: expression i fills field Ci, so the number of Columns must equal the row's arity (ErrValidation otherwise, at SQL or execution time). Names play no part. Use a pointer type parameter, such as Row2[string, *int64], for a column that can be NULL (a LEFT JOIN side, an aggregate over no rows).

A tuple row is a projection: it has no table, so Insert, Update and Delete refuse it with ErrValidation, and it carries no soft-delete column of its own. As for any projection the live-row filter comes from the From table (and WithDeleted or OnlyDeleted adjust it). After works when OrderBy names a unique key, because a tuple has no primary key to break ties with.

func (*Row1[A]) MomoMeta ¶ added in v0.3.0

func (*Row1[A]) MomoMeta() *Meta[Row1[A]]

MomoMeta returns the shared metadata of this instantiation.

type Row2 ¶ added in v0.3.0

type Row2[A, B any] struct {
	Projection
	C1 A
	C2 B
}

Row2 is a result row of 2 columns, scanned positionally into C1..C2. Use a pointer type parameter for a column that can be NULL; see Row1.

func (*Row2[A, B]) MomoMeta ¶ added in v0.3.0

func (*Row2[A, B]) MomoMeta() *Meta[Row2[A, B]]

MomoMeta returns the shared metadata of this instantiation.

type Row3 ¶ added in v0.3.0

type Row3[A, B, C any] struct {
	Projection
	C1 A
	C2 B
	C3 C
}

Row3 is a result row of 3 columns, scanned positionally into C1..C3. Use a pointer type parameter for a column that can be NULL; see Row1.

func (*Row3[A, B, C]) MomoMeta ¶ added in v0.3.0

func (*Row3[A, B, C]) MomoMeta() *Meta[Row3[A, B, C]]

MomoMeta returns the shared metadata of this instantiation.

type Row4 ¶ added in v0.3.0

type Row4[A, B, C, D any] struct {
	Projection
	C1 A
	C2 B
	C3 C
	C4 D
}

Row4 is a result row of 4 columns, scanned positionally into C1..C4. Use a pointer type parameter for a column that can be NULL; see Row1.

func (*Row4[A, B, C, D]) MomoMeta ¶ added in v0.3.0

func (*Row4[A, B, C, D]) MomoMeta() *Meta[Row4[A, B, C, D]]

MomoMeta returns the shared metadata of this instantiation.

type Row5 ¶ added in v0.3.0

type Row5[A, B, C, D, E any] struct {
	Projection
	C1 A
	C2 B
	C3 C
	C4 D
	C5 E
}

Row5 is a result row of 5 columns, scanned positionally into C1..C5. Use a pointer type parameter for a column that can be NULL; see Row1.

func (*Row5[A, B, C, D, E]) MomoMeta ¶ added in v0.3.0

func (*Row5[A, B, C, D, E]) MomoMeta() *Meta[Row5[A, B, C, D, E]]

MomoMeta returns the shared metadata of this instantiation.

type Row6 ¶ added in v0.3.0

type Row6[A, B, C, D, E, F any] struct {
	Projection
	C1 A
	C2 B
	C3 C
	C4 D
	C5 E
	C6 F
}

Row6 is a result row of 6 columns, scanned positionally into C1..C6. Use a pointer type parameter for a column that can be NULL; see Row1.

func (*Row6[A, B, C, D, E, F]) MomoMeta ¶ added in v0.3.0

func (*Row6[A, B, C, D, E, F]) MomoMeta() *Meta[Row6[A, B, C, D, E, F]]

MomoMeta returns the shared metadata of this instantiation.

type Row7 ¶ added in v0.3.0

type Row7[A, B, C, D, E, F, G any] struct {
	Projection
	C1 A
	C2 B
	C3 C
	C4 D
	C5 E
	C6 F
	C7 G
}

Row7 is a result row of 7 columns, scanned positionally into C1..C7. Use a pointer type parameter for a column that can be NULL; see Row1.

func (*Row7[A, B, C, D, E, F, G]) MomoMeta ¶ added in v0.3.0

func (*Row7[A, B, C, D, E, F, G]) MomoMeta() *Meta[Row7[A, B, C, D, E, F, G]]

MomoMeta returns the shared metadata of this instantiation.

type Row8 ¶ added in v0.3.0

type Row8[A, B, C, D, E, F, G, H any] struct {
	Projection
	C1 A
	C2 B
	C3 C
	C4 D
	C5 E
	C6 F
	C7 G
	C8 H
}

Row8 is a result row of 8 columns, scanned positionally into C1..C8. Use a pointer type parameter for a column that can be NULL; see Row1.

func (*Row8[A, B, C, D, E, F, G, H]) MomoMeta ¶ added in v0.3.0

func (*Row8[A, B, C, D, E, F, G, H]) MomoMeta() *Meta[Row8[A, B, C, D, E, F, G, H]]

MomoMeta returns the shared metadata of this instantiation.

type Row9 ¶ added in v0.3.0

type Row9[A, B, C, D, E, F, G, H, I any] struct {
	Projection
	C1 A
	C2 B
	C3 C
	C4 D
	C5 E
	C6 F
	C7 G
	C8 H
	C9 I
}

Row9 is a result row of 9 columns, scanned positionally into C1..C9. Use a pointer type parameter for a column that can be NULL; see Row1.

func (*Row9[A, B, C, D, E, F, G, H, I]) MomoMeta ¶ added in v0.3.0

func (*Row9[A, B, C, D, E, F, G, H, I]) MomoMeta() *Meta[Row9[A, B, C, D, E, F, G, H, I]]

MomoMeta returns the shared metadata of this instantiation.

type Row10 ¶ added in v0.3.0

type Row10[A, B, C, D, E, F, G, H, I, J any] struct {
	Projection
	C1  A
	C2  B
	C3  C
	C4  D
	C5  E
	C6  F
	C7  G
	C8  H
	C9  I
	C10 J
}

Row10 is a result row of 10 columns, scanned positionally into C1..C10. Use a pointer type parameter for a column that can be NULL; see Row1.

func (*Row10[A, B, C, D, E, F, G, H, I, J]) MomoMeta ¶ added in v0.3.0

func (*Row10[A, B, C, D, E, F, G, H, I, J]) MomoMeta() *Meta[Row10[A, B, C, D, E, F, G, H, I, J]]

MomoMeta returns the shared metadata of this instantiation.

type RowExpr ¶ added in v0.8.0

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

RowExpr is a row value, (a, b, ...), compared as a whole with another row value of the same length. Build it with Row. Comparison is lexicographic: (a, b) < (x, y) is a < x OR (a = x AND b < y), which is the keyset position check. A NULL element makes the comparison NULL, as in SQL.

func Row ¶ added in v0.8.0

func Row(elems ...Expr) RowExpr

Row is the row value (e1, e2, ...), for example a FIFO position check:

momo.Row(w.CreatedAt, w.ID).Lt(momo.Row(me.At, momo.Value(id)))

Both sides need at least two elements and the same number of them, or the build fails with ErrValidation. A dialect without row values fails with ErrUnsupported.

func (RowExpr) AppendSQL ¶ added in v0.8.0

func (r RowExpr) AppendSQL(b *Builder)

AppendSQL writes "(e1, e2, ...)" to b.

func (RowExpr) Ge ¶ added in v0.8.0

func (r RowExpr) Ge(other RowExpr) Predicate

Ge is true when the row is lexicographically greater than or equal to other.

func (RowExpr) Gt ¶ added in v0.8.0

func (r RowExpr) Gt(other RowExpr) Predicate

Gt is true when the row is lexicographically greater than other.

func (RowExpr) Le ¶ added in v0.8.0

func (r RowExpr) Le(other RowExpr) Predicate

Le is true when the row is lexicographically less than or equal to other.

func (RowExpr) Lt ¶ added in v0.8.0

func (r RowExpr) Lt(other RowExpr) Predicate

Lt is true when the row is lexicographically less than other.

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 Scope ¶ added in v0.4.0

type Scope[T any] func(SelectQuery[T]) SelectQuery[T]

Scope is a named, reusable query transformation, for example a tenant or visibility filter shared by many call sites. A Scope receives and returns a SelectQuery value, so it composes with every other builder method.

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). A predicate key from OrderByPredicate is an ErrValidation: its value can be NULL and a cursor over it skips or repeats rows.

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]) DistinctOn ¶ added in v0.4.0

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

DistinctOn keeps the first row of each group of rows that agree on es: SELECT DISTINCT ON (es). Which row is first is decided by OrderBy, so DistinctOn requires an OrderBy whose leading items are exactly es (in any order) followed by the tie-breakers that pick the row; otherwise the build fails with ErrValidation, the same rule PostgreSQL enforces. DistinctOn replaces Distinct. PostgreSQL only; other dialects fail the build with ErrUnsupported.

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/vecop"
	"github.com/azrtydxb/momo/vector"
)

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

	plan, err := momo.Select[example.Document](db).
		OrderBy(vecop.L2(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]) ForKeyShare ¶ added in v0.5.0

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

ForKeyShare adds FOR KEY SHARE (PostgreSQL only): the weakest row lock, which only blocks changes to the key columns and deletes. Options and restrictions are those of ForUpdate, and LockOf works with it. MySQL, MariaDB and SQLite return ErrUnsupported when the query is built.

func (SelectQuery[T]) ForNoKeyUpdate ¶ added in v0.5.0

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

ForNoKeyUpdate adds FOR NO KEY UPDATE (PostgreSQL only): a write lock that does not block foreign-key inserts and FOR KEY SHARE locks on the same row, so it suits a row update that leaves the key columns alone. Options and restrictions are those of ForUpdate, and LockOf works with it. MySQL, MariaDB and SQLite have no such mode and return ErrUnsupported when the query is built.

func (SelectQuery[T]) ForShare ¶

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

ForShare adds FOR SHARE (PostgreSQL). Options and restrictions are those of ForUpdate.

func (SelectQuery[T]) ForUpdate ¶

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

ForUpdate adds FOR UPDATE (PostgreSQL). Options: "NOWAIT", "SKIP LOCKED"; naming both is an ErrValidation, repeating one is not. A row lock cannot be combined with DISTINCT, GROUP BY or HAVING: the build fails with ErrValidation. Count and Exists wrap the whole statement, so they keep the lock clause and lock the rows they count.

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]) LockOf ¶ added in v0.4.0

func (sq SelectQuery[T]) LockOf(tables ...string) SelectQuery[T]

LockOf narrows the row lock of ForUpdate, ForShare, ForNoKeyUpdate or ForKeyShare to the named tables (FOR UPDATE OF t1, t2). A joined select needs it to lock only the claimed side: a LEFT JOIN cannot lock its nullable side, and a job-claim join locks the job table and leaves the parent alone. Each name is the qualifier the table has in the query, its alias when it has one, and is quoted as an identifier. The names render after the lock mode and before NOWAIT or SKIP LOCKED, whatever the call order. Without one of the four lock modes the build fails with ErrValidation; like them, LockOf returns ErrUnsupported on SQLite, and MySQL and MariaDB refuse the PostgreSQL-only modes. Each name must be the table or alias of the FROM or a joined table, else the build fails with ErrValidation naming it. A lock cannot be combined with DISTINCT, DISTINCT ON, GROUP BY or HAVING (ErrValidation); other invalid combinations, such as aggregates or UNION, are left to the server.

func (SelectQuery[T]) MaxRows ¶ added in v0.4.0

func (sq SelectQuery[T]) MaxRows(n int) SelectQuery[T]

MaxRows caps the rows a collecting read (All, Pluck) may return. When the query yields more than n rows the read fails with ErrLimitExceeded; rows are never silently truncated. Streaming reads (Each, Iter) and Count are not capped, and n must be positive.

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. A boolean condition orders through OrderByPredicate, a PredicateOrder.

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.

Interceptors run with a background context here; the execution methods pass the statement's own context.

func (SelectQuery[T]) Scope ¶ added in v0.4.0

func (sq SelectQuery[T]) Scope(s ...Scope[T]) SelectQuery[T]

Scope applies each scope to the query in order and returns the result. A nil scope is skipped.

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 Series ¶ added in v0.4.0

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

Series is a generate_series(...) source producing one column of type T. Build it with GenerateSeries or GenerateTimeSeries.

func GenerateSeries ¶ added in v0.4.0

func GenerateSeries[T SeriesInt](alias, col string, start, stop T, step ...T) Series[T]

GenerateSeries is generate_series(start, stop[, step]) AS alias (col) over integers: the values from start to stop inclusive. A zero step fails the build with ErrValidation; a negative step counts down. More than one step is also a validation error. PostgreSQL only.

func GenerateTimeSeries ¶ added in v0.4.0

func GenerateTimeSeries(alias, col string, start, stop time.Time, step time.Duration) Series[time.Time]

GenerateTimeSeries is generate_series over timestamptz: start, start+step, ... up to stop. step is truncated to whole microseconds; a step that truncates to zero fails the build with ErrValidation. PostgreSQL only.

func (Series[T]) Column ¶ added in v0.4.0

func (s Series[T]) Column() Column[T]

Column is the typed column the series produces.

func (Series[T]) Qualifier ¶ added in v0.4.0

func (s Series[T]) Qualifier() string

Qualifier is the alias the source's column is qualified with.

type SeriesInt ¶ added in v0.4.0

type SeriesInt interface{ ~int | ~int32 | ~int64 }

SeriesInt is the integer types GenerateSeries accepts.

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. On PostgreSQL a native $n placeholder binds the nth argument of this fragment (see SQL), so pgx and sqlc statements port unchanged; momo renumbers it. 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.

A member that has its own ORDER BY, LIMIT, OFFSET or row lock is rendered in parentheses so the clause binds to the member, not to the whole union, as in the top N of each kind:

(SELECT ... ORDER BY name LIMIT $1) UNION ALL (SELECT ... ORDER BY name LIMIT $1)

SQLite rejects parentheses around a compound member, so there such a member renders as SELECT * FROM (member). A member without those clauses renders bare. A member with its own WITH clause is grouped the same way.

PostgreSQL rejects a row lock inside a set operation, so a member with ForUpdate or another lock returns ErrUnsupported there; MySQL accepts it in a parenthesised member, and SQLite has no row locks. To run a union directly, with an outer ORDER BY and LIMIT, use SelectUnion.

type TableRef ¶

type TableRef struct {
	Name   string
	Alias  string
	Schema string
	// SoftDelete is the name of the table's soft-delete marker column; empty
	// when the table has none.
	SoftDelete string
	// SoftDeleteKind is the type of that marker column.
	SoftDeleteKind SoftDeleteKind
}

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

Generated code sets SoftDelete and SoftDeleteKind on a model's Table when the model has a soft-delete column. A projection read FROM such a table then excludes soft-deleted rows like a model does. Use As to alias it.

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) As ¶ added in v0.3.0

func (t TableRef) As(alias string) TableRef

As returns a copy of t under the alias, keeping everything else, including the soft-delete column that a projection read FROM t is filtered by.

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.

func Lateral ¶ added in v0.4.0

func Lateral(q Subquery, alias string) TableSource

Lateral uses a subquery as a LATERAL FROM/JOIN source under alias. The subquery may reference columns of the FROM and JOIN sources before it:

momo.Select[row](db).
	From(accounts).
	CrossJoin(momo.Lateral(latestOrder, "lo"))

renders CROSS JOIN LATERAL (...) AS "lo". A LEFT JOIN needs an ON condition, so pass momo.SQL("true") when there is nothing to join on. Reference the subquery's columns with ColumnOf. PostgreSQL and MySQL 8 support LATERAL; SQLite and MariaDB fail the build with ErrUnsupported.

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. On a Tx from AdoptTx it returns an error matching ErrAdoptedTx and does nothing.

func (*Tx) Dialect ¶

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

Dialect returns the dialect of the transaction's connection.

func (*Tx) DriverTx ¶ added in v0.3.0

func (tx *Tx) DriverTx() DriverTx

DriverTx returns the driver-level transaction behind tx, for driver packages that unwrap it (pgxdriver.UnwrapSQLTx, pgxdriver.UnwrapPgxTx). A savepoint scope returns the same outer transaction. Calling Commit or Rollback on it directly bypasses Momo's bookkeeping; prefer the unwrap helpers of the driver package.

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. On a Tx from AdoptTx it returns an error matching ErrAdoptedTx and does nothing.

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) 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 UnionQuery ¶ added in v0.8.0

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

UnionQuery runs a UNION or UNION ALL directly, with an outer ORDER BY, LIMIT and OFFSET, and scans its rows into T. Build one with SelectUnion.

UnionQuery is an immutable value: every method returns a new query.

func SelectUnion ¶ added in v0.8.0

func SelectUnion[T any](q Querier, all bool, members ...Subquery) UnionQuery[T]

SelectUnion starts a top-level UNION (all=false) or UNION ALL (all=true) of members, whose rows scan into T. T is a model, a projection, a tuple row Row1 to Row10 or a plain struct, matched to the result columns as for Query. Each member is rendered as Union renders it: in parentheses when it has its own ORDER BY, LIMIT, OFFSET or lock, as SELECT * FROM (member) on SQLite.

rows, err := momo.SelectUnion[hit](db, true,
	momo.Select[hit](db).From(teachers).Columns(...).OrderBy(name.Asc()).Limit(5),
	momo.Select[hit](db).From(students).Columns(...).OrderBy(name.Asc()).Limit(5),
).OrderBy(momo.Asc(momo.ColumnOf[string]("", "full_name"))).Limit(8).All(ctx)

An outer ORDER BY on a union may name only result columns, not expressions over them: build them with ColumnOf and an empty source. A qualified column or an expression is an ErrValidation at build time.

Hooks and interceptors see op "select" with an empty table name.

func (UnionQuery[T]) All ¶ added in v0.8.0

func (u UnionQuery[T]) All(ctx context.Context) ([]T, error)

All runs the union and returns every row. An empty result is a nil slice and a nil error.

func (UnionQuery[T]) Limit ¶ added in v0.8.0

func (u UnionQuery[T]) Limit(n int64) UnionQuery[T]

Limit caps the rows of the whole union.

func (UnionQuery[T]) Offset ¶ added in v0.8.0

func (u UnionQuery[T]) Offset(n int64) UnionQuery[T]

Offset skips rows of the whole union.

func (UnionQuery[T]) OrderBy ¶ added in v0.8.0

func (u UnionQuery[T]) OrderBy(items ...Orderable) UnionQuery[T]

OrderBy adds outer ORDER BY items, which order the whole union.

func (UnionQuery[T]) Rows ¶ added in v0.8.0

func (u UnionQuery[T]) Rows(ctx context.Context) iter.Seq2[T, error]

Rows runs the union and yields one T per row without buffering the result. An error is yielded once, as the final element.

func (UnionQuery[T]) SQL ¶ added in v0.8.0

func (u UnionQuery[T]) SQL() (Compiled, error)

SQL compiles the union without executing it.

type UnnestArray ¶ added in v0.4.0

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

UnnestArray is one array argument of Unnest; UnnestCol implements it.

type UnnestCol ¶ added in v0.4.0

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

UnnestCol is one bound array of an Unnest source and the name of the column it produces. Build it with UnnestValues or UnnestValuesAs.

func UnnestValues ¶ added in v0.4.0

func UnnestValues[T any](name string, vals []T) UnnestCol[T]

UnnestValues binds vals as one array argument that unnest turns into the column name. The array's SQL element type is derived from T: string is text, int64 and int bigint, int32 integer, int16 smallint, float64 double precision, float32 real, bool boolean, time.Time timestamptz, []byte bytea and a 16-byte array uuid (a pointer to any of these allows NULL elements; named types follow their underlying kind). The array is bound as one parameter and is not masked in logs or traces, even for sensitive values; do not unnest secrets. Any other T fails the build with ErrValidation; use UnnestValuesAs to name the type.

func UnnestValuesAs ¶ added in v0.4.0

func UnnestValuesAs[T any](name, sqlType string, vals []T) UnnestCol[T]

UnnestValuesAs is UnnestValues with an explicit SQL element type such as numeric or timestamp (words of letters, digits and underscores, with an optional length suffix). The bound array is cast to sqlType[].

func (UnnestCol[T]) Ref ¶ added in v0.4.0

func (c UnnestCol[T]) Ref(source string) Column[T]

Ref is the typed column through which the query reads this column from the Unnest source called source (its alias).

type UnnestColumnSource ¶ added in v0.8.0

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

UnnestColumnSource is LATERAL unnest(col) AS alias (name): the elements of an array-valued column or expression of an earlier source, one row each. Build it with UnnestColumn and name it with As.

func UnnestColumn ¶ added in v0.8.0

func UnnestColumn[T any](col Expression[[]T]) UnnestColumnSource[T]

UnnestColumn expands an array column (or any array expression) of a source earlier in the FROM list into rows:

momo.Select[row](db).From(q2).
	CrossJoin(momo.UnnestColumn(q2Flags).As("fl", "flag"))

renders CROSS JOIN LATERAL unnest("q2"."flags") AS "fl" ("flag"). Read the element through the Column of the source returned by As. Unlike Unnest it takes a column and not bound values, so it can reference columns of the sources before it. Without As the source has no alias and fails the build with ErrValidation. It must come after the source that holds the array: as the first FROM item it fails the build with ErrValidation. Use CrossJoin, or LeftJoin with momo.SQL("true") as the ON condition to keep the rows whose array is NULL or empty. A NULL array yields no rows. PostgreSQL only.

func (UnnestColumnSource[T]) As ¶ added in v0.8.0

func (u UnnestColumnSource[T]) As(alias, col string) UnnestColumnSource[T]

As names the source alias and the column the elements are read from.

func (UnnestColumnSource[T]) Column ¶ added in v0.8.0

func (u UnnestColumnSource[T]) Column() Column[T]

Column is the typed column of the elements.

func (UnnestColumnSource[T]) Qualifier ¶ added in v0.8.0

func (u UnnestColumnSource[T]) Qualifier() string

Qualifier is the alias the source's column is qualified with.

type UnnestSource ¶ added in v0.4.0

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

UnnestSource is unnest(a1, a2, ...) AS alias (c1, c2, ...) used as a FROM or JOIN source. Build it with Unnest.

func Unnest ¶ added in v0.4.0

func Unnest(alias string, cols ...UnnestArray) UnnestSource

Unnest expands one or more bound arrays into rows, one column per array:

momo.Select[fx](db).From(momo.Unnest("fx", currency, rate))

renders FROM unnest($1::text[], $2::double precision[]) AS "fx" ("currency", "rate"). Arrays of different lengths are padded with NULL to the longest. Read the columns through UnnestCol.Ref(alias). Unnest also serves as a CTE body source and as the right side of a Join. PostgreSQL only.

func (UnnestSource) Qualifier ¶ added in v0.4.0

func (u UnnestSource) Qualifier() string

Qualifier is the alias the source's columns are qualified with.

func (UnnestSource) WithOrdinality ¶ added in v0.4.0

func (u UnnestSource) WithOrdinality(name string) UnnestSource

WithOrdinality adds WITH ORDINALITY: an extra 1-based bigint column called name, read as an int64 column through ColumnOf.

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 BulkUpdate ¶ added in v0.4.0

func BulkUpdate[T any, PT Modeler[T]](q Querier, key ColumnRef, cols []ColumnRef, rows [][]any) UpdateQuery[T]

BulkUpdate updates rows of T from a list of rows keyed by key. Only listed keys change. Each row holds the key value first, then one value per column of cols, in order:

BulkUpdate[Document](db, DocID, []ColumnRef{DocTitle}, [][]any{{int64(2), "new"}})

It renders UPDATE t SET c = v.c FROM (one SELECT per row, joined by UNION ALL) AS v WHERE t.key = v.key with every value bound, so a row whose key is not in the table changes nothing. The rows take their column types from the table. A key listed twice applies one of its rows. The statement also maintains the updated-at and version columns, runs the OnUpdate defaults and the field validators over each row, and honours Where for extra conditions; Set and Model cannot be combined with it. PostgreSQL only (dialect.Merge gates both): other dialects fail with ErrUnsupported. A statement over the bind-parameter limit fails with ErrLimitExceeded: BulkUpdate does not chunk, so split a larger list across calls (in a transaction if it must be atomic).

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]) AppendSQL ¶ added in v0.5.0

func (u UpdateQuery[T]) AppendSQL(b *Builder)

AppendSQL renders the statement in parentheses as a WITH clause body. It runs the handle's interceptors with a background context, as SQL does, but not the beforeWrite checks of Exec. Only PostgreSQL accepts the result.

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]) From ¶ added in v0.5.0

func (u UpdateQuery[T]) From(srcs ...TableSource) UpdateQuery[T]

From adds sources to the statement, rendering UPDATE t SET ... FROM src WHERE ...: the SET expressions and the WHERE may reference src's columns (ColumnOf on a CTE or FromSubquery alias, or a model's columns). src is a table, aliased table, CTE or FromSubquery, as in a join. Join src to the target in Where. Calling From again replaces the sources.

From takes more than one source (UPDATE t SET ... FROM a, b WHERE ...), for instance a CTE and a table: they render as a comma list, in order. With more than one source the unfiltered-mutation guard also requires Where to reference every source (see below).

Returning may list columns of a source (ColumnOf on a CTE or FromSubquery alias, or a table's descriptors under its alias). On PostgreSQL the RETURNING list of a statement with a source is rendered qualified, so a column that the source and the target share is not ambiguous; scan such a list into a plain struct, projection or tuple row with ScanUpdate (Rows and One refuse it with ErrValidation). SQLite cannot return a source's columns (ErrUnsupported).

PostgreSQL and SQLite (3.33 or later) render UPDATE ... FROM. MySQL and MariaDB render the multi-table form UPDATE t, src SET t.col = ... WHERE ..., qualifying the assigned columns. Soft-deleted rows of src are not filtered: add that predicate to Where. Interceptors apply to the target table only, not to src.

With a From source the unfiltered-mutation guard is stricter: at least one Where predicate must reference a column of the target table, otherwise the statement joins every target row to src and fails with ErrUnfilteredMutation unless AllRows confirms it. A raw SQL fragment does not count as such a reference. An Or counts only when every branch references the target, because a branch off the target can be true on its own; an And counts when any term does. Not swaps the two rules (De Morgan): a negated And needs every term on the target, a negated Or any term. JoinSQL with AND or OR follows the same rules as And and Or; with another separator it does not count. The guard checks that Where references the target, not that it restricts it: a constant-true predicate such as t.id = t.id passes. With two or more sources the same rule applies to each of them: a source that no Where predicate references is cross-joined to the target, and the statement fails with ErrUnfilteredMutation unless AllRows confirms it. A single source is not checked this way, so a one-row source that only feeds SET stays valid. From cannot be combined with BulkUpdate (ErrValidation).

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. A nil pointer in a dbdefault field means "not set": the column is left out of SET and keeps its stored value.

func (UpdateQuery[T]) One ¶ added in v0.7.0

func (u UpdateQuery[T]) One(ctx context.Context) (T, error)

One runs the UPDATE and returns the single RETURNING row: ErrNotFound when no row matched (a guarded update) and ErrTooManyRows when more than one did. Without a Returning list it returns every column. It is not supported with a version-checked Model update (ErrValidation; use Exec) or on a dialect without RETURNING (ErrUnsupported: MySQL, and MariaDB, where momo has no RETURNING form for UPDATE).

Without WithImplicitTx or an explicit transaction the statement has already changed every matched row when One returns ErrTooManyRows; run One inside db.Tx and roll back on ErrTooManyRows. AfterLoad runs on the returned row before the write commits, so an AfterLoad error rolls an implicit transaction back, unlike SelectQuery.One.

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.

Interceptors run with a background context here; the execution methods pass the statement's own context.

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.

func (UpdateQuery[T]) With ¶ added in v0.5.0

func (u UpdateQuery[T]) With(name string, q Subquery, cols ...string) UpdateQuery[T]

With prefixes the statement with a common table expression. cols optionally names its columns. The body may be a SelectQuery, an ExprSelect, a Union or (on PostgreSQL only) an INSERT, UPDATE or DELETE; a data-modifying body on another dialect fails with ErrUnsupported. The body needs Returning only when the outer statement reads the CTE: a body that nothing reads is valid without it, and PostgreSQL reports the error otherwise. Reference the CTE with CTE and ColumnOf, for instance as the From source. MariaDB does not accept the clause here (ErrUnsupported), and BulkUpdate cannot be combined with it (ErrValidation).

func (UpdateQuery[T]) WithRecursive ¶ added in v0.5.0

func (u UpdateQuery[T]) WithRecursive(name string, q Subquery, cols ...string) UpdateQuery[T]

WithRecursive is With for a recursive CTE.

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 VectorColumnOf ¶ added in v0.4.0

func VectorColumnOf[V VectorValue](s VectorSource[V]) VectorColumn[V]

VectorColumnOf returns the VectorColumn behind any vector column descriptor that satisfies VectorSource: VectorColumn itself, its nullable variant and the permission variants. Code outside this package uses it to read a source's table, column name, dimension and flags when it builds a distance expression. VectorColumnOf is an extension point; see Ops.

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. Zero marks a dimensionless column: a distance argument of any size is accepted and checked against itself.

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) ParseCmp ¶ added in v0.4.0

func (c VectorColumn) ParseCmp(string, string) (Predicate, error)

ParseCmp always fails: a vector has no text form to filter by.

func (VectorColumn) ParseEq ¶ added in v0.4.0

func (c VectorColumn) ParseEq(string) (Predicate, error)

ParseEq always fails: a vector has no text form to filter by.

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.

type View ¶ added in v0.5.0

type View struct{}

View is embedded next to Model in a struct that maps a database view or materialized view. The generator records it in Meta.View, and every write builder (Insert, Upsert, Update, SoftDelete, BulkUpdate, Delete, Merge) then fails with ErrValidation naming the model; reads work as for any model. Declare the view itself in the model's MomoConfig with c.View.

type ActiveUser struct {
    momo.Model `momo:"table:active_users"`
    momo.View
    ID int64 `momo:"pk"`
}

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

type WindowExpr ¶ added in v0.4.0

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

WindowExpr is call OVER (...). It is an Expression[T]; name it with As.

func WindowOf ¶ added in v0.4.0

func WindowOf[T any](call Expr, w WindowSpec) WindowExpr[T]

WindowOf turns the aggregate call into a window function: call OVER (...). Code outside this package that defines its own aggregate type (agg.Call) builds its Over method on it; call must write identifiers through Builder.Ident and values through Builder.Arg. WindowOf is an extension point; see Ops.

func (WindowExpr[T]) AppendSQL ¶ added in v0.4.0

func (e WindowExpr[T]) AppendSQL(b *Builder)

AppendSQL writes the window expression to b.

func (WindowExpr[T]) As ¶ added in v0.4.0

func (e WindowExpr[T]) As(alias string) Aliased[T]

As names the window expression in a select list.

type WindowFunc ¶ added in v0.4.0

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

WindowFunc is a window function that needs its OVER clause: RowNumber, Rank, DenseRank, Lag, Lead, FirstValue and LastValue. Aggregates take theirs through Agg.Over.

func DenseRank ¶ added in v0.4.0

func DenseRank() WindowFunc[int64]

DenseRank is dense_rank(): the rank without gaps. It needs an ORDER BY.

func FirstValue ¶ added in v0.4.0

func FirstValue[T any](e Expression[T]) WindowFunc[T]

FirstValue is first_value(e) over the window frame. It requires an ORDER BY in the window on purpose: without one the first row is arbitrary.

func Lag ¶ added in v0.4.0

func Lag[T any](e Expression[T], offset int) WindowFunc[T]

Lag is lag(e, offset): the value offset rows before the current row, NULL when there is none (scan into a nullable destination). It needs an ORDER BY and rejects a frame.

func LagDefault ¶ added in v0.4.0

func LagDefault[T any](e Expression[T], offset int, def T) WindowFunc[T]

LagDefault is lag(e, offset, def): def replaces the NULL of a missing row. def is masked in logs and traces when e is a sensitive column.

func LastValue ¶ added in v0.4.0

func LastValue[T any](e Expression[T]) WindowFunc[T]

LastValue is last_value(e) over the window frame. The default frame ends at the current row; add Rows(UnboundedPreceding(), UnboundedFollowing()) for the last row of the partition.

func Lead ¶ added in v0.4.0

func Lead[T any](e Expression[T], offset int) WindowFunc[T]

Lead is lead(e, offset): the value offset rows after the current row.

func LeadDefault ¶ added in v0.4.0

func LeadDefault[T any](e Expression[T], offset int, def T) WindowFunc[T]

LeadDefault is lead(e, offset, def).

func Rank ¶ added in v0.4.0

func Rank() WindowFunc[int64]

Rank is rank(): the rank with gaps after ties. It needs an ORDER BY.

func RowNumber ¶ added in v0.4.0

func RowNumber() WindowFunc[int64]

RowNumber is row_number(): the row's 1-based position in its partition.

func (WindowFunc[T]) Over ¶ added in v0.4.0

func (f WindowFunc[T]) Over(w WindowSpec) WindowExpr[T]

Over attaches the window specification.

type WindowSpec ¶ added in v0.4.0

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

WindowSpec is the OVER (...) clause: PARTITION BY, ORDER BY and a frame. It is an immutable value; every method returns a new one.

func Over ¶ added in v0.4.0

func Over() WindowSpec

Over starts an empty window specification: OVER ().

func (WindowSpec) Groups ¶ added in v0.4.0

func (w WindowSpec) Groups(start, end FrameBound) WindowSpec

Groups sets a GROUPS BETWEEN start AND end frame (PostgreSQL and SQLite; MySQL fails the build with ErrUnsupported). It needs an ORDER BY.

func (WindowSpec) OrderBy ¶ added in v0.4.0

func (w WindowSpec) OrderBy(items ...Orderable) WindowSpec

OrderBy adds ORDER BY items, exactly like SelectQuery.OrderBy.

func (WindowSpec) PartitionBy ¶ added in v0.4.0

func (w WindowSpec) PartitionBy(es ...Expr) WindowSpec

PartitionBy adds PARTITION BY expressions.

func (WindowSpec) Range ¶ added in v0.4.0

func (w WindowSpec) Range(start, end FrameBound) WindowSpec

Range sets a RANGE BETWEEN start AND end frame. An offset bound needs exactly one ORDER BY item, and the offset is an integer literal, so it suits integer (or numeric) ordering columns; interval offsets for timestamp ordering are not expressible.

func (WindowSpec) Rows ¶ added in v0.4.0

func (w WindowSpec) Rows(start, end FrameBound) WindowSpec

Rows sets a ROWS BETWEEN start AND end frame.

Directories ¶

Path Synopsis
Package advisory takes PostgreSQL advisory locks through momo.
Package advisory takes PostgreSQL advisory locks through momo.
Package agg holds the aggregates that the basic Agg of the root package does not cover: ORDER BY inside the call, WITHIN GROUP ordered-set aggregates, string, array and JSON array aggregation, and the JSON object builders that feed them.
Package agg holds the aggregates that the basic Agg of the root package does not cover: ORDER BY inside the call, WITHIN GROUP ordered-set aggregates, string, array and JSON array aggregation, and the JSON object builders that feed them.
Package arrayop holds the PostgreSQL array predicates: containment, contained-by, overlap and the single-bind "= ANY" membership test.
Package arrayop holds the PostgreSQL array predicates: containment, contained-by, overlap and the single-bind "= ANY" membership test.
Package cli is the momo command line tool as a library.
Package cli is the momo command line tool as a library.
cmd
momo command
Command momo is the momo command line tool.
Command momo is the momo command line tool.
momovet command
Command momovet reports non-constant SQL passed to Momo's raw-SQL functions.
Command momovet reports non-constant SQL passed to Momo's raw-SQL functions.
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
mysqldriver
Package mysqldriver adapts database/sql on github.com/go-sql-driver/mysql to momo for MySQL 8.4 and 9.7 LTS and MariaDB 11.8 LTS.
Package mysqldriver adapts database/sql on github.com/go-sql-driver/mysql to momo for MySQL 8.4 and 9.7 LTS and MariaDB 11.8 LTS.
pgxdriver
Package pgxdriver adapts jackc/pgx v5 to momo.
Package pgxdriver adapts jackc/pgx v5 to momo.
sqlitedriver
Package sqlitedriver adapts mattn/go-sqlite3 and sqlite-vec to momo.
Package sqlitedriver adapts mattn/go-sqlite3 and sqlite-vec to momo.
sqlitepure
Package sqlitepure adapts the pure-Go SQLite driver modernc.org/sqlite to momo.
Package sqlitepure adapts the pure-Go SQLite driver modernc.org/sqlite 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.
testing command
Command testing shows how to test application code against a real PostgreSQL database with momotest: a database or a transaction per test, the project's embedded migrations applied, seeded fixtures, and tests that run in parallel.
Command testing shows how to test application code against a real PostgreSQL database with momotest: a database or a transaction per test, the project's embedded migrations applied, seeded fixtures, and tests that run in parallel.
testing/models
Package models is the schema of the testing example: bank accounts whose balance the database refuses to take below zero.
Package models is the schema of the testing example: bank accounts whose balance the database refuses to take below zero.
testing/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.
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.
Package fts holds the PostgreSQL full-text search expressions: to_tsvector, the tsquery builders, the @@ match, ts_rank, ts_rank_cd and ts_headline.
Package fts holds the PostgreSQL full-text search expressions: to_tsvector, the tsquery builders, the @@ match, ts_rank, ts_rank_cd and ts_headline.
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.
apiquery
Package apiquery holds the request-side helpers the GraphQL, gRPC and OpenAPI generators share: opaque keyset cursors, page-size limits and filters resolved through the generated column allowlist.
Package apiquery holds the request-side helpers the GraphQL, gRPC and OpenAPI generators share: opaque keyset cursors, page-size limits and filters resolved through the generated column allowlist.
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.
fromdb
Package fromdb turns an inspected database schema into Go model source and the schema the matching init snapshot is written from.
Package fromdb turns an inspected database schema into Go model source and the schema the matching init snapshot is written from.
graphql
Package graphql generates a GraphQL schema from momo models: Relay connections with keyset cursors, typed filter and order inputs and one query per model.
Package graphql generates a GraphQL schema from momo models: Relay connections with keyset cursors, typed filter and order inputs and one query per model.
grpc
Package grpc generates protobuf messages and CRUD/list services from momo models.
Package grpc generates protobuf messages and CRUD/list services from momo models.
internal/gosrc
Package gosrc holds the small pieces of Go source assembly the API generators (graphql, grpc, openapi) share.
Package gosrc holds the small pieces of Go source assembly the API generators (graphql, grpc, openapi) share.
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.
openapi
Package openapi generates an OpenAPI 3.1 document from momo models.
Package openapi generates an OpenAPI 3.1 document from momo models.
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.
viz
Package viz draws entity-relationship diagrams of momo models or of a migration snapshot, as Mermaid erDiagram text or Graphviz DOT.
Package viz draws entity-relationship diagrams of momo models or of a migration snapshot, as Mermaid erDiagram text or Graphviz DOT.
Package importer converts models written for another Go ORM into Momo models.
Package importer converts models written for another Go ORM into Momo models.
bun
Package bun reads Bun models from Go source without running it and turns them into importer models plus a report of every behaviour that changes when the project moves to Momo.
Package bun reads Bun models from Go source without running it and turns them into importer models plus a report of every behaviour that changes when the project moves to Momo.
ent
Package ent reads an Ent schema package from Go source without running it and turns it into importer models plus a report of every behaviour that changes when the project moves to Momo.
Package ent reads an Ent schema package from Go source without running it and turns it into importer models plus a report of every behaviour that changes when the project moves to Momo.
gorm
Package gorm reads GORM models from Go source without running it and turns them into importer models plus a report of every behaviour that changes when the project moves to Momo.
Package gorm reads GORM models from Go source without running it and turns them into importer models plus a report of every behaviour that changes when the project moves to Momo.
history
Package history converts the migration history of another tool (Atlas, goose, golang-migrate, Bun) into a Momo migration directory plus the history rows that mark those migrations as applied, so none of them runs again.
Package history converts the migration history of another tool (Atlas, goose, golang-migrate, Bun) into a Momo migration directory plus the history rows that mark those migrations as applied, so none of them runs again.
history/atlas
Package atlas reads an Atlas migration directory (<version>_<name>.sql files and atlas.sum) and the atlas_schema_revisions table into a history.Source.
Package atlas reads an Atlas migration directory (<version>_<name>.sql files and atlas.sum) and the atlas_schema_revisions table into a history.Source.
history/bun
Package bun reads the SQL migrations of a Bun project and its bun_migrations table into a history.Source.
Package bun reads the SQL migrations of a Bun project and its bun_migrations table into a history.Source.
history/golangmigrate
Package golangmigrate reads a golang-migrate directory and its schema_migrations table into a history.Source.
Package golangmigrate reads a golang-migrate directory and its schema_migrations table into a history.Source.
history/goose
Package goose reads a goose migration directory and its version table into a history.Source.
Package goose reads a goose migration directory and its version table into a history.Source.
history/internal/histutil
Package histutil holds the small helpers the tool-specific history readers share.
Package histutil holds the small helpers the tool-specific history readers share.
internal
chunk
Package chunk holds the bind-limit arithmetic that splits a long key list into statements.
Package chunk holds the bind-limit arithmetic that splits a long key list into statements.
compat/schemafixture
Package schemafixture holds the helpers the importer fixture modules share: reading the schema a source ORM created, building the schema Momo derives from the imported models through the real generator, and comparing the two with migrate.Diff.
Package schemafixture holds the helpers the importer fixture modules share: reading the schema a source ORM created, building the schema Momo derives from the imported models through the real generator, and comparing the two with migrate.Diff.
dbdefault
Package dbdefault classifies column defaults for the dbdefault tag: it tells the generator and momo generate --from-db when a Go zero value (false, 0) would differ from the database default and so needs a pointer field.
Package dbdefault classifies column defaults for the dbdefault tag: it tells the generator and momo generate --from-db when a Go zero value (false, 0) would differ from the database default and so needs a pointer field.
docscheck
Package docscheck holds the checks that keep the documentation site honest: the navigation lists real pages, every page is reachable, the Go code blocks compile, and the agent guide carries its rules.
Package docscheck holds the checks that keep the documentation site honest: the navigation lists real pages, every page is reachable, the Go code blocks compile, and the agent guide carries its rules.
exprtest
Package exprtest holds the render-only fixtures that the tests of a feature package (arrayop, fts, ...) need to see the SQL and arguments of a predicate or expression: a one-column projection row and a DB over the recording fake driver.
Package exprtest holds the render-only fixtures that the tests of a feature package (arrayop, fts, ...) need to see the SQL and arguments of a predicate or expression: a one-column projection row and a DB over the recording fake driver.
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.
fkcase
Package fkcase builds the schemas of the foreign key action tests of every migration dialect from models, through the generator's own schema builder, so the tests run the path a user's model takes: the on_delete, on_update and no_constraint tags become foreign keys (or none) before the diff sees them.
Package fkcase builds the schemas of the foreign key action tests of every migration dialect from models, through the generator's own schema builder, so the tests run the path a user's model takes: the on_delete, on_update and no_constraint tags become foreign keys (or none) before the diff sees them.
gendocs
Package gendocs writes the parts of the documentation that restate facts the code already holds: which databases are usable, the dialect capability flags, the Go to SQL type table and the dependency versions.
Package gendocs writes the parts of the documentation that restate facts the code already holds: which databases are usable, the dialect capability flags, the Go to SQL type table and the dependency versions.
gendocs/cmd/gendocs command
Command gendocs rewrites the generated blocks of the documentation.
Command gendocs rewrites the generated blocks of the documentation.
gendocs/reference command
Command reference writes docs/reference.md: every exported identifier of the core and feature packages with the first sentence of its doc comment.
Command reference writes docs/reference.md: every exported identifier of the core and feature packages with the first sentence of its doc comment.
golden
Package golden compares generated output with golden files.
Package golden compares generated output with golden files.
importtest
Package importtest is the shared end-to-end check for history importers.
Package importtest is the shared end-to-end check for history importers.
keyenc
Package keyenc encodes relation key tuples as comparable strings.
Package keyenc encodes relation key tuples as comparable strings.
migratecfg
Package migratecfg is the one reader and validator of the part of momo.yaml that configures the migration engine: the migrate: section and introspect.only.
Package migratecfg is the one reader and validator of the part of momo.yaml that configures the migration engine: the migrate: section and introspect.only.
obstest
Package obstest is test support for the observability tests.
Package obstest is test support for the observability tests.
redact
Package redact masks sensitive query arguments before they reach hooks.
Package redact masks sensitive query arguments before they reach hooks.
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.
snake
Package snake is the Go-style snake_case conversion and the initialism word list behind it.
Package snake is the Go-style snake_case conversion and the initialism word list behind it.
sqliteconf
Package sqliteconf holds the settings type shared by driver/sqlitedriver (cgo, mattn/go-sqlite3) and driver/sqlitepure (pure Go, modernc).
Package sqliteconf holds the settings type shared by driver/sqlitedriver (cgo, mattn/go-sqlite3) and driver/sqlitepure (pure Go, modernc).
sqlmask
Package sqlmask hides the text of database/sql conversion failures.
Package sqlmask hides the text of database/sql conversion failures.
sqlscan
Package sqlscan tokenizes raw SQL fragments into verbatim text and placeholders.
Package sqlscan tokenizes raw SQL fragments into verbatim text and placeholders.
sqlshared
Package sqlshared holds the part of the SQLite adapters that does not depend on the SQLite driver: the momo.Driver implementation over database/sql, transactions, read-only transactions, pinned connections and time scanning.
Package sqlshared holds the part of the SQLite adapters that does not depend on the SQLite driver: the momo.Driver implementation over database/sql, transactions, read-only transactions, pinned connections and time scanning.
sqlstub
Package sqlstub opens a database/sql pool over a stub driver that answers every query with one row holding one fixed value.
Package sqlstub opens a database/sql pool over a stub driver that answers every query with one row holding one fixed value.
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.
vectorparity
Package vectorparity holds the dataset and the exact reference that the vector parity tests share, so that PostgreSQL pgvector and MariaDB are both held to one answer computed in plain Go.
Package vectorparity holds the dataset and the exact reference that the vector parity tests share, so that PostgreSQL pgvector and MariaDB are both held to one answer computed in plain Go.
Package jsonop holds the JSON operators over types.JSON[T] columns (jsonb on PostgreSQL): text extraction, nested paths, containment and key tests.
Package jsonop holds the JSON operators over types.JSON[T] columns (jsonb on PostgreSQL): text extraction, nested paths, containment and key tests.
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.
mysql
Package mysql is the MySQL and MariaDB migration dialect: it reads a live database into a schema.Schema through information_schema, renders DDL, and serializes migration runs with GET_LOCK.
Package mysql is the MySQL and MariaDB migration dialect: it reads a live database into a schema.Schema through information_schema, renders DDL, and serializes migration runs with GET_LOCK.
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.
sqlite
Package sqlite is the SQLite migration dialect: it reads a live database into a schema.Schema through the SQLite catalog (sqlite_schema and the table-valued pragmas), renders a change plan as SQLite DDL, and rebuilds a table with the twelve-step procedure of https://www.sqlite.org/lang_altertable.html#otheralter when ALTER TABLE cannot make the change.
Package sqlite is the SQLite migration dialect: it reads a live database into a schema.Schema through the SQLite catalog (sqlite_schema and the table-valued pragmas), renders a change plan as SQLite DDL, and rebuilds a table with the twelve-step procedure of https://www.sqlite.org/lang_altertable.html#otheralter when ALTER TABLE cannot make the change.
Package momolog provides a log/slog hook for Momo.
Package momolog provides a log/slog hook for Momo.
Package momotest gives an application's tests a real PostgreSQL database with the project's migrations applied, isolated per test.
Package momotest gives an application's tests a real PostgreSQL database with the project's migrations applied, isolated per test.
sqlitetest
Package sqlitetest gives an application's tests an ephemeral SQLite database: a new file in the test's own temporary directory, opened through driver/sqlitedriver (mattn/go-sqlite3 with sqlite-vec), closed and deleted when the test ends.
Package sqlitetest gives an application's tests an ephemeral SQLite database: a new file in the test's own temporary directory, opened through driver/sqlitedriver (mattn/go-sqlite3 with sqlite-vec), closed and deleted when the test ends.
Package momovet provides a go/analysis analyzer that reports SQL text built at run time and passed to Momo's raw-SQL functions.
Package momovet provides a go/analysis analyzer that reports SQL text built at run time and passed to Momo's raw-SQL functions.
Package obs holds ready-made observability hooks for Momo: a Recorder that keeps every statement and relation load, an NPlusOne detector that reports a relation loaded once per row, and a slow-query hook (NewSlowQuery) that can capture a plain EXPLAIN plan.
Package obs holds ready-made observability hooks for Momo: a Recorder that keeps every statement and relation load, an NPlusOne detector that reports a relation loaded once per row, and a slow-query hook (NewSlowQuery) that can capture a plain EXPLAIN plan.
Package schema is Momo's normalized, dialect-aware schema representation.
Package schema is Momo's normalized, dialect-aware schema representation.
Package sqlfn holds the typed scalar SQL functions and operators: Substr, Lower, Upper, Length, CurrentSchema, Case, the arithmetic helpers Add, Sub, Mul and Div, NullIf, CurrentDate, Infinity, CoalesceExpr, Cast and Concat.
Package sqlfn holds the typed scalar SQL functions and operators: Substr, Lower, Upper, Length, CurrentSchema, Case, the arithmetic helpers Add, Sub, Mul and Div, NullIf, CurrentDate, Infinity, CoalesceExpr, Cast and Concat.
Package timeop holds the typed date and time expressions: Extract, AtTimeZone, intervals with AddInterval, SubInterval, AddDuration and SubDuration (down to the microsecond), timestamp differences with Sub, Age and EpochSeconds, Now, CurrentTimestamp, ClockTimestamp, StatementTimestamp and Trunc.
Package timeop holds the typed date and time expressions: Extract, AtTimeZone, intervals with AddInterval, SubInterval, AddDuration and SubDuration (down to the microsecond), timestamp differences with Sub, Age and EpochSeconds, Now, CurrentTimestamp, ClockTimestamp, StatementTimestamp and Trunc.
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 vecop holds the vector distance builders: L2, InnerProduct, Cosine, L1, Hamming and Jaccard.
Package vecop holds the vector distance builders: L2, InnerProduct, Cosine, L1, Hamming and Jaccard.
Package vecsearch applies pgvector search settings (hnsw and ivfflat) to one transaction.
Package vecsearch applies pgvector search settings (hnsw and ivfflat) to one transaction.
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