Dialects
QUARK routes SQL generation through a dialect abstraction. Dialects are responsible for placeholders, identifier quoting, insert ID behavior, upsert syntax, and DDL operations.
| Dialect | Constructor | Notes |
|---|---|---|
| PostgreSQL | quark.PostgreSQL() | $N placeholders, RETURNING, ON CONFLICT. |
| MySQL | quark.MySQL() | AUTO_INCREMENT, LAST_INSERT_ID, ON DUPLICATE KEY UPDATE. |
| MariaDB | quark.MariaDB() | MySQL-compatible upsert behavior (ON DUPLICATE KEY UPDATE). Treated as a distinct dialect for DDL edge cases. |
| SQLite | quark.SQLite() | In-memory friendly, RETURNING where supported. |
| MSSQL | quark.MSSQL() | IDENTITY, OFFSET/FETCH, MERGE upserts. |
| Oracle | quark.Oracle() | GENERATED AS IDENTITY, MERGE upserts. |
Since v1.1.0, MariaDB is detected automatically: on a quark.New("mysql", dsn)
connection Quark probes SELECT VERSION() once at construction and switches to
the MariaDB dialect when the server reports MariaDB, so you get the MariaDB DDL
edge cases without picking the constructor explicitly.
Upsert mapping
user := User{Email: "alice@example.com", Name: "Alice Updated"}
err := quark.For[User](ctx, client).Upsert(
&user,
[]string{"email"},
[]string{"name", "active"},
)
| Dialect | Generated shape |
|---|---|
| PostgreSQL | ON CONFLICT (email) DO UPDATE SET ... |
| MySQL | ON DUPLICATE KEY UPDATE ... |
| MariaDB | ON DUPLICATE KEY UPDATE ... |
| SQLite | ON CONFLICT (email) DO UPDATE SET col = excluded.col |
| MSSQL | MERGE INTO ... USING ... WHEN MATCHED ... |
| Oracle | MERGE INTO ... USING ... WHEN MATCHED ... |
Oracle-specific behavior
Oracle differs from the other engines in two ways worth knowing:
- Empty strings are NULL. Oracle stores
''asNULL, so an empty string written to aVARCHAR2/CLOBcolumn comes back asNULL. When scanning into a non-pointerstringfield, QUARK coerces thatNULLto"", so empty strings round-trip consistently with the other engines. Use*stringorsql.Null[string]if you need to tellNULLapart from"". - JSON path is inlined.
WhereJSONbinds the JSON path as a parameter on every engine except Oracle, whoseJSON_VALUErejects a bound path (ORA-40454: path expression not a literal). On Oracle the path is inlined as a literal instead. It stays injection-safe because the path is validated against a strict[A-Za-z0-9_.]grammar before it reaches the SQL.
Custom dialects
Custom or proprietary engines can be registered:
myDialect := &MyCustomDialect{}
quark.RegisterDialect("customdb", myDialect)
dialect, err := quark.DetectDialectByName("customdb")
if err != nil {
return err
}
client, err := quark.New("customdb", dsn, quark.WithDialect(dialect))
DetectDialectByName looks up a registered dialect by name and returns
(Dialect, error). Use RegisterDialect once at init time.
The dialect interface covers SQL generation, placeholder formatting, identifier quoting, and schema DDL.