tsq

package module
v4.3.0 Latest Latest
Warning

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

Go to latest
Published: Jul 14, 2026 License: MIT Imports: 24 Imported by: 0

README

TSQ - 类型安全的 Go SQL 查询代码生成工具

 _____  __    ____
/__   \/ _\  /___ \
  / /\/\ \  //  / /
 / /   _\ \/ \_/ /
 \/    \__/\___,_\

GitHub release (latest by date) Build Status Go Reference Go Report Card

TSQ(Type-Safe Query)会把带注解的 Go 结构体生成为表元数据、CRUD/分页助手和类型安全查询列,让你用 Go API 组合 SQL,而不是在业务代码里手写大量字符串。

当前主线版本是 v4 typed DSLQuery / Build-based 查询链路 / Into 都带 owner 类型,表 owner、结果 owner 和物理表语义已经拆开,联表结果与本地结果扫描会更早在编译期暴露错误。

核心关系现在可以直接记成:

  • Owner:任何可扫描目标
  • Table:物理表 owner,也是 mutation target,并暴露稳定的列/主键元数据
  • Result:投影 owner,只参与查询结果映射

执行层也统一成了显式 context 语义:SQLExecutor / Runtime 的执行方法都把 ctx context.Context 放在第一个参数。

如果只记一件事,可以直接把运行时关系记成:

  • Runtime:表 metadata、索引声明、tracer,以及当前数据库上下文
  • SQLExecutor:最小执行接口,查询 / CRUD / chunked helper 都接这个

先回答三个上手问题

问题 最短答案
最小要写什么? 一个带 @TABLE 注解的 Go struct。
生成后得到什么? 每个表生成一个 *.tsq.go;每个 @RESULT 生成一个 *.result.tsq.go
怎么跑第一条查询? tsq gen ./dbruntime, err := tsq.NewRuntime("sqlite", dsn, database.TSQTables())query, err := tsq.Select(...).From(table).Where(...).Build()query.List(ctx, runtime, ...)

安装

go install github.com/tmoeish/tsq/v4/cmd/tsq@latest

TSQ 本身不附带数据库 driver;你的应用只需要安装自己实际使用的那个 driver。下面的 quickstart 默认用 modernc.org/sqlite,是因为它不依赖 CGO,最适合零配置上手。

也可以从源码构建:

git clone https://github.com/tmoeish/tsq.git
cd tsq
make build
安装 TSQ agent skill

这个仓库同时发布了一个可安装的 agent skill,适合 GitHub Copilot、Claude Code、Gemini CLI 等 coding agent 在别的 Go 项目里学习如何接入和使用 TSQ。

gh skill install tmoeish/tsq tsq --agent github-copilot --scope user

安装方式、手动复制路径和使用示例见 docs/skill.md

5 分钟最小路径

1. 定义一个表结构
package database

// @TABLE(
//   search=["Name","Email"]
// )
type User struct {
	ID    int64  `db:"id" json:"id"`
	Name  string `db:"name" json:"name"`
	Email string `db:"email" json:"email"`
}
2. 生成代码
tsq fmt ./database
tsq gen ./database

gen 接受三种输入:

  • 模块导入路径:github.com/acme/app/internal/database
  • 相对目录:./internal/database
  • 绝对目录:/path/to/app/internal/database

生成后通常会看到:

  • database/user.tsq.goUser 表的列、CRUD、分页和查询助手
  • database/runtime.tsq.go:当前包全部表的 TSQTables() metadata 入口
  • database/*.result.tsq.go:只在你声明 @RESULT 时生成
  • database/sqlite.sql / database/mysql.sql / database/postgres.sql:每种内置方言的 schema 文件;首次生成写入初始建表语句,后续变更会按时间顺序追加带日期注释的增量 DDL
  • database/tsq.json:最新 schema snapshot、初始 schema 文件内容与增量历史记录,用于后续 tsq gen 对账

如果目标文件已经存在,TSQ 只会覆盖已有的生成文件;遇到手写文件会拒绝覆盖并直接报错。

DDL 的默认字符串映射现在更偏向“常规业务字段”:

  • stringsql.NullStringnull.String 以及它们的 type alias / 自定义字符串类型,在没写 size 时默认生成 VARCHAR(255)
  • 写了 size:N 之后,会按方言选更合适的类型;例如 MySQL 超过 VARCHAR 安全范围时会自动切到 MEDIUMTEXT / LONGTEXT
  • 如果字段本身是 TSQ 不认识的自定义类型(例如实现了 driver.Valuer / sql.Scanner 的 JSON slice),可以直接在 db tag 里写 type:JSONtype:TEXTtype:JSONB 这类覆盖;TSQ 会原样写入三个方言的 DDL,并把这个 raw type 记录进 runtime/schema snapshot
  • int / uint 以及基于它们的 enum / type alias 默认按常规整型宽度生成(MySQL INT,Postgres INTEGER);只有显式 int64 / uint64 才会落到 BIGINT
3. 跑第一条查询
package main

import (
	"context"
	"log"

	_ "modernc.org/sqlite"

	"github.com/tmoeish/tsq/v4"
	"github.com/your/module/database"
)

func main() {
	ctx := context.Background()

	runtime, err := tsq.NewRuntime(
		"sqlite",
		"file:app.db?cache=shared",
		database.TSQTables(),
		&tsq.RuntimeOptions{
			TablePolicy: tsq.SchemaPolicyCreateMissing,
			IndexPolicy: tsq.SchemaPolicyCreateMissing,
		},
	)
	if err != nil {
		log.Fatal(err)
	}

	query, err := tsq.
		Select(database.User__Cols...).
		From(database.TableUser).
		Where(database.User_Name.ContainsVal("alice")).
		Build()
	if err != nil {
		log.Fatal(err)
	}

	users, err := query.List(ctx, runtime)
	if err != nil {
		log.Fatal(err)
	}

	log.Printf("loaded %d users", len(users))
}

更完整的从零到 SQLite 示例见 docs/quickstart.md

NewRuntime 现在会自己 sql.Open 并按 driverName 解析 dialect。默认策略是 manual:TSQ 只记录提醒日志,不会自动补表或补索引。要让启动阶段自动补齐缺失对象,可以显式传:

  • TablePolicy: tsq.SchemaPolicyCreateMissing
  • IndexPolicy: tsq.SchemaPolicyCreateMissing

如果你希望 TSQ 进一步校验、重建不匹配对象,或清理 TSQ 托管范围内的多余对象,则使用 SchemaPolicyValidate / SchemaPolicyReconcile / SchemaPolicyManaged

文档导航

文档 适合什么时候看
docs/quickstart.md 从空目录开始,5 分钟跑通 SQLite 示例
docs/concepts.md 想建立 Table 注解、生成文件、查询构建链路、Result、Runtime 的心智模型
docs/skill.md 想把 TSQ 作为一个 agent skill 安装到 Copilot / Claude Code / Gemini CLI
examples/README.md 想按 quickstart / cookbook / full-suite 找示例
BEST_PRACTICES.md 想看输入校验、分页、事务、排序和生产环境建议
MIGRATION_GUIDE.md 从旧 API 迁移到当前 Build-based API

能力矩阵(内置 Dialect)

TSQ 当前内置的 Dialect 实现只有 SQLite / MySQL / PostgreSQL。下面的矩阵描述的是这三者在仓库当前实现下的行为,不再用“完整支持”这种泛化说法。

能力 SQLite MySQL PostgreSQL 说明
生成 CRUD / 分页助手 生成层支持一致
类型安全列与链式查询 tsq.Select(...).From(table).Where(...).Build()
@RESULT 结果映射 生成 *.result.tsq.go
自动乐观锁(version Update/Delete 在执行时按 VersionColumn() 做版本校验
InVar() / NInVar() 动态集合过滤 执行时展开参数
CASE 表达式 构建与执行都支持
行锁读取(FOR UPDATE / FOR SHARE 能否执行取决于运行时 dialect
非递归 CTE / WITH MySQL 会在执行前显式拒绝
FULL JOIN 执行 SQL 可构建,执行能力受方言限制

补充说明:

  • TSQ 现在只内置 SQLite / MySQL / PostgreSQL 三个完整闭环的 Dialect 实现。
  • 如果你要接入自定义数据库,需要实现完整 Dialect 合约,而不是依赖 TSQ 在接口外推断能力、DDL 或索引行为。

常见边界和注意事项

Where(...) / Search(...) 是覆盖式 setter

这两个方法都只能设置一次。

  • Where(cond1, cond2) / Search(col1, col2) 的多个参数会按 AND 组合
  • 需要 OR 时请显式使用 tsq.Or(...)
query, err := tsq.
	Select(database.User__Cols...).
	From(database.TableUser).
	Where(
		database.User_OrgID.EQVal(1),
		tsq.Or(
			database.User_Name.ContainsVal("alice"),
			database.User_Email.ContainsVal("alice"),
		),
	).
	Build()
InVar() 的空切片 / nil 切片语义是“显式不匹配”

InVar() 用于把执行时传入的切片参数展开成 IN (...)
这里有一个刻意设计的边界:如果执行时传入的是空切片或 nil,TSQ 会把它渲染成 IN (NULL),从而让查询保持合法 SQL,同时返回 0 条结果

这不是兜底容错,而是 API 语义的一部分:

  • 适合表达“调用方当前没有任何可匹配 ID / 状态 / 分类”
  • 避免业务层为了空列表额外分叉写一套“不查任何数据”的逻辑
  • InVar() 在 nil / empty / non-empty 三种输入下都保持统一调用方式

如果你的业务想表达“空列表时忽略这个筛选条件”,请在业务层显式分支,不要依赖 InVar() 自动跳过过滤。

NInVar() 的空切片 / nil 切片语义是“显式全匹配”

NInVar() 用于把执行时传入的切片参数展开成 NOT IN (...)
如果执行时传入的是空切片或 nil,TSQ 会把它渲染成一个空结果子查询,让 NOT IN (...) 保持合法 SQL,同时等价于不过滤任何值

这同样是刻意设计的 API 语义:

  • 适合表达“当前没有任何需要排除的 ID / 状态 / 分类”
  • NInVar() 在 nil / empty / non-empty 三种输入下都保持统一调用方式

如果你的业务想表达别的含义,请在业务层显式分支,而不是依赖 TSQ 猜测空切片语义。

Build() 成功不代表所有方言都能执行

Build() 负责校验查询结构本身:列归属、子句顺序、结果映射、子查询形状等。
但像 CTE、FULL JOININTERSECT 这类能力是否可执行,必须等拿到实际执行器和它对应的 dialect 之后才能判断。

仓库里这是刻意保留的行为,因为:

  • 同一个 *tsq.Query 可能会在不同 runtime / registry / executor 上复用
  • 一个进程里可以存在多个 registry,各自绑定不同 dialect
  • Build() 阶段并不知道最终会由哪个数据库实例执行

因此,TSQ 会在 List/Get/Page/Count/... 这类执行入口按实际 executor dialect 做能力校验。
如果你要提前约束方言,请在应用层把 query 的使用范围限制到固定 executor,而不是假设 Build() 能替你推断运行时环境。

同样的规则也适用于行锁:

  • ForUpdate() / ForShare() 会在 Build() 成功
  • 但 SQLite 这类不支持行锁的方言,会在真正执行时返回显式错误
  • NOWAIT / SKIP LOCKED 也属于执行期 capability 校验的一部分
version 字段现在是自动乐观锁语义

如果表声明了 VersionColumn()

  • Update(...) 会自动把版本条件带进 WHERE
  • 更新成功后会把数据库里的 version 自增 1,并同步回内存对象
  • Delete(...) 会按主键 + version 做删除
  • 如果匹配行数少于预期,会返回 ErrOptimisticLockConflict

这不是“仅提供版本元数据”的弱约定,而是默认生效的 mutation 语义。
如果你不想启用自动乐观锁,就不要给表声明 version 列。

Chunked helper 的事务边界由调用方控制

ChunkedInsertChunkedUpdateChunkedDeleteChunkedDeleteByIDs 都接收 SQLExecutor,这是刻意设计:

  • 传普通 *sql.DB / runtime:允许按 chunk 执行,前面成功的 chunk 不会因为后面失败自动回滚
  • runtime.WithTx(...) 提供的事务 executor:整个 chunked 操作就运行在该事务里

换句话说,TSQ 不会在 chunked helper 里偷偷创建外层事务。
如果你的业务需要原子性,请显式用 runtime.WithTx(...),或者自己管理 *sql.Tx

如果你想在 version 乐观锁冲突时自动重试整个事务回调,可以传:

&tsq.TxOptions{Retry: tsq.IsOptimisticLockError}
关键词搜索的“转义”不是 SQL 注入防护

TSQ 的参数绑定本身负责避免把用户输入直接拼进 SQL。
EscapeKeywordSearch 的作用是转义 LIKE 通配符(如 %_\),避免用户输入改变搜索语义。

pageReq := &tsq.PageReq{
	Page:    1,
	Size:    10,
	OrderBy: "id",
	Order:   "asc",
	Keyword: tsq.EscapeKeywordSearch(request.Keyword),
}
普通值默认走 bind 参数,不提供 literal SQL 快捷入口

当前 TSQ 的默认行为是:把普通值绑定成参数,而不是把值直接拼进 SQL 字符串。

  • EQVal("alice")InVal(1, 2, 3)CASE ... THEN "ok" 这类普通值都会进入参数列表
  • 如果你需要列引用或 typed 子查询,请把它们直接作为 EQ/NE/GT/GTE/LT/LTE 的 RHS 传入;如果你需要数据库函数,请继续用 Fn(...)Expr(...)Exprf(...) 这类表达式能力
  • 不要把“手工拼 SQL literal”当成常规扩展方式
推荐使用当前的 Build-based API

仓库当前主路径是:

query, err := tsq.
	Select(database.User__Cols...).
	From(database.TableUser).
	Where(database.User_ID.EQVal(1)).
	Build()

查询写法请优先参考 docs/quickstart.mddocs/concepts.mdexamples/full-suite/main.go 的当前示例。

Builder 中间态可以安全分支,但最终复用优先用已构建 Query

当前 builder 在链式推进时会隔离分支状态:

  • 从同一个中间 builder 继续往下链,不会回写污染之前的分支
  • 但真正需要高频复用的对象仍然应该是 Build() 后得到的 *tsq.Query[Owner]

也就是说,builder 适合表达构建过程,query 适合表达稳定复用的查询形状

生成 helper 不会在导入包时 panic

生成的 *.tsq.go 会在包初始化时准备查询定义,但如果准备失败,错误会在调用对应 helper 时显式返回,而不是因为 import 该包直接 panic

如果你看到类似 initialize XxxQuery 的错误,优先检查:

  • 生成前后的模型/注解是否一致
  • 生成代码是否已重新运行
  • 当前查询涉及的列、索引、@RESULT 投影是否仍然合法
子查询边界要显式遵守
  • Build() 返回的是 *tsq.Query[Owner],owner 类型会沿着 builder 保留下来。
  • 标量比较(如 EQ(subquery) / GT(subquery))、Between(subqueryA, subqueryB),以及 In(subquery) / NIn(subquery) 里的 typed 子查询 必须只选择一列
  • ExistsSub / NExistsSub 只要求传入已 Build() 的子查询,不受返回列数限制。
  • 值比较推荐用 EQVal/NEVal/GTVal/GTEVal/LTVal/LTEVal,列和 typed 子查询则直接作为 EQ/NE/GT/GTE/LT/LTE 的 RHS 传入。
  • 结果投影统一使用包级 tsq.Into(...),不要再写 col.Into(...)

示例入口

开发

make fmt
make lint
make test
make build
make examples
./bin/examples/full-suite

常用目标:

make help
make build
make test
make test-coverage
make fmt
make vet
make lint
make clean
make install
make update-examples

Documentation

Overview

Package tsq provides a typed SQL query builder and execution helpers.

Index

Constants

View Source
const (
	// IndexInitSkip is deprecated; use SchemaPolicyManual.
	IndexInitSkip = SchemaPolicyManual
	// IndexInitValidate is deprecated; use SchemaPolicyValidate.
	IndexInitValidate = SchemaPolicyValidate
	// IndexInitUpsert is deprecated; use SchemaPolicyCreateMissing.
	IndexInitUpsert = SchemaPolicyCreateMissing
)

Variables

This section is empty.

Functions

func ChunkedDelete

func ChunkedDelete[T Table](
	ctx context.Context,
	tx SQLExecutor,
	items []T,
	options ...*ChunkedOptions,
) error

ChunkedDelete deletes items in chunks using the provided executor.

Transaction boundaries are intentionally caller-controlled. Passing a plain *sql.DB or non-transactional executor allows partial progress across chunks; passing a *sql.Tx makes the whole chunked operation participate in that transaction. TSQ does not open an implicit outer transaction for this helper.

func ChunkedDeleteByPKs added in v4.1.14

func ChunkedDeleteByPKs[O Table, T any](
	ctx context.Context,
	tx SQLExecutor,
	pkField TypedColumn[O, T],
	pks []T,
	options ...*ChunkedOptions,
) error

ChunkedDeleteByPKs deletes rows by primary-key values in chunks.

Transaction boundaries are intentionally caller-controlled. Passing a plain *sql.DB or non-transactional executor allows partial progress across chunks; passing a *sql.Tx makes the whole chunked operation participate in that transaction. TSQ does not open an implicit outer transaction for this helper.

func ChunkedInsert

func ChunkedInsert[T Table](
	ctx context.Context,
	tx SQLExecutor,
	items []T,
	options ...*ChunkedInsertOptions,
) error

ChunkedInsert inserts items in chunks using the provided executor.

Transaction boundaries are intentionally caller-controlled. Passing a plain *sql.DB or non-transactional executor allows partial progress across chunks; passing a *sql.Tx makes the whole chunked operation participate in that transaction. TSQ does not open an implicit outer transaction for this helper.

func ChunkedUpdate

func ChunkedUpdate[T Table](
	ctx context.Context,
	tx SQLExecutor,
	items []T,
	options ...*ChunkedOptions,
) error

ChunkedUpdate updates items in chunks using the provided executor.

Transaction boundaries are intentionally caller-controlled. Passing a plain *sql.DB or non-transactional executor allows partial progress across chunks; passing a *sql.Tx makes the whole chunked operation participate in that transaction. TSQ does not open an implicit outer transaction for this helper.

func Delete

func Delete[T Table](
	ctx context.Context,
	tx SQLExecutor,
	item T,
) error

Delete deletes item using the table metadata on T.

func Insert

func Insert[T Table](
	ctx context.Context,
	tx SQLExecutor,
	item T,
) error

Insert inserts item using the table metadata on T.

func IsCommonTransactionRetryableError added in v4.1.9

func IsCommonTransactionRetryableError(err error) bool

IsCommonTransactionRetryableError reports whether err matches any built-in transaction retry helper.

func IsOptimisticLockError added in v4.1.9

func IsOptimisticLockError(err error) bool

IsOptimisticLockError reports whether err is an ErrOptimisticLockConflict.

func IsRetryableNetworkError added in v4.1.9

func IsRetryableNetworkError(err error) bool

IsRetryableNetworkError reports whether err looks like a transient connection failure.

func IsRetryableTransactionConflictError added in v4.1.9

func IsRetryableTransactionConflictError(err error) bool

IsRetryableTransactionConflictError reports whether err looks like a transient database concurrency failure.

func Update

func Update[T Table](
	ctx context.Context,
	tx SQLExecutor,
	item T,
) error

Update updates item using the table metadata on T.

func WithTx1 added in v4.1.9

func WithTx1[T any](
	r *Runtime,
	ctx context.Context,
	options *TxOptions,
	fn func(context.Context, SQLExecutor) (T, error),
) (T, error)

WithTx1 runs fn in a transaction and returns one result value plus an error. The runtime is an explicit parameter because Go does not support generic methods on Runtime.

func WithTx2 added in v4.1.9

func WithTx2[T1, T2 any](
	r *Runtime,
	ctx context.Context,
	options *TxOptions,
	fn func(context.Context, SQLExecutor) (T1, T2, error),
) (T1, T2, error)

WithTx2 runs fn in a transaction and returns two result values plus an error. The runtime is an explicit parameter because Go does not support generic methods on Runtime.

Types

type BoundColumn

type BoundColumn[O Owner] interface {
	SQLColumn
	// contains filtered or unexported methods
}

BoundColumn is a selectable expression bound to a specific owner type.

type CaseStage added in v4.1.1

type CaseStage[T any] interface {
	When(cond Condition, result any) CaseStage[T]
	Else(result any) CaseStage[T]
	End() ValueColumn[T]
}

CaseStage builds a searched CASE expression.

func Case

func Case[T any]() CaseStage[T]

Case creates a searched CASE expression builder.

type ChunkedInsertOptions

type ChunkedInsertOptions struct {
	ChunkSize    int  // 每块处理的数量,默认 1000
	IgnoreErrors bool // 是否忽略重复键插入错误并继续处理后续数据
}

ChunkedInsertOptions 分块插入配置选项。

func DefaultChunkedInsertOptions

func DefaultChunkedInsertOptions() *ChunkedInsertOptions

DefaultChunkedInsertOptions 返回默认的分块插入配置。

type ChunkedOptions

type ChunkedOptions struct {
	ChunkSize int // 每块处理的数量,默认 1000
}

ChunkedOptions 分块执行通用配置选项。

func DefaultChunkedOptions

func DefaultChunkedOptions() *ChunkedOptions

DefaultChunkedOptions 返回默认的分块执行配置。

type Column added in v4.1.0

type Column[O Owner, T any] interface {
	// TypedColumn exposes the common SQL expression metadata and typed value marker.
	TypedColumn[O, T]
	// RHS lets typed expressions flow into comparison predicates on another column.
	RHS[T]
	// SearchColumn marks the expression as usable in keyword search expansion.
	SearchColumn
	// WithTable returns a copy of the column rebound to a different table source.
	WithTable(table Table) Column[O, T]
	// As returns a copy of the column that targets an aliased table reference.
	As(alias string) Column[O, T]

	// IsNull checks whether the column value is NULL.
	IsNull() Condition
	// IsNotNull checks whether the column value is not NULL.
	IsNotNull() Condition

	// EQ compares the column to rhs with =.
	EQ(rhs RHS[T]) Condition
	// NE compares the column to rhs with <>.
	NE(rhs RHS[T]) Condition
	// GT compares the column to rhs with >.
	GT(rhs RHS[T]) Condition
	// GTE compares the column to rhs with >=.
	GTE(rhs RHS[T]) Condition
	// LT compares the column to rhs with <.
	LT(rhs RHS[T]) Condition
	// LTE compares the column to rhs with <=.
	LTE(rhs RHS[T]) Condition
	// Like compares the column to rhs with LIKE.
	Like(rhs RHS[T]) Condition
	// NLike compares the column to rhs with NOT LIKE.
	NLike(rhs RHS[T]) Condition
	// Between compares the column to an inclusive RHS range.
	Between(start, end RHS[T]) Condition
	// NBetween compares the column to values outside an inclusive RHS range.
	NBetween(start, end RHS[T]) Condition
	// In compares the column to a typed membership subquery with IN.
	In(rhs Subquery[T]) Condition
	// NIn compares the column to a typed membership subquery with NOT IN.
	NIn(rhs Subquery[T]) Condition

	// EQVal compares the column to arg with =.
	EQVal(arg T) Condition
	// NEVal compares the column to arg with <>.
	NEVal(arg T) Condition
	// GTVal compares the column to arg with >.
	GTVal(arg T) Condition
	// GTEVal compares the column to arg with >=.
	GTEVal(arg T) Condition
	// LTVal compares the column to arg with <.
	LTVal(arg T) Condition
	// LTEVal compares the column to arg with <=.
	LTEVal(arg T) Condition
	// LikeVal compares the column to arg with LIKE.
	LikeVal(arg T) Condition
	// NLikeVal compares the column to arg with NOT LIKE.
	NLikeVal(arg T) Condition
	// BetweenVal compares the column to an inclusive literal range.
	BetweenVal(start, end T) Condition
	// NBetweenVal compares the column to values outside an inclusive literal range.
	NBetweenVal(start, end T) Condition
	// InVal compares the column to an explicit list of bound values.
	InVal(args ...T) Condition
	// NInVal compares the column to a negated list of bound values.
	NInVal(args ...T) Condition
	// StartsWithVal compares the column to a bound prefix pattern.
	StartsWithVal(str string) Condition
	// NStartsWithVal compares the column to a negated bound prefix pattern.
	NStartsWithVal(str string) Condition
	// EndsWithVal compares the column to a bound suffix pattern.
	EndsWithVal(str string) Condition
	// NEndsWithVal compares the column to a negated bound suffix pattern.
	NEndsWithVal(str string) Condition
	// ContainsVal compares the column to a bound contains pattern.
	ContainsVal(str string) Condition
	// NContainsVal compares the column to a negated bound contains pattern.
	NContainsVal(str string) Condition

	// EQVar compares the column to a runtime-bound value with =.
	EQVar() Condition
	// NEVar compares the column to a runtime-bound value with <>.
	NEVar() Condition
	// GTVar compares the column to a runtime-bound value with >.
	GTVar() Condition
	// GTEVar compares the column to a runtime-bound value with >=.
	GTEVar() Condition
	// LTVar compares the column to a runtime-bound value with <.
	LTVar() Condition
	// LTEVar compares the column to a runtime-bound value with <=.
	LTEVar() Condition
	// LikeVar compares the column to a runtime-bound pattern with LIKE.
	LikeVar() Condition
	// NLikeVar compares the column to a runtime-bound pattern with NOT LIKE.
	NLikeVar() Condition
	// BetweenVar compares the column to two runtime-bound values with BETWEEN.
	BetweenVar() Condition
	// NBetweenVar compares the column to two runtime-bound values with NOT BETWEEN.
	NBetweenVar() Condition
	// InVar binds a slice at execution time for IN predicates.
	InVar() Condition
	// NInVar binds a slice at execution time for NOT IN predicates.
	NInVar() Condition
	// StartsWithVar compares the column to a runtime-bound prefix pattern.
	StartsWithVar() Condition
	// NStartsWithVar compares the column to a negated runtime-bound prefix pattern.
	NStartsWithVar() Condition
	// EndsWithVar compares the column to a runtime-bound suffix pattern.
	EndsWithVar() Condition
	// NEndsWithVar compares the column to a negated runtime-bound suffix pattern.
	NEndsWithVar() Condition
	// ContainsVar compares the column to a runtime-bound contains pattern.
	ContainsVar() Condition
	// NContainsVar compares the column to a negated runtime-bound contains pattern.
	NContainsVar() Condition

	// ExistsSub returns an EXISTS predicate for the supplied subquery.
	ExistsSub(sq rawSubquery) Condition
	// NExistsSub returns a NOT EXISTS predicate for the supplied subquery.
	NExistsSub(sq rawSubquery) Condition
	// Unique returns a deferred portability error because UNIQUE subquery predicates are not supported.
	Unique(sq rawSubquery) Condition
	// NUnique returns a deferred portability error because NOT UNIQUE subquery predicates are not supported.
	NUnique(sq rawSubquery) Condition

	// Pred formats a custom predicate template around the receiver column.
	// The format must contain one %s placeholder for the receiver column plus
	// one %s placeholder for each extra argument.
	//
	// Extra arguments may be:
	//   - another SQL expression such as a Column
	//   - an Expression such as Bind(...)
	//   - a plain Go value, which TSQ turns into a bound parameter
	//
	// Raw subqueries are rejected; use typed RHS values such as a Column or
	// typed Subquery via EQ/NE/GT/GTE/LT/LTE/Like/Between, or use In/ExistsSub
	// style helpers instead.
	//
	// Example:
	//
	//	users.Name.Pred("LOWER(%s) = LOWER(%s)", tsq.Bind("alice"))
	Pred(format string, args ...any) Condition
	// Expr formats the receiver column into a custom SQL expression template.
	// The format must contain exactly one %s placeholder, which receives the
	// receiver column expression.
	//
	// This is an escape hatch for expression wrappers that TSQ does not model
	// directly, such as CAST(%s AS TEXT) or (%s COLLATE NOCASE).
	//
	// Example:
	//
	//	users.Name.Expr("LOWER(%s)")
	Expr(format string) Column[O, T]
	// Exprf formats the receiver column plus extra SQL expressions into a custom
	// SQL expression template. The first %s placeholder receives the receiver
	// column; each additional %s placeholder receives the corresponding argument
	// expression.
	//
	// Extra arguments may be Columns, Expressions, or plain Go values.
	//
	// Example:
	//
	//	users.Name.Exprf("COALESCE(%s, %s)", orgs.Name)
	Exprf(format string, args ...any) Column[O, T]

	// Count wraps the column in COUNT and marks it as an aggregate expression.
	Count() Column[O, int64]
	// Sum wraps the column in SUM and marks it as an aggregate expression.
	Sum() Column[O, T]
	// Avg wraps the column in AVG and marks it as an aggregate expression.
	Avg() Column[O, float64]
	// Max wraps the column in MAX and marks it as an aggregate expression.
	Max() Column[O, T]
	// Min wraps the column in MIN and marks it as an aggregate expression.
	Min() Column[O, T]
	// Distinct wraps the column in DISTINCT and marks it as a distinct expression.
	Distinct() Column[O, T]

	// Upper wraps the column in UPPER.
	Upper() Column[O, T]
	// Lower wraps the column in LOWER.
	Lower() Column[O, T]
	// Substring wraps the column in SUBSTRING using start and length.
	Substring(start, length int) Column[O, T]
	// Length wraps the column in LENGTH.
	Length() Column[O, T]
	// Trim wraps the column in TRIM.
	Trim() Column[O, T]
	// Concat appends str to the column expression with CONCAT.
	Concat(str string) Column[O, T]
	// Now returns a NOW expression.
	Now() Column[O, T]
	// Date wraps the column in DATE.
	Date() Column[O, T]
	// Year wraps the column in YEAR.
	Year() Column[O, T]
	// Month wraps the column in MONTH.
	Month() Column[O, T]
	// Day wraps the column in DAY.
	Day() Column[O, T]
	// Round wraps the column in ROUND with precision.
	Round(precision int) Column[O, T]
	// Ceil wraps the column in CEIL.
	Ceil() Column[O, T]
	// Floor wraps the column in FLOOR.
	Floor() Column[O, T]
	// Abs wraps the column in ABS.
	Abs() Column[O, T]
	// Coalesce wraps the column in COALESCE with a fallback value.
	Coalesce(value any) Column[O, T]
	// NullIf wraps the column in NULLIF with value.
	NullIf(value any) Column[O, T]

	// Asc creates an ascending ORDER BY clause for this column.
	Asc() OrderBy
	// Desc creates a descending ORDER BY clause for this column.
	Desc() OrderBy
}

Column is the user-facing typed SQL expression API that preserves fluent chaining without exposing the concrete implementation.

func NewCol

func NewCol[O Table, T any](baseName, jsonFieldName string, fieldPointer func(*O) *T) Column[O, T]

NewCol creates a new typed column for the table represented by O.

type CompoundStage added in v4.1.1

type CompoundStage[O Owner] interface {
	QueryStage[O]
	ForUpdate() LockedStage[O]
	ForShare() LockedStage[O]
	Union(other QueryStage[O]) CompoundStage[O]
	UnionAll(other QueryStage[O]) CompoundStage[O]
	Intersect(other QueryStage[O]) CompoundStage[O]
	IntersectAll(other QueryStage[O]) CompoundStage[O]
	Except(other QueryStage[O]) CompoundStage[O]
	ExceptAll(other QueryStage[O]) CompoundStage[O]
}

CompoundStage is the query state after one or more set operations.

type Condition

type Condition interface {
	Tables() map[string]Table // Tables returns the tables referenced by the predicate.
	Clause() string           // Clause returns the predicate SQL in canonical form.
	Args() []any              // Args returns the bind arguments captured by the predicate.
}

Condition is the runtime view of a rendered SQL predicate.

func And

func And(conds ...Condition) Condition

And combines conditions with SQL AND.

func Or

func Or(conds ...Condition) Condition

Or combines conditions with SQL OR.

type ErrAmbiguousSortField

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

ErrAmbiguousSortField reports that a sort field matches multiple selected columns.

func (*ErrAmbiguousSortField) Error

func (e *ErrAmbiguousSortField) Error() string

Error implements error.

func (*ErrAmbiguousSortField) Is

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

Is reports whether target is an *ErrAmbiguousSortField for the same field. An *ErrAmbiguousSortField with an empty field matches any ErrAmbiguousSortField, enabling both type-level and value-level errors.Is checks.

type ErrIndexMissing

type ErrIndexMissing struct {
	Table  string   // Table is the table that should contain the index.
	Name   string   // Name is the expected index name.
	Fields []string // Fields is the expected indexed column order.
	Unique bool     // Unique reports whether the missing index should be unique.
}

ErrIndexMissing reports that an expected index was not found.

func (*ErrIndexMissing) Error

func (e *ErrIndexMissing) Error() string

Error implements error.

type ErrOptimisticLockConflict added in v4.0.2

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

ErrOptimisticLockConflict reports that a version-guarded mutation matched fewer rows than expected.

func (*ErrOptimisticLockConflict) Error added in v4.0.2

func (e *ErrOptimisticLockConflict) Error() string

Error implements error.

func (*ErrOptimisticLockConflict) Is added in v4.0.2

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

Is reports whether target is an optimistic lock conflict.

type ErrOrderCountMismatch

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

ErrOrderCountMismatch reports that the ORDER BY field and direction counts differ.

func (*ErrOrderCountMismatch) Error

func (e *ErrOrderCountMismatch) Error() string

Error implements error.

func (*ErrOrderCountMismatch) Is

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

Is reports whether target is an *ErrOrderCountMismatch with the same counts. An *ErrOrderCountMismatch with zero orderBys and zero orders matches any ErrOrderCountMismatch, enabling type-level errors.Is checks.

type ErrTableMissing added in v4.1.19

type ErrTableMissing struct {
	Name string // Name is the expected physical table name.
}

ErrTableMissing reports that an expected table was not found.

func (*ErrTableMissing) Error added in v4.1.19

func (e *ErrTableMissing) Error() string

Error implements error.

type ErrUnknownSortField

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

ErrUnknownSortField reports that a requested sort field is unknown.

func (*ErrUnknownSortField) Error

func (e *ErrUnknownSortField) Error() string

Error implements error.

func (*ErrUnknownSortField) Is

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

Is reports whether target is an *ErrUnknownSortField for the same field. An *ErrUnknownSortField with an empty field matches any ErrUnknownSortField, enabling both type-level and value-level errors.Is checks.

type Expression

type Expression interface {
	Expr() string // Expr returns the SQL fragment text.
	Args() []any  // Args returns the bind arguments referenced by Expr.
}

Expression represents a SQL fragment plus the args needed to render it safely.

func Bind

func Bind(value any) Expression

Bind returns a parameterized expression for value.

func BindSlice

func BindSlice(values any) Expression

BindSlice returns a comma-separated placeholder list for a slice value.

type FilteredStage added in v4.1.1

type FilteredStage[O Owner] interface {
	QueryStage[O]
	GroupBy(cols ...SQLColumn) GroupedStage[O]
	ForUpdate() LockedStage[O]
	ForShare() LockedStage[O]
	Union(other QueryStage[O]) CompoundStage[O]
	UnionAll(other QueryStage[O]) CompoundStage[O]
	Intersect(other QueryStage[O]) CompoundStage[O]
	IntersectAll(other QueryStage[O]) CompoundStage[O]
	Except(other QueryStage[O]) CompoundStage[O]
	ExceptAll(other QueryStage[O]) CompoundStage[O]
}

FilteredStage is the query state after both Where(...) and Search(...).

type FromStage added in v4.1.1

type FromStage[O Owner] interface {
	Select(cols ...BoundColumn[O]) *queryBuilder[O]
}

FromStage is the result of From(...) before Select(...) is attached.

func From

func From[O Owner](table Table) FromStage[O]

From creates a new state-machine builder with the specified base table.

type GroupedStage added in v4.1.1

type GroupedStage[O Owner] interface {
	QueryStage[O]
	Having(conds ...Condition) HavingStage[O]
	ForUpdate() LockedStage[O]
	ForShare() LockedStage[O]
	Union(other QueryStage[O]) CompoundStage[O]
	UnionAll(other QueryStage[O]) CompoundStage[O]
	Intersect(other QueryStage[O]) CompoundStage[O]
	IntersectAll(other QueryStage[O]) CompoundStage[O]
	Except(other QueryStage[O]) CompoundStage[O]
	ExceptAll(other QueryStage[O]) CompoundStage[O]
}

GroupedStage is the query state after GroupBy(...).

type HavingStage added in v4.1.1

type HavingStage[O Owner] interface {
	QueryStage[O]
	ForUpdate() LockedStage[O]
	ForShare() LockedStage[O]
	Union(other QueryStage[O]) CompoundStage[O]
	UnionAll(other QueryStage[O]) CompoundStage[O]
	Intersect(other QueryStage[O]) CompoundStage[O]
	IntersectAll(other QueryStage[O]) CompoundStage[O]
	Except(other QueryStage[O]) CompoundStage[O]
	ExceptAll(other QueryStage[O]) CompoundStage[O]
}

HavingStage is the query state after Having(...).

type IndexInitMode

type IndexInitMode = SchemaPolicy

IndexInitMode is kept as a deprecated alias for SchemaPolicy during the policy rename.

type LockedStage added in v4.1.1

type LockedStage[O Owner] interface {
	QueryStage[O]
	NoWait() LockedStage[O]
	SkipLocked() LockedStage[O]
}

LockedStage is the query state after ForUpdate()/ForShare().

type Logger added in v4.1.19

type Logger interface {
	Enabled(ctx context.Context, level slog.Level) bool
	LogAttrs(ctx context.Context, level slog.Level, msg string, attrs ...slog.Attr)
}

Logger is the subset of slog.Logger used by runtime bootstrap.

type Order

type Order string

Order represents a SQL ORDER BY direction.

const (
	// ASC sorts rows in ascending order.
	ASC Order = "ASC" // Ascending order
	// DESC sorts rows in descending order.
	DESC Order = "DESC" // Descending order
)

func ReverseOrder

func ReverseOrder(order Order) Order

ReverseOrder returns the opposite sort direction.

type OrderBy

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

OrderBy represents an ORDER BY clause with its column and direction.

func (OrderBy) Expr

func (ob OrderBy) Expr() string

Expr returns the SQL expression for this ORDER BY clause.

func (OrderBy) Field

func (ob OrderBy) Field() SQLColumn

Field returns the ordered column.

func (OrderBy) Order

func (ob OrderBy) Order() Order

Order returns the sort direction.

type Owner

type Owner interface {
	// TSQOwner marks a type as a valid tsq scan owner.
	TSQOwner()
}

Owner marks any Go type that tsq can scan selected fields into. Table and Result are the two specialized owner kinds built on top of it.

type PageRequest added in v4.0.6

type PageRequest struct {
	Size    int    `json:"size"     query:"size"`     // Size is the requested page size.
	Page    int    `json:"page"     query:"page"`     // Page is the 1-based page number.
	OrderBy string `json:"order_by" query:"order_by"` // OrderBy lists sortable field names separated by commas.
	Order   string `json:"order"    query:"order"`    // Order lists sort directions aligned with OrderBy.
	Keyword string `json:"keyword"  query:"keyword"`  // Keyword carries the optional free-text search term.
}

PageRequest captures a page request, sort instructions, and optional keyword search.

func NewPageRequest added in v4.0.6

func NewPageRequest(params url.Values) *PageRequest

NewPageRequest creates *PageRequest from query parameters(e.g. page=1&size=20&order_by=id&order=DESC).

func (*PageRequest) Normalize added in v4.0.6

func (r *PageRequest) Normalize() error

Normalize applies default page values and clamps the requested page size.

func (*PageRequest) Offset added in v4.0.6

func (r *PageRequest) Offset() int

Offset calculates the offset value for SQL LIMIT clause. Returns 0 if calculation would overflow; guaranteed safe for use in SQL.

func (*PageRequest) ToQuery added in v4.0.6

func (r *PageRequest) ToQuery() url.Values

ToQuery serializes the request back into URL query parameters.

func (*PageRequest) Validate added in v4.0.6

func (r *PageRequest) Validate() error

Validate reports invalid paging or sorting input without mutating r.

type PageResponse added in v4.0.6

type PageResponse[T any] struct {
	PageRequest

	Total     int64 `json:"total"`      // Total is the full number of matching rows.
	TotalPage int64 `json:"total_page"` // TotalPage is the number of available pages after rounding up.
	Data      []*T  `json:"data"`       // Data contains the rows for the current page.
}

PageResponse wraps paginated data with request and count metadata.

func NewPageResponse added in v4.0.6

func NewPageResponse[T any](r *PageRequest, total int64, data []*T) *PageResponse[T]

NewPageResponse creates a PageResponse from the request, total count, and data.

func (*PageResponse[T]) HasNext added in v4.0.6

func (r *PageResponse[T]) HasNext() bool

HasNext reports whether another page exists after the current one.

func (*PageResponse[T]) HasPrev added in v4.0.6

func (r *PageResponse[T]) HasPrev() bool

HasPrev reports whether a page exists before the current one.

func (*PageResponse[T]) IsEmpty added in v4.0.6

func (r *PageResponse[T]) IsEmpty() bool

IsEmpty reports whether the current page contains any rows.

type Query

type Query[O Owner] struct {
	// contains filtered or unexported fields
}

Query is a compiled SQL query with count, list, and keyword-search variants. Query is the immutable, concurrency-safe result of Build, separating query definition from execution.

func (*Query[O]) Count

func (q *Query[O]) Count(
	ctx context.Context,
	tx SQLExecutor,
	args ...any,
) (int, error)

Count executes the count query and returns the number of matching records. The result is truncated to int; use Count64 when an int64 is required.

func (*Query[O]) Count64

func (q *Query[O]) Count64(
	ctx context.Context,
	tx SQLExecutor,
	args ...any,
) (int64, error)

Count64 executes the count query and returns the number of matching records as int64. This avoids truncation on large result sets or 32-bit platforms.

func (*Query[O]) CountSQL added in v4.0.2

func (q *Query[O]) CountSQL() string

CountSQL returns the COUNT query SQL statement.

func (*Query[O]) Exists

func (q *Query[O]) Exists(
	ctx context.Context,
	tx SQLExecutor,
	args ...any,
) (bool, error)

Exists reports whether any records match the query conditions.

func (*Query[O]) Get added in v4.1.23

func (q *Query[O]) Get(
	ctx context.Context,
	tx SQLExecutor,
	args ...any,
) (*O, error)

Get executes q and returns one row or nil when no row matches.

func (*Query[O]) GetOrErr added in v4.1.23

func (q *Query[O]) GetOrErr(
	ctx context.Context,
	tx SQLExecutor,
	args ...any,
) (*O, error)

GetOrErr executes q and returns one row or sql.ErrNoRows.

func (*Query[O]) KeywordCountSQL added in v4.0.2

func (q *Query[O]) KeywordCountSQL() string

KeywordCountSQL returns the keyword-search COUNT query SQL statement.

func (*Query[O]) KeywordListSQL added in v4.0.2

func (q *Query[O]) KeywordListSQL() string

KeywordListSQL returns the keyword-search SELECT query SQL statement.

func (*Query[O]) List added in v4.1.23

func (q *Query[O]) List(
	ctx context.Context,
	tx SQLExecutor,
	args ...any,
) ([]*O, error)

List executes q and returns all matching rows.

func (*Query[O]) ListSQL

func (q *Query[O]) ListSQL() string

ListSQL returns the main SELECT query SQL statement.

func (*Query[O]) Load

func (q *Query[O]) Load(
	ctx context.Context,
	tx SQLExecutor,
	holder *O,
	args ...any,
) error

Load executes q and scans one row into holder.

func (*Query[O]) Page added in v4.1.23

func (q *Query[O]) Page(
	ctx context.Context,
	tx SQLExecutor,
	page *PageRequest,
	args ...any,
) (*PageResponse[O], error)

Page executes a paginated query with the provided page parameters.

func (*Query[O]) QueryFloat

func (q *Query[O]) QueryFloat(
	ctx context.Context,
	tx SQLExecutor,
	args ...any,
) (float64, error)

QueryFloat executes the query and returns a single float result.

func (*Query[O]) QueryInt

func (q *Query[O]) QueryInt(
	ctx context.Context,
	tx SQLExecutor,
	args ...any,
) (int64, error)

QueryInt executes the query and returns a single integer result.

func (*Query[O]) QueryString added in v4.0.2

func (q *Query[O]) QueryString(
	ctx context.Context,
	tx SQLExecutor,
	args ...any,
) (string, error)

QueryString executes the query and returns a single string result.

type QueryStage added in v4.1.1

type QueryStage[O Owner] interface {
	Build() (*Query[O], error)
	MustBuild() *Query[O]
	Get(ctx context.Context, tx SQLExecutor, args ...any) (*O, error)
	GetOrErr(ctx context.Context, tx SQLExecutor, args ...any) (*O, error)
	Load(ctx context.Context, tx SQLExecutor, holder *O, args ...any) error
	Exists(ctx context.Context, tx SQLExecutor, args ...any) (bool, error)
	Count(ctx context.Context, tx SQLExecutor, args ...any) (int, error)
	List(ctx context.Context, tx SQLExecutor, args ...any) ([]*O, error)
	Page(ctx context.Context, tx SQLExecutor, page *PageRequest, args ...any) (*PageResponse[O], error)
}

QueryStage is a buildable query state that can participate in CTEs and set operations.

type RHS added in v4.1.15

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

RHS is a typed right-hand-side operand for scalar predicates such as EQ/NE/GT/GTE/LT/LTE/Like/NLike.

Supported RHS values are TSQ typed columns/expressions and typed Subquery handles built with BuildSubquery or AsSubquery. Plain Go values intentionally use the dedicated EQVal/NEVal/GTVal/GTEVal/LTVal/LTEVal helpers, while runtime-bound placeholders continue to use EQVar/NEVar/GTVar/GTEVar/LTVar/LTEVar.

type RegistrationError

type RegistrationError struct {
	Type      RegistrationErrorType // Type classifies the registration failure.
	TableName string                // TableName identifies the conflicting or invalid table entry.
	Message   string                // Message contains the user-facing error text.
}

RegistrationError reports a table-registration failure.

func (*RegistrationError) Error

func (e *RegistrationError) Error() string

Error implements error.

type RegistrationErrorType

type RegistrationErrorType string

RegistrationErrorType identifies a table-registration failure category.

const (
	// RegistrationErrorNilTable means RegisterTable received a nil table.
	RegistrationErrorNilTable RegistrationErrorType = "nil_table"
	// RegistrationErrorInvalidIndex means RegisterTable received invalid index metadata.
	RegistrationErrorInvalidIndex RegistrationErrorType = "invalid_index"
	// RegistrationErrorDuplicate means the same table key was registered twice.
	RegistrationErrorDuplicate RegistrationErrorType = "duplicate"
)

type Result

type Result interface {
	Owner
	// TSQResult marks a scan owner as projection-only.
	TSQResult()
}

Result marks a projection-only owner. It participates in typed SELECT flows but is not a physical mutation target.

type ResultColumn added in v4.1.0

type ResultColumn[O Owner, T any] interface {
	TypedColumn[O, T]
}

ResultColumn is the user-facing typed projection API returned by MapInto. It keeps result bindings inspectable without exposing the concrete projection implementation.

func MapInto added in v4.0.2

func MapInto[Target Owner, T any](
	source ValueColumn[T],
	fieldPointer func(*Target) *T,
	jsonFieldName string,
) ResultColumn[Target, T]

MapInto creates a result-owned projection column from another typed expression.

type Runtime

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

Runtime owns the initialized TSQ process state used for execution, index setup, identifier validation, and tracing.

func NewRuntime

func NewRuntime(
	driverName string,
	dsn string,
	tables []TableRegistration,
	options ...*RuntimeOptions,
) (*Runtime, error)

NewRuntime opens a database connection, resolves the SQL dialect from driverName, and constructs an initialized runtime for the provided table metadata.

func (*Runtime) DB added in v4.1.4

func (r *Runtime) DB() *sql.DB

DB returns the current *sql.DB.

func (*Runtime) ExecContext added in v4.1.9

func (r *Runtime) ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error)

ExecContext executes a statement against the runtime database.

func (*Runtime) QueryContext added in v4.1.9

func (r *Runtime) QueryContext(ctx context.Context, query string, args ...any) (*sql.Rows, error)

QueryContext executes a query against the runtime database.

func (*Runtime) QueryRowContext added in v4.1.9

func (r *Runtime) QueryRowContext(ctx context.Context, query string, args ...any) *sql.Row

QueryRowContext executes a query expected to return at most one row.

func (*Runtime) SQLDialect added in v4.1.4

func (r *Runtime) SQLDialect() tsqdialect.Dialect

SQLDialect returns the concrete SQL dialect bound to this runtime.

func (*Runtime) ValidateIdentifiersForDialect

func (r *Runtime) ValidateIdentifiersForDialect() error

ValidateIdentifiersForDialect validates all configured table and column identifiers against the current database dialect.

func (*Runtime) WithTx added in v4.1.9

func (r *Runtime) WithTx(
	ctx context.Context,
	options *TxOptions,
	fn func(context.Context, SQLExecutor) error,
) error

WithTx starts a transaction on the runtime database and passes a dialect-aware executor to fn. It manages BeginTx, Commit, and Rollback automatically.

type RuntimeOptions added in v4.1.18

type RuntimeOptions struct {
	TablePolicy SchemaPolicy // TablePolicy chooses how TSQ manages declared tables and columns during NewRuntime.
	IndexPolicy SchemaPolicy // IndexPolicy chooses how TSQ manages declared indexes during NewRuntime.
	Tracers     []Tracer     // Tracers configures the runtime's tracer chain during NewRuntime.
	Logger      Logger       // Logger receives schema bootstrap decisions and executed DDL.
	// IdentifierValidationMode controls how to handle identifier length violations:
	// "strict" = fail if any identifier exceeds dialect limits (default for most dialects)
	// "warn"   = log warnings but allow (for permissive databases)
	// "skip"   = no validation (useful for dynamic schemas)
	IdentifierValidationMode string
}

RuntimeOptions controls runtime initialization behavior.

type SQLColumn

type SQLColumn interface {
	SQLExpr() string       // SQLExpr returns the rendered expression, such as "users.name".
	OutputName() string    // OutputName returns the default scan alias for the expression.
	JSONFieldName() string // JSONFieldName returns the stable field name used by JSON-facing helpers.
	Table() Table          // Table returns the primary table that owns the expression.
	Name() string          // Name returns the physical column name when the expression comes from a table column.
	QualifiedName() string // QualifiedName returns the expression with its table qualifier or transformation applied.
	// contains filtered or unexported methods
}

SQLColumn is the runtime view of a selectable SQL expression.

func AliasColumns

func AliasColumns(cols []SQLColumn, table Table) []SQLColumn

AliasColumns rebinds each column onto table while preserving order.

func RebindColumn

func RebindColumn(col SQLColumn, table Table) SQLColumn

RebindColumn returns col rebound to table when the column supports rebinding.

func SQLColumns

func SQLColumns[O Owner](cols ...BoundColumn[O]) []SQLColumn

SQLColumns converts typed columns into a runtime slice of SQLColumn values.

type SQLExecutor added in v4.0.2

type SQLExecutor interface {
	QueryContext(ctx context.Context, query string, args ...any) (*sql.Rows, error)
	QueryRowContext(ctx context.Context, query string, args ...any) *sql.Row
	ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error)
}

SQLExecutor defines the shared query execution surface implemented by database/sql entry points such as *sql.DB and *sql.Tx. The standard library does not provide this exact interface, so tsq defines the minimal Context-based method set it needs.

func WrapExecutor

func WrapExecutor(exec SQLExecutor, sqlDialect dialect.Dialect) SQLExecutor

WrapExecutor wraps a SQLExecutor with dialect information.

type SchemaPolicy added in v4.1.19

type SchemaPolicy string

SchemaPolicy controls how TSQ manages declared schema objects during runtime bootstrap.

const (
	// SchemaPolicyManual leaves declared objects untouched and only logs a reminder.
	SchemaPolicyManual SchemaPolicy = "manual"
	// SchemaPolicyValidate fails when a declared object is missing or mismatched.
	SchemaPolicyValidate SchemaPolicy = "validate"
	// SchemaPolicyCreateMissing creates missing declared objects but still fails on mismatches.
	SchemaPolicyCreateMissing SchemaPolicy = "create_missing"
	// SchemaPolicyReconcile creates missing declared objects and reconciles mismatches.
	SchemaPolicyReconcile SchemaPolicy = "reconcile"
	// SchemaPolicyManaged reconciles declared objects and removes TSQ-managed extras.
	SchemaPolicyManaged SchemaPolicy = "managed"
)

type SearchColumn

type SearchColumn interface {
	SQLColumn
	// contains filtered or unexported methods
}

SearchColumn marks expressions that may participate in keyword search expansion.

type SearchStage added in v4.1.1

type SearchStage[O Owner] interface {
	QueryStage[O]
	Where(conds ...Condition) FilteredStage[O]
	GroupBy(cols ...SQLColumn) GroupedStage[O]
	ForUpdate() LockedStage[O]
	ForShare() LockedStage[O]
	Union(other QueryStage[O]) CompoundStage[O]
	UnionAll(other QueryStage[O]) CompoundStage[O]
	Intersect(other QueryStage[O]) CompoundStage[O]
	IntersectAll(other QueryStage[O]) CompoundStage[O]
	Except(other QueryStage[O]) CompoundStage[O]
	ExceptAll(other QueryStage[O]) CompoundStage[O]
}

SearchStage is the query state after Search(...).

type SelectStage added in v4.1.1

type SelectStage[O Owner] interface {
	From(table Table) *queryBuilder[O]
}

SelectStage is the result of Select(...) before From(...) is attached.

func Select

func Select[O Owner](cols ...BoundColumn[O]) SelectStage[O]

Select creates a new state-machine builder with the specified columns.

type Subquery added in v4.1.15

type Subquery[T any] interface {
	RHS[T]
	// contains filtered or unexported methods
}

Subquery is an opaque typed handle for a built single-column subquery. Construct it with BuildSubquery or AsSubquery.

func AsSubquery added in v4.1.15

func AsSubquery[O Owner, T any](query *Query[O], selected TypedColumn[O, T]) (Subquery[T], error)

AsSubquery validates that query is a built single-column query whose selected column matches selected, then returns a typed subquery handle suitable for RHS comparisons such as EQ/NE/GT/GTE/LT/LTE/Like/Between and for In/NIn.

func BuildSubquery added in v4.1.15

func BuildSubquery[O Owner, T any](qb QueryStage[O], selected TypedColumn[O, T]) (Subquery[T], error)

BuildSubquery builds qb and validates that it returns exactly one column with the same typed expression metadata as selected.

type Table

type Table interface {
	Owner
	Cols() []SQLColumn             // Cols returns the physical columns exposed by the table.
	Table() string                 // Table returns the SQL identifier used in rendered queries.
	SearchColumns() []SearchColumn // SearchColumns returns columns eligible for keyword-search helpers.
	PrimaryKeys() []string         // PrimaryKeys returns the primary-key column names in declaration order.
	AutoIncrement() bool           // AutoIncrement reports whether inserts rely on generated primary keys.
	VersionColumn() string         // VersionColumn returns the optimistic-lock column name, if any.
}

Table defines a physical SQL table source. Unlike Result, a Table is both a scan owner and a mutation target, and it exposes stable column and primary-key metadata for metadata-driven execution.

func AliasTable

func AliasTable(table Table, alias string) Table

AliasTable returns table wrapped with the provided SQL alias.

func CTE

func CTE[O Owner](name string, query QueryStage[O]) Table

CTE creates a reusable non-recursive WITH/CTE table handle from a query. Rebind existing columns to the returned table via RebindColumn or Col.WithTable to reference the CTE output columns in outer queries.

type TableColumn

type TableColumn[O Table] interface {
	BoundColumn[O]
	SearchColumn
	// contains filtered or unexported methods
}

TableColumn is a physical source column that belongs to a table owner.

type TableIndex added in v4.1.4

type TableIndex struct {
	Name   string   // Name is the stable physical index name.
	Fields []string // Fields preserves the indexed column order.
	Unique bool     // Unique reports whether the index enforces uniqueness.
}

TableIndex declares one physical index owned by a registered table.

type TableRegistration added in v4.1.11

type TableRegistration struct {
	Table   Table                      // Table is the physical table metadata.
	Columns []tsqdialect.DDLColumnSpec // Columns declares the physical column schema owned by Table.
	Indexes []TableIndex               // Indexes declares the indexes owned by Table.
}

TableRegistration describes one table plus its declared indexes for runtime bootstrap.

type Tracer

type Tracer func(next func(ctx context.Context) error) func(ctx context.Context) error

Tracer wraps a function call with tracing behavior. Configure tracers via RuntimeOptions.Tracers when constructing a Runtime.

type TxOptions added in v4.1.9

type TxOptions struct {
	// SQL passes through to database/sql BeginTx.
	SQL *sql.TxOptions
	// Retry decides whether a failed transaction attempt should be retried.
	// When set, RetryConfig defaults are applied automatically unless overridden.
	Retry TxRetryPredicate
	// RetryConfig customizes retry timing and attempt limits when Retry is set.
	RetryConfig *TxRetryConfig
}

TxOptions configures Runtime.WithTx.

type TxRetryConfig added in v4.1.9

type TxRetryConfig struct {
	// MaxAttempts is the total number of attempts, including the first try.
	MaxAttempts int
	// InitialBackoff is the delay after the first retryable failure.
	InitialBackoff time.Duration
	// MaxBackoff caps exponential backoff. Zero means no cap.
	MaxBackoff time.Duration
	// BackoffMultiplier grows the delay after each retryable failure.
	BackoffMultiplier float64
}

TxRetryConfig configures retry timing and attempt limits for Runtime.WithTx.

func DefaultTxRetryConfig added in v4.1.9

func DefaultTxRetryConfig() *TxRetryConfig

DefaultTxRetryConfig returns the default retry timing used when Retry is set.

type TxRetryPredicate added in v4.1.9

type TxRetryPredicate func(err error) bool

TxRetryPredicate reports whether err should retry the whole transaction callback.

type TypedColumn

type TypedColumn[O Owner, T any] interface {
	BoundColumn[O]
	// contains filtered or unexported methods
}

TypedColumn is a selectable expression that also carries the scanned Go value type.

type ValueColumn added in v4.1.1

type ValueColumn[T any] interface {
	SQLColumn
	// contains filtered or unexported methods
}

ValueColumn is a selectable expression that carries a scanned Go value type without exposing its owner in the API surface.

type WhereStage added in v4.1.1

type WhereStage[O Owner] interface {
	QueryStage[O]
	Search(cols ...SearchColumn) FilteredStage[O]
	GroupBy(cols ...SQLColumn) GroupedStage[O]
	ForUpdate() LockedStage[O]
	ForShare() LockedStage[O]
	Union(other QueryStage[O]) CompoundStage[O]
	UnionAll(other QueryStage[O]) CompoundStage[O]
	Intersect(other QueryStage[O]) CompoundStage[O]
	IntersectAll(other QueryStage[O]) CompoundStage[O]
	Except(other QueryStage[O]) CompoundStage[O]
	ExceptAll(other QueryStage[O]) CompoundStage[O]
}

WhereStage is the query state after Where(...).

Directories

Path Synopsis
cmd
tsq command
examples
academy
Code generated by tsq-v4.2.0.
Code generated by tsq-v4.2.0.
advanced command
full-suite command
quickstart command
internal
cmd

Jump to

Keyboard shortcuts

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