Skip to content

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.json
error[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 mode

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:HASH
sqlquack 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.

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, or utc-qiic-v1 for QUOTED_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_QUERY and CLIENT_SESSION_KEEP_ALIVE.
  • DATABASE and SCHEMA set the current database and schema, which complete relation names written without them. See Schema files.
  • TIMEZONE = UTC, or no TIMEZONE, leaves time-zone-dependent statements to the session_timezone_utc assumption. Any other TIMEZONE refuses them, even with --utc-session.