Concepts
Policy and sessions
Choose which known divergences conversion may rely on, and under which Snowflake session.
Some conversions match Snowflake except for a documented difference, called a known divergence.
HASH is one: DuckDB computes its own 64-bit hash, consistent within one run but never
Snowflake’s value. A policy file lists every known divergence and whether conversion may rely on
it. Anything relying on a refused divergence is refused with
SQ0008.
$ sqlquack convert due.sql --schema schema.jsonerror[SQ0008]: the conversion needs a known divergence the policy refuses: `fn:HASH` --> due.sql:2:8 |2 | HASH(customer_id) AS bucket | ^^^^^^^^^^^^^^^^^ fn:HASH needs known divergence `hash`, which the policy refuses in this modeChoosing a policy
Section titled “Choosing a policy”convert, function, census and index read sqlquack.toml from the current directory, or the
file --policy names. Without one they use the built-in policy, which refuses every known
divergence but the clock: CURRENT_DATE() and CURRENT_TIMESTAMP() convert with a warning, because
both engines take the value when the query runs, in the session’s time zone, which must be UTC in
both for the values to agree. --strict and --loud-divergences-only refuse them too.
--builtin-policy chooses the built-in policy even where a sqlquack.toml exists. A --policy file
that does not exist, or a policy file that is not valid, is an error.
A policy file is complete on its own: the comparator, session profiles and edge-case tables are
required, and a divergence the file leaves out is refused. A file holding only one
[divergences.<id>] table is rejected. Start from a copy of the repository’s
sqlquack.toml in the directory
you run sqlquack from. To allow a divergence, change default in its existing table from
"refuse" to "warn":
[divergences.hash]constructs = ["fn:HASH"]default = "warn"strict = "refuse"A conversion that relies on an allowed divergence names it on standard error:
sqlquack convert: relies on known divergence hash at fn:HASHsqlquack convert: done, converted under utc-v1| Flag | Effect |
|---|---|
--policy <file> |
The policy file. Default sqlquack.toml in the current directory, or the built-in policy without one. |
--builtin-policy |
Use the built-in policy even where a sqlquack.toml exists: every known divergence but the clock is refused. |
--strict |
Refuse every divergence the policy refuses in strict mode. |
--utc-session |
Confirm both sessions run in UTC, so strict mode relies on session_timezone_utc. |
--confirm-declared-offsets |
Confirm the TIMESTAMP_TZ offsets the schema declares. |
--python-runtime |
Confirm a Python runtime registers the project’s Python functions. |
--loud-divergences-only |
Rely only on divergences whose converted SQL raises rather than returns another value. |
Sessions
Section titled “Sessions”Converted SQL is proven only where both sessions run in UTC: Snowflake’s TIMEZONE and DuckDB’s
TimeZone must be 'UTC'. Statements whose result depends on the session time zone carry the
session_timezone_utc assumption, and strict mode refuses them unless you pass --utc-session.
--session NAME=VALUE passes your Snowflake session parameters, one flag each:
- Conversion runs under the recorded session profile they match:
utc-v1, orutc-qiic-v1forQUOTED_IDENTIFIERS_IGNORE_CASE = true. A parameter left unset takes Snowflake’s documented default. - Parameters that match no profile are refused by name with
SQ0007, so a misspelt name never falls back to defaults. - These are accepted and ignored, because they cannot change a result:
QUERY_TAG,USE_CACHED_RESULT,STATEMENT_TIMEOUT_IN_SECONDS,STATEMENT_QUEUED_TIMEOUT_IN_SECONDS,ABORT_DETACHED_QUERYandCLIENT_SESSION_KEEP_ALIVE. DATABASEandSCHEMAset the current database and schema, which complete relation names written without them. See Schema files.TIMEZONE = UTC, or noTIMEZONE, leaves time-zone-dependent statements to thesession_timezone_utcassumption. Any otherTIMEZONErefuses them, even with--utc-session.