Documentation
¶
Overview ¶
Package config loads and validates sqletch.yaml. Strict decoding: unknown keys are errors, required fields are named in messages. See docs/design/07-cli-config.md.
Index ¶
- Constants
- func SQLiteFileURIString(absPath, rawQuery string) string
- type Cache
- type Config
- func (c Config) Abs(p string) string
- func (c Config) ExpandGlobs(patterns []string) ([]string, error)
- func (c Config) Expanded(query string) bool
- func (c Config) NativeOracle() bool
- func (c Config) NullOverridesFor(query string) map[string]bool
- func (c Config) ResolveTargets() (Resolution, []diagnostics.Diagnostic)
- type Database
- type Expansion
- type Output
- type Override
- type Policy
- type PolicyParam
- type Resolution
- type ResolvedTarget
- type SQLiteFileURI
- type Schema
- type Target
- type TreeCaps
- type Verification
Constants ¶
const ( OracleServer = "server" OracleNative = "native" )
Oracle backend names.
const DefaultVerificationMaxShapes = 4096
DefaultVerificationMaxShapes is the shape budget `check --exhaustive` gets when the config says nothing.
Variables ¶
This section is empty.
Functions ¶
func SQLiteFileURIString ¶
SQLiteFileURIString rebuilds a `file:` URI for an absolute path, preserving a raw query string verbatim. It is how cli.sqliteDSNPath re-roots a relative `file:` DSN against the config directory. url.URL escapes the path (spaces, `#`, …) so the emitted URI round-trips through SQLite's decoder.
Types ¶
type Config ¶
type Config struct {
Version int `yaml:"version"`
Dialect string `yaml:"dialect"`
ServerVersion string `yaml:"server_version"`
Database Database `yaml:"database"`
Schema Schema `yaml:"schema"`
Targets []Target `yaml:"targets"`
Cache Cache `yaml:"cache"`
Overrides []Override `yaml:"overrides"`
Expansion Expansion `yaml:"static_expansion"`
Verification Verification `yaml:"verification"`
TreeCaps TreeCaps `yaml:"filter_tree_caps"`
Policies []Policy `yaml:"policies"`
// Dir is the directory containing sqletch.yaml; all relative paths
// resolve against it. Not part of the YAML.
Dir string `yaml:"-"`
// Path is the config file itself, so later phases can attach
// diagnostics to it (e.g. SQLETCH200). Not part of the YAML.
Path string `yaml:"-"`
// LegacyQueries and LegacyOutput decode the pre-`targets` spelling
// (one config, one output package) for ONE purpose: to answer it
// with the rewrite (SQLETCH301) instead of goccy's generic
// "unknown field". Neither is ever consumed — the presence of
// either is an error, so there is no ambiguity about which
// spelling won. See docs/design/19-multi-target-output.md §7.
LegacyQueries []string `yaml:"queries"`
LegacyOutput *Output `yaml:"output"`
}
func Load ¶
func Load(path string) (Config, []diagnostics.Diagnostic)
Load reads and validates the configuration. Config values are literal: there is deliberately no ${VAR} environment-variable expansion (removed as a secret-exfiltration / SSRF vector — a cloned repo could otherwise splice the caller's environment, including secrets, into database.dsn and point it at an attacker host). An operator who wants the dev-database DSN to come from the environment should leave database.dsn empty and let the driver's own libpq/DSN environment variables (PGHOST, MYSQL_DSN, …) take effect, or template the config outside sqletch.
func (Config) ExpandGlobs ¶
ExpandGlobs resolves config-relative globs into a sorted, duplicate- free path list; a pattern matching nothing is an error (a typoed glob silently matching zero files is the classic footgun).
Every match is path-escape-checked (SQLETCH306): the read paths (cli/commands.go, pipeline.go via os.ReadFile) consume this list unchecked, so a committed `queries: ["../../etc/*.conf"]` or a symlinked-directory glob would otherwise read arbitrary host-readable files on a clone-and-run `check`/`generate` and disclose them through catalog/scan and diagnostic excerpts. The check mirrors Load's write-path policy (lexical `..` plus symlinked-component resolution) against the project directory.
func (Config) NativeOracle ¶
NativeOracle reports whether the native-inference backend is selected.
func (Config) NullOverridesFor ¶
NullOverridesFor collects the per-column nullability overrides of one query.
func (Config) ResolveTargets ¶ added in v0.0.2
func (c Config) ResolveTargets() (Resolution, []diagnostics.Diagnostic)
ResolveTargets expands every target's patterns against the project directory. It is the single resolution seam: pipeline.Run and the LSP's OfflineChecker both call it, so the two can never disagree about which file belongs to which generated package.
A pattern matching nothing is a WARNING, not an error (design 19 §4): in a monorepo a captured directory may not exist yet, and a target with no files simply emits nothing. `schema.files` keeps the stricter rule — an empty schema silently fingerprints nothing.
type Database ¶
type Database struct {
DSN string `yaml:"dsn"`
// Oracle selects the type-oracle backend: "server" (default; a
// dev database serves cache misses) or "native" (sqletch's own
// corpus-validated inference — MySQL only, design 15). Strict by
// decision D1: no fallback, and no DSN to fall back to.
Oracle string `yaml:"oracle"`
}
type Expansion ¶
Expansion configures strict static expansion: listed queries are materialized shape-by-shape into .sql files and dispatch to precomposed SQL instead of composing at runtime.
type Output ¶
func (Output) HasCaptureRefs ¶ added in v0.0.2
HasCaptureRefs reports whether the output spells a `$n` capture reference, i.e. whether it can only be validated once patterns have been expanded against the filesystem.
type Policy ¶
type Policy struct {
Name string `yaml:"name"`
Tables []string `yaml:"tables"`
Predicate string `yaml:"predicate"`
Param PolicyParam `yaml:"param"`
// AppliesTo restricts the statement kinds the policy covers;
// empty means select, update, and delete. INSERT … VALUES is
// never a policy target (no rows are filtered).
AppliesTo []string `yaml:"applies_to"`
}
Policy declares one cross-query policy (spec §"Cross-Query Policies"): a predicate woven at compile time into every query that touches a designated table, plus the enforcement that no reachable shape goes unscoped. Shape checks that need the dialect (predicate probing, identifier rules) live in policy.Validate; Load checks only the config-level vocabulary.
type PolicyParam ¶
PolicyParam declares the policy predicate's parameter. Type is required on Tier 2 dialects (their oracles cannot type parameter slots) and asserted like a `-- @param` hint on Tier 1.
type Resolution ¶ added in v0.0.2
type Resolution struct {
// Targets are ordered by output path, and each target's Files are
// sorted: determinism is never conditional on directory order.
Targets []ResolvedTarget
// Files is the union of every target's files, sorted. It is what
// the whole-workspace commands (fmt, explain) and the LSP iterate.
Files []string
// Dirs are the directories the expansion consulted, absolute. A
// directory's mtime moves when an entry is added or removed, so
// re-stating these is a sound memo signature for the LSP (§6).
Dirs []string
}
Resolution is one expansion of `targets` against the project directory.
type ResolvedTarget ¶ added in v0.0.2
type ResolvedTarget struct {
// Package is the generated package name (captures substituted).
Package string
// Path is the output directory as written in the config (captures
// substituted): project-relative and slash-separated unless the
// user spelled an absolute path. Use Config.Abs for filesystem
// access and Slug for a derived-output namespace.
Path string
// Files are the template files, absolute and sorted.
Files []string
}
ResolvedTarget is one generated Go package: the files that feed it and where it is written. It is what `targets` resolves to once patterns are expanded and captures substituted (docs/design/19-multi-target-output.md §4).
func (ResolvedTarget) Abs ¶ added in v0.0.2
func (t ResolvedTarget) Abs(c Config) string
Abs is the target's output directory as a filesystem path.
func (ResolvedTarget) Slug ¶ added in v0.0.2
func (t ResolvedTarget) Slug() string
Slug is the target's identity inside the derived-output trees (.sqletch/explain/<slug>/…, .sqletch/expanded/<slug>/…). It mirrors the output path so the tree is readable, with the escapes a path component cannot carry folded away — an absolute or climbing path (only reachable via the absolute-output warning) must not send a derived write out of .sqletch/.
type SQLiteFileURI ¶
type SQLiteFileURI struct {
// InMemory is true for an in-memory / private-temp database — an empty
// or ":memory:" path component. It is not a filesystem path, so it is
// exempt from the escape check and passes through resolution untouched.
InMemory bool
// Abs is true when the path component is absolute. An absolute dev-DB
// path is a normal operator choice (allowed like the plain absolute
// spelling) and is never re-rooted against the config dir.
Abs bool
// Safe is false when the URI cannot be proven to stay inside the project
// — a non-local authority (`file://host/…` with host other than
// localhost) or an undecodable percent-escape. A trusted config never
// produces one; callers refuse it (SQLETCH306) rather than guess.
Safe bool
// Path is the DECODED path component: relative to the config dir when
// !Abs, absolute when Abs, empty when InMemory.
Path string
// Query is the raw query string after '?' (without the '?'), preserved
// verbatim by SQLiteFileURIString.
Query string
}
SQLiteFileURI is the classified result of parsing a SQLite `file:` DSN. A `file:` URI is a real filesystem reference (ncruces opens SQLite with OPEN_URI|OPEN_CREATE), so its path component must go through the same SQLETCH306 escape check as a plain path. Both the config validator (Load → the SQLETCH306 refusal) and the DSN resolver (cli.sqliteDSNPath) classify with this type, so the parse lives in one place and they cannot drift.
func ParseSQLiteFileURI ¶
func ParseSQLiteFileURI(dsn string) SQLiteFileURI
ParseSQLiteFileURI classifies a DSN the caller already knows begins with "file:". net/url does the heavy lifting: a relative path component lands in Opaque (kept percent-encoded), an absolute one in Path (already decoded), and any authority in Host. Only an empty or "localhost" authority is provably safe — SQLite rejects the rest — so anything else is refused conservatively (a false-reject on trusted config is a LOW annoyance; a false-accept is the vulnerability).
type Target ¶ added in v0.0.2
Target pairs a set of query-file patterns with the Go package they generate into (docs/design/19-multi-target-output.md). Patterns may carry capture groups whose text substitutes into Output, so ONE target can fan out into one package per matched directory; the grouping key is the SUBSTITUTED output, not the config entry, so several patterns landing on the same output merge into one package.
type TreeCaps ¶
TreeCaps bounds @filter-tree values at runtime; the values are baked into generated code.
type Verification ¶
type Verification struct {
MaxShapes int `yaml:"max_shapes"`
}
Verification bounds the work `check --exhaustive` will do. It is a config key rather than a flag because it decides whether a CI gate passes: a project's verification budget must be the same on every machine that runs the check, not a property of who typed the command.