Release Notes
Quark is v1.2.2 on the stable v1.x line (v1.1.0 was the hardening
minor; v1.2.0 adds the scaling follow-ups). v1.x keeps API compatibility;
breaking changes go to v2.x.
Narrative release notes for each minor live in the main repository under
docs/RELEASE_NOTES_*.md
— the current line is documented at
docs/RELEASE_NOTES_v1.2.0.md.
Patch releases are recorded in the
CHANGELOG and
the GitHub Releases, with a
short summary below. Migration steps for the most recent breaking changes are
at
docs/MIGRATION_v0.9.0.md;
v0.10 through v1.2.x introduced no breaking changes.
v1.2.2
Patch release: the v1.2.1 audit backlog executed — correctness and security this time, not just the CLI.
- Security:
quark tenant provisionconcatenated the tenant id and strategy intoCREATE DATABASE/CREATE SCHEMA/INSERTSQL — the id is now validated against the TenantRouter contract before any SQL, DDL identifiers are dialect-quoted, and the registry INSERT is parameterized. - Correctness:
Count()/Paginateon a compound select counted only the base operand (List()=4,Count()=2) — now wrapped asSELECT COUNT(*) FROM (<compound>);Upsert/UpsertBatchwith emptyconflictColsreturnErrInvalidQueryon every engine instead of panicking (MySQL/MariaDB) or diverging;OffsetwithoutLimitrenders the correct per-engine sentinel instead of being dropped (MySQL) or producing invalid SQL (SQLite/MariaDB). - CLI stops lying:
migrate up/downandseed runwith an empty registry exit non-zero with the embed recipe;validateloads your Go structs withgo/packagesand compares columns both ways;tenant migrateresolves the tenant's DSN fromtenant.dsn_templateinstead of migrating the default database;inspect table/model generateon a missing table exit non-zero; phantom flags (--skip-seed,--tenant-id,--env) removed;init --dialect bogusfails before writing anything. - Batching:
UpsertBatchchunks likeCreateBatch, and both use per-dialect bind-parameter ceilings (~65k PG/MySQL/MariaDB, ~32k SQLite, ~2100 MSSQL) instead of a universal 2000. - Upsert PK back-fill: SQL Server reads the generated key back via
MERGE … OUTPUT INSERTED.<pk>(both branches). Oracle'sMERGEhas noRETURNING— the PK stays zero there, now documented. - Set operations:
INTERSECT/EXCEPTenabled on MariaDB (10.3+); MySQL keeps returningErrUnsupportedFeature(it gained them in 8.0.31, which Quark cannot assume without a version probe). - Deferred with a design sketch: opt-in strict mode for unbounded
Iter()and N+1 detection (#247).
v1.2.1
Patch release: the 2026-07 audit findings, all in the CLI, docs and the raw-query guard — no query-builder or CRUD changes.
- CLI works out of the box:
quark initwrotedriver: postgresql, but the registered PostgreSQL driver ispgx, so every DB command failed withunknown driver(the Oracle driver was not even linked into the binary). The CLI now maps dialect names to registered drivers and links go-ora. - Real exit codes:
migrate/inspect/tenant/seed/initfailures exit non-zero (they printed the error and exited 0, silently breaking CI gates). - Honest
sync: its--dry-run/--safe/--no-transaction/--modelsflags were no-ops and are gone; the command checks the connection and prints how to wireclient.Sync(...)(schema sync needs your compiled model types). inspect --format json|yamlimplemented;quark version/--versionadded;inspect sqlrelabeled (it reconstructs DDL from a live table).- Hardening: dialect
Quote()doubles embedded closing quotes (defense in depth under the identifier allowlist), and the raw-query guard no longer rejects--inside single-quoted string literals ('range--max') while still rejecting it outside.
v1.2.0
Minor release: the scaling follow-ups deferred at v1.1.
- Cross-instance cache-stampede coordination: opt-in
WithCacheCrossInstance()+ the optionalCacheLockerstore capability (implemented by thememoryandredisstores). The lock winner recomputes and writes; losers wait-and-reread. A lock-backend failure degrades to the previous in-process behavior, never to an error. - Shard key from the entity: entities implementing
ShardKeyerroute withWithShardKeyOf(ctx, entity)— a caller-side helper, not a router hook. - Scatter-gather cross-shard reads:
ScatterGather/ScatterCountwith an explicit caller-sideScatterMerge; COUNT is the only aggregate merged for you. - Security: toolchain pinned to go1.26.5 and pgx to v5.9.2 (upstream security fixes accumulated against the v1.1.5 pins).
- Fixes/perf: the Update zero-value warning no longer fires on nil pointers; the read path memoizes the scan plan and pre-sizes result slices.
No breaking changes.
v1.1.5
Patch release.
SQLGuarderror wrapping: a rejectedWhereoperator, a raw query missing placeholders, or a raw query matching a suspicious pattern now wrapquark.ErrInvalidQuery, soerrors.Is(err, quark.ErrInvalidQuery)matches them (they previously returned a message the sentinel did not wrap). No SQL or behavior change beyond the error value.
This release also lands the v1.1.4 docs↔code certification: the published docs
(Query Builder joins, the Dialect interface, SQLGuard's lexical validation,
the quark CLI subcommands, the PostgreSQL CreateListener, batch hooks, and
the composite-PK cache tag) were corrected to match the shipped API. No
breaking changes.
v1.1.4
Patch release: five cross-engine correctness fixes surfaced by the acceptance
harness (examples/superapp/).
CreateBatchnow back-fills generated primary keys on MySQL and SQL Server (it already did on the RETURNING dialects and Oracle). Those engines can't read keys back from a multi-row insert, so when the PK is auto-generated they insert per row and recover each viaLastInsertId/SCOPE_IDENTITY—entity.IDis populated everywhere instead of silently left at 0.- Recursive CTEs work on Oracle and SQL Server:
WithRecursiveno longer emits theRECURSIVEkeyword there (both infer recursion structurally and reject it), which previously failed with ORA-02000 / a T-SQL syntax error. - Set-op pagination works on Oracle and SQL Server: a
Union/Intersect/ExceptwithLimit/Offsetand no explicitOrderBynow emits a positionalORDER BY 1(a select-list ordinal, valid under a compound-select) instead of the primary key, which the engines rejected. - Batch and upsert lifecycle hooks:
CreateBatch/UpdateBatchfireBeforeCreate/BeforeUpdateper entity, andUpsert/UpsertBatchfireBeforeCreate(insert-prep), so timestamp/default/derived-field hooks apply to batched and upserted rows.After*hooks remain single-write only.
No breaking changes.
v1.1.3
Patch release: three cross-engine correctness fixes surfaced by the acceptance harness.
CreateBatchnow back-fills the generated primary key on Oracle: the per-row insert path usesRETURNING … INTO, soentity.IDis populated like every other engine instead of left at 0.- The schema diff no longer drifts MariaDB JSON columns to a cosmetic
longtext → JSONalter: the introspector relabels alongtextcarrying MariaDB's auto-addedjson_valid(col)CHECK back tojson. - L2 cache:
CreateBatchnow invalidates the table tag on the RETURNING dialects (PostgreSQL/SQLite/MariaDB), so a cached table-level read no longer serves stale rows after a batch insert (the batch counterpart of the v1.1.1 single-insert fix).
No breaking changes.
v1.1.2
Patch release: two schema-as-code correctness fixes surfaced by the
cross-engine acceptance harness (examples/superapp/).
ApplyPlannow renders the primary key when creating a table from a plan — auto-increment for a single integer PK, a table-level constraint for composite keys — so a plan-created table is functionally interchangeable with one created byMigrate.Columngains aPrimaryKeyfield, populated by introspection on all six engines and compared by the diff (a PK change is reported but refused by the executor: it needs a table rebuild).PlanMigrationreturns an empty plan on a freshly migrated database, as documented: m2m join tables are now part of the desired schema (the diff no longer proposes a destructiveDROPof a table Quark itself created), and catalog-decorated defaults ('x'::text,((1)), unquoted MySQL strings, bool-literal case) and type aliases (timestamp without time zone,tinyint(1), OracleTIMESTAMP(6)) no longer produce cosmetic alter ops.
No breaking changes.
v1.1.1
Patch release: four correctness fixes, three of them surfaced by the acceptance harness.
- L2 cache:
Createnow invalidates the model table tag on every insert path — on PostgreSQL/SQLite/MariaDB (RETURNING) and SQL Server (OUTPUT/SCOPE_IDENTITY), a cached table-level read no longer serves stale results after an insert. - The migrator normalizes boolean column defaults per dialect
(
default:"1"on aboolfield now migrates on all six engines; PostgreSQL getsTRUE/FALSE). ErrInvalidIdentifieris reachable viaerrors.Ison every validation path.quark model generate --fieldscreates the--outdirectory and reports failures with a non-zero exit.
No breaking changes.
v1.1.0
Hardening release: a systematic cross-engine pass over the whole public surface
plus the correctness fixes it surfaced — versioned migrations on SQL Server, a
MariaDB schema-diff false positive, CreateBatch bind-parameter chunking,
dialect-aware savepoints, multi-tenant write routing, and three eager-loading
fixes. No new public API and no breaking changes. The release-candidate soak
ran clean on the four production CI engines.
See RELEASE_NOTES_v1.1.0.md.
v1.0.0
The first stable release. v1.0 closes a qualitative
release checklist — most
notably Oracle joining the blocking CI matrix, which completes cross-engine
coverage across all six dialects. No new public API beyond the v0.13 surface and
no breaking changes since v0.9.0; v1.0 is the SemVer commitment to that
surface. The benchmark harness gained the code-generation tier (ent and sqlc),
confirming codegen is a type-safety feature rather than a speedup (the earlier
≥3× performance gate was retired). Known limitations consciously deferred to
v1.1+ are listed in the release notes.
See RELEASE_NOTES_v1.0.0.md.
v0.13.0
High-availability cut: opt-in read replicas. WithReplicas(dsns...) opens
read-only pools and routes multi-row reads across them round-robin while writes
stay on the primary; Sticky(ctx) pins a read to the primary for
read-your-writes. A read to a replica that hits a transient connection error
fails over to the primary, and the replica is taken out of rotation for a
cooldown (WithReplicaDownCooldown, default 5s). Performance: the copy-on-write
query-builder clone now shares slices and appends within bounded capacity,
trimming the per-query allocation overhead. Tooling: a runnable stress/load
harness in benchmarks/stress (latency percentiles, throughput, pool
contention). Reads inside Client.Tx and under RowLevelSecurityNative always
use the primary. No breaking changes.
See RELEASE_NOTES_v0.13.0.md.
v0.12.0
Opt-in compile-time column type-safety on top of the code generator.
quark gen emits, per model, a <Model>Columns value of typed column
handles, and the query builder gains Query.WhereP — WHERE conditions
without magic column strings, with compile-time checking of both the column
and the bound value. Pure compile-time sugar; the string Where(...) API
stays valid and interchangeable. Performance: the audit row diff is now
built only when an audit sink is configured (~9% allocation drop on
InsertOne). No breaking changes.
See RELEASE_NOTES_v0.12.0.md.
v0.11.0
First cut of opt-in code generation. A new quark gen subcommand of
cmd/quark parses your model package (go/packages + go/types) and
emits a quark_gen.go per package that registers typed implementations
into a runtime registry — typed scanners on the read path and a typed
INSERT binder for single-integer-PK models. The reflection path stays the
permanent default; codegen is fully opt-in.
Comes with a reproducible benchmarks/ module — Quark vs hand-written
database/sql vs GORM — and a calibrated profiling finding:
codegen recovers ~2–5% on scan and ~1% on
INSERT. Quark's per-op CPU is dominated by database/sql and the engine,
not reflection. Generate for correctness and forward compatibility, not
for speed. No breaking changes.
See RELEASE_NOTES_v0.11.0.md.
v0.10.0
Correctness and resilience: JSON[T] / Array[T] now round-trip on
SQL Server (a []byte→VARBINARY→NVARCHAR conversion corrupted them);
rolling back to a savepoint discards the After*/OnCommit/OnRollback
hooks queued in that scope, so undone work no longer fires its
side-effects on the outer commit; a real cross-engine deadlock-retry
integration test (PG/MySQL/MariaDB) backs WithDeadlockRetry; raw SQL
under RowLevelSecurityNative emits a quark.tenant.raw_under_native_rls
warning. No breaking changes.
See RELEASE_NOTES_v0.10.0.md.
v0.9.0
PostgreSQL engine-enforced multi-tenancy
(RowLevelSecurityNative + the quarktenant policy CLI),
transactional After* hooks that fire post-commit plus
BeforeFind/AfterFind, Tx.OnCommit/Tx.OnRollback +
quark.TxFromContext, a real EventBus (Client.UseEventBus), and an
optional audit log (Client.EnableAuditLog) written atomically with
each write. Two breaking-minor changes — see
MIGRATION_v0.9.0.md.
v0.4.0 – v0.8.0
Composable query builder (typed AST, subqueries, CTEs, window
functions, set operators, pessimistic locking); schema-as-code
migrations (introspection, pure-Go diff, transactional/resumable
apply, quarkmigrate CLI, backfill); observability and L2 cache
(OTel metrics, span redaction, slow-query log, stampede protection,
deadlock retry); per-column timezones and Array[T]. Per-version
notes are in-repo under docs/RELEASE_NOTES_v0.{4,5,6,7,8}.0.md.
v0.3.0
First proper tag since v0.1.1. Bundles the early security and correctness fixes with rich types, dirty tracking, optimistic locking, and soft-delete scopes. See the in-repo release notes for the full breakdown.
v0.1.0
Public release baseline.
Core
- Generic
Query[T]entry point throughquark.For[T](ctx, provider). - Immutable builder methods for predictable query composition.
- CRUD helpers for create, update, delete, hard delete, upsert, and map updates.
- Read helpers for
Find,First,List,Count, aggregates, pagination, streaming, and cursors. - Validation through
validatetags and model-levelValidate(context.Context) error. - Lifecycle hooks for create, update, and delete paths.
Dialects
- SQLite, PostgreSQL, MySQL, MariaDB, SQL Server, and Oracle dialects.
- Dialect-specific placeholders, identifier quoting, pagination, upsert SQL, generated-ID handling, JSON extraction, and DDL helpers.
- Custom dialect registration through
RegisterDialect.
Schema
Migratefor table and many-to-many join-table creation.Syncfor additive changes, renames, and controlled destructive drops.CreateIndexandAddForeignKeyhelpers.- Versioned Go migrations through
github.com/jcsvwinston/quark/migrate.
Relations
Preloadforhas_one,has_many,belongs_to, many-to-many, and polymorphic relations.- Recursive association persistence for common aggregate saves.
- Tenant context propagation through association loads and saves where tenant columns are present.
Production Features
- Transactions, manual transactions, savepoints, and nested savepoint-style callbacks.
TenantRouterwith database-per-tenant, schema-per-tenant, and row-level strategies.- Cache abstraction with memory and Redis stores.
- Query observers, middleware, and OpenTelemetry middleware.
- SQLGuard validation for identifiers, operators, and raw-query escape hatches.
Compatibility Notes
- The
cmd/quarkCLI ships and is installable viago install— it provides migration, tenant, andquark gencodegen subcommands (Operational Workflows). DeleteByandDeleteBatchperform hard deletes.Selectaccepts simple identifiers rather than arbitrary SQL expressions.- Raw SQL APIs require
AllowRawQueries: true. - Migration commands that use
migrate.Migratorshould use a client configured withAllowRawQueries: true.