This file collects design + implementation tasks that are deliberately out of scope for the current milestone. Each item has enough context to pick up cold; none of them block the pre-1.0 cleanup.
gerpo's public messaging suggests "any SQL backend behind an executor.Adapter",
but the bundled adapters and the SQL fragments we emit are silently
PostgreSQL-shaped. Several places need work before another major dialect can be
treated as a first-class target.
-
LIKE / CONCAT type cast (
sqlstmt/sqlpart/where.go): we emitCAST(? AS text)insideCONCAT(...). The accompanying comment claims this is portable to MySQL, but MySQL has notexttype forCAST— the standard there isCAST(? AS CHAR). Need to:- Add a dialect-aware text-cast helper (e.g.
dialect.TextCast()). - Resolve it through the adapter capability (see below).
- Add MySQL integration tests covering every LIKE-family operator.
- Add a dialect-aware text-cast helper (e.g.
-
COUNT(*) OVER ()window function (sqlstmt/count.go): supported in PG 9.2+, MySQL 8.0+, SQL Server 2005+, SQLite 3.25+. Should work everywhere recent enough, but worth verifying on a real MySQL box. -
Boolean literals: PG accepts
TRUE/FALSE; MySQL prefers1/0for performance and SQL Server requires1/0. Currently we rely on driver-side parameter binding so this is implicit, but custom virtual-column filters may trip on it.
-
MySQL has no
RETURNING(MariaDB 10.5+ does). Fallback options considered and deferred:LastInsertId()fromsql.Result— covers only single auto-increment PK inserts; doesn't help with UUID DEFAULT,created_attriggers, or UPDATE.- Read-back via a separate
SELECTkeyed on PK — needs gerpo to know which column is the PK, doesn't help when PK is also DB-generated. - User-supplied read-back via
WithAfterInserthook (now that hooks return error) — this is the recommended workaround today.
When MySQL support lands, the cleanest path is probably the hook-based read-back wired into a stable adapter capability so
Build()does not refuse to build a repo that usesReturnedOnInsert/Update. -
SQL Server uses
OUTPUTclause, notRETURNING. Need a dialect helper that emits the right syntax (OUTPUT INSERTED.colfor INSERT, slightly different for UPDATE).
- Currently handled — pgx adapters use
placeholder.Dollar, database/sql is configurable. SQL Server uses@p1/@p2style — would need a newplaceholder.AtNameformat.
- Today the only adapter capability is
ReturningCapable(added with the RETURNING feature). Cleaner long-term shape: a singleDialect()method onAdapterreturning aDialectvalue with named capabilities + SQL helpers. Defer until we actually have a non-PG dialect to support.
- We have pgx5, pgx4, database/sql. When MySQL/MariaDB/SQL Server support lands, the adapter README/index should mark which dialects each adapter actually targets in production.
tests/integration/only has Postgres infra (docker-compose, schema). Needs:- Parallel Docker compose stack for MySQL / MariaDB / SQL Server.
forEachAdapterloop extended to spin per-dialect repositories.- Dialect-specific test paths skipped when the adapter under test does not
support the feature (e.g. a
t.Skip(...)matrix per dialect capability).
docs/features/adapters.mdanddocs/architecture/adapters-internals.mdneed a "Supported dialects" matrix once we land more than PG.- README's "Supported drivers" section should be split into "drivers" vs "dialects" — the underlying confusion is exactly the one we're trying to resolve in code.
InsertMany ships as a multi-row INSERT ... VALUES (...), (...) with
executor-level chunking at PG's 65535-placeholder limit. A few follow-ups worth
considering when we see real workloads pushing the path hard:
-
PostgreSQL
COPY FROM. For multi-tens-of-thousands-row importsCOPYis materially faster than multi-row VALUES. pgx exposesCopyFromdirectly. ComplicatesRETURNING:COPY FROMin pgx returns affected count only, not per-row generated values. Would need either a capability flag (AdapterSupportsCopy) toggling the path, or a separateBulkCopymethod that explicitly disclaimsRETURNING. -
ON CONFLICT / UPSERT. Currently out of scope (see review item 3.1 — skipped). If/when UPSERT lands,
InsertManyshould accept the same conflict spec so bulk-upserts are one SQL statement. -
Per-row overrides. Today
Exclude/Only/Returningapply uniformly to every row in the batch. Per-row shaping would double the generated-SQL complexity without a clear user win — defer until a real use case shows up.
gerpo does not wrap SAVEPOINT / ROLLBACK TO SAVEPOINT / RELEASE SAVEPOINT
today. Users who need nested rollbacks fall back to raw SQL on the tx:
_, _ = tx.ExecContext(ctx, "SAVEPOINT sp")
_, _ = tx.ExecContext(ctx, "ROLLBACK TO SAVEPOINT sp")That works but is fiddly — naming, RELEASE on success, error handling and nesting must be hand-rolled every time.
Design questions to settle before shipping:
- Shape.
tx.Savepoint(name)returning aSavepointvalue withCommit()/Rollback()— symmetric toTx. Alternatively agerpo.RunInSavepoint(ctx, name, fn)mirror ofRunInTx, which handles RELEASE/ROLLBACK from the returned error.RunInSavepointis probably the 90% case. - Naming. Auto-generate unique names (counter inside the
Txwrapper) or require the caller to pass one. Auto is friendlier; explicit names help when reading logs. - Dialect coverage. PostgreSQL / SQLite / MySQL 8+ / MS SQL Server all
support
SAVEPOINT, butRELEASE SAVEPOINTsemantics differ slightly on MSSQL (whereRELEASEdoes not exist — savepoints are auto-released on COMMIT). Since gerpo is PG-only today this is deferred to the multi-dialect work above. - Nesting. Allow
Savepointon aSavepoint? Trivial to support since PostgreSQL handles it natively, but the state machine inexecutor/adapters/internal/base.gois currently two levels deep (Adapter → transaction) — would need a third.
Deferred until someone asks. If you have a use case, please open an issue describing it so the API is shaped around real requirements rather than speculation.