Skip to content

Getting Started

Quickstart

Install SQLQuack, check a file and convert it to DuckDB SQL.

SQLQuack installs from PyPI with Python 3.12 or newer. The package ships the sqlquack command and the Python library, with DuckDB’s library beside them.

pip install sqlquack
sqlquack --version

To build the command line from the repository instead, use Rust. The repository pins its toolchain in rust-toolchain.toml, which rustup installs on first use.

git clone https://github.com/sqlstack-io/sqlquack
cd sqlquack
cargo build --release -p sqlquack

The binary links DuckDB’s prebuilt shared library, which the build downloads to target/duckdb-download/<target triple>/<DuckDB version>. cargo run puts it on the library path for you:

cargo run --release -p sqlquack -- check query.sql

To run target/release/sqlquack directly, point the library path at that directory:

LD_LIBRARY_PATH=target/duckdb-download/x86_64-unknown-linux-gnu/v2.0.0 target/release/sqlquack check query.sql

Use DYLD_LIBRARY_PATH on macOS and PATH on Windows.

check needs no schema. It parses the SQL and reports what Snowflake would reject.

-- orders.sql
SELECT customer_id, SUM(total) AS revenue
FROM orders
WHERE status = 'shipped'
GROUP BY customer_id
QUALIFY revenue > 100
$ sqlquack check orders.sql
error[SQ0203]: QUALIFY needs a window function
--> orders.sql:5:1
|
5 | QUALIFY revenue > 100
| ^^^^^^^^^^^^^^^^^^^^^ QUALIFY is used without a window function in the SELECT list or QUALIFY
|
= help: move the condition to WHERE or HAVING, or filter on a window function's result
sqlquack check: found 1 problem

Move the condition to HAVING SUM(total) > 100 and check reports no problems.

Conversion types every expression, so it needs the columns and Snowflake types of the relations a statement reads. Declare them in a schema file:

{
"ORDERS": [
["ORDER_ID", "NUMBER(38,0)"],
["CUSTOMER_ID", "NUMBER(38,0)"],
["STATUS", "VARCHAR"],
["TOTAL", "NUMBER(12,2)"],
["ORDERED_AT", "TIMESTAMP_NTZ"]
]
}
$ sqlquack convert revenue.sql --schema schema.json --builtin-policy
SELECT
"ORDERS"."CUSTOMER_ID" AS "CUSTOMER_ID",
CAST(sum("ORDERS"."TOTAL") AS DECIMAL(24,2)) AS "REVENUE"
FROM "ORDERS" AS "ORDERS"
WHERE ("ORDERS"."STATUS" = 'shipped')
GROUP BY "ORDERS"."CUSTOMER_ID"
sqlquack convert: done, converted under the built-in default

The DuckDB SQL goes to standard output and the progress lines to standard error, so sqlquack convert revenue.sql … > revenue.duckdb.sql keeps only the SQL.

convert reads sqlquack.toml from the current directory, or the file --policy names. Without one, or with --builtin-policy, it uses the built-in policy, which refuses every known divergence but the clock (CURRENT_DATE() and CURRENT_TIMESTAMP() convert with a warning). See Policy and sessions.