Skip to main content
Version: 1.2.2

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 provision concatenated the tenant id and strategy into CREATE DATABASE / CREATE SCHEMA / INSERT SQL — 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()/Paginate on a compound select counted only the base operand (List()=4, Count()=2) — now wrapped as SELECT COUNT(*) FROM (<compound>); Upsert/UpsertBatch with empty conflictCols return ErrInvalidQuery on every engine instead of panicking (MySQL/MariaDB) or diverging; Offset without Limit renders the correct per-engine sentinel instead of being dropped (MySQL) or producing invalid SQL (SQLite/MariaDB).
  • CLI stops lying: migrate up/down and seed run with an empty registry exit non-zero with the embed recipe; validate loads your Go structs with go/packages and compares columns both ways; tenant migrate resolves the tenant's DSN from tenant.dsn_template instead of migrating the default database; inspect table/model generate on a missing table exit non-zero; phantom flags (--skip-seed, --tenant-id, --env) removed; init --dialect bogus fails before writing anything.
  • Batching: UpsertBatch chunks like CreateBatch, 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's MERGE has no RETURNING — the PK stays zero there, now documented.
  • Set operations: INTERSECT/EXCEPT enabled on MariaDB (10.3+); MySQL keeps returning ErrUnsupportedFeature (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 init wrote driver: postgresql, but the registered PostgreSQL driver is pgx, so every DB command failed with unknown 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 / init failures exit non-zero (they printed the error and exited 0, silently breaking CI gates).
  • Honest sync: its --dry-run/--safe/--no-transaction/--models flags were no-ops and are gone; the command checks the connection and prints how to wire client.Sync(...) (schema sync needs your compiled model types).
  • inspect --format json|yaml implemented; quark version / --version added; inspect sql relabeled (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 optional CacheLocker store capability (implemented by the memory and redis stores). 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 ShardKeyer route with WithShardKeyOf(ctx, entity) — a caller-side helper, not a router hook.
  • Scatter-gather cross-shard reads: ScatterGather / ScatterCount with an explicit caller-side ScatterMerge; 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.

  • SQLGuard error wrapping: a rejected Where operator, a raw query missing placeholders, or a raw query matching a suspicious pattern now wrap quark.ErrInvalidQuery, so errors.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/).

  • CreateBatch now 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 via LastInsertId/SCOPE_IDENTITYentity.ID is populated everywhere instead of silently left at 0.
  • Recursive CTEs work on Oracle and SQL Server: WithRecursive no longer emits the RECURSIVE keyword 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/ Except with Limit/Offset and no explicit OrderBy now emits a positional ORDER 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/UpdateBatch fire BeforeCreate/BeforeUpdate per entity, and Upsert/UpsertBatch fire BeforeCreate (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.

  • CreateBatch now back-fills the generated primary key on Oracle: the per-row insert path uses RETURNING … INTO, so entity.ID is populated like every other engine instead of left at 0.
  • The schema diff no longer drifts MariaDB JSON columns to a cosmetic longtext → JSON alter: the introspector relabels a longtext carrying MariaDB's auto-added json_valid(col) CHECK back to json.
  • L2 cache: CreateBatch now 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/).

  • ApplyPlan now 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 by Migrate. Column gains a PrimaryKey field, 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).
  • PlanMigration returns 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 destructive DROP of 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), Oracle TIMESTAMP(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: Create now 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 a bool field now migrates on all six engines; PostgreSQL gets TRUE/FALSE).
  • ErrInvalidIdentifier is reachable via errors.Is on every validation path.
  • quark model generate --fields creates the --out directory 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.WherePWHERE 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 through quark.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 validate tags and model-level Validate(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

  • Migrate for table and many-to-many join-table creation.
  • Sync for additive changes, renames, and controlled destructive drops.
  • CreateIndex and AddForeignKey helpers.
  • Versioned Go migrations through github.com/jcsvwinston/quark/migrate.

Relations

  • Preload for has_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.
  • TenantRouter with 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/quark CLI ships and is installable via go install — it provides migration, tenant, and quark gen codegen subcommands (Operational Workflows).
  • DeleteBy and DeleteBatch perform hard deletes.
  • Select accepts simple identifiers rather than arbitrary SQL expressions.
  • Raw SQL APIs require AllowRawQueries: true.
  • Migration commands that use migrate.Migrator should use a client configured with AllowRawQueries: true.