Run Snowflake SQL locally, powered by DuckDB - lightweight in-memory SQL engine for development and testing
Project description
โ๏ธ๐ฆ SnowDuck
Run Snowflake SQL locally, powered by DuckDB
SnowDuck is a lightweight, in-memory SQL engine that emulates Snowflake's behavior for development and testing. Write and test Snowflake SQL locally without cloud access or costs.
Why SnowDuck?
- ๐ Fast Development - Test SQL queries instantly without waiting for cloud connections
- ๐ฐ Zero Cloud Costs - Develop and test locally without Snowflake compute charges
- ๐งช Easy Testing - Mock Snowflake databases for unit tests and CI/CD pipelines
- โก Lightning Fast - Powered by DuckDB's in-memory execution engine
- ๐ Drop-in Compatible - Uses Snowflake's connector interface - just patch and go
Features
Core SQL Support
| Category | Functions |
|---|---|
| DDL Operations | CREATE/DROP DATABASE, SCHEMA, TABLE |
| DML Operations | INSERT, UPDATE, DELETE, MERGE |
| Advanced SQL | CTEs, JOINs, subqueries, CASE, QUALIFY |
| Session Variables | SET/SELECT $variable syntax |
| Information Schema | Query metadata (databases, tables, columns) |
Function Support
| Category | Functions |
|---|---|
| String | CONCAT, CONCAT_WS, SPLIT, SPLIT_PART, CONTAINS, REPLACE, TRIM, LTRIM, RTRIM, LPAD, RPAD, SPACE, STRTOK, TRANSLATE, REVERSE, STARTSWITH, ENDSWITH, ASCII, CHR, INITCAP, SOUNDEX, UPPER, LOWER, LENGTH, LEN, SUBSTR, SUBSTRING, INSTR, POSITION |
| Date/Time | DATEADD, DATEDIFF, TIMEDIFF, DATE_TRUNC, DATE_PART, EXTRACT, LAST_DAY, ADD_MONTHS, DATE_FROM_PARTS, TIME_FROM_PARTS, TIMESTAMP_FROM_PARTS, CONVERT_TIMEZONE, TO_DATE, TO_TIMESTAMP |
| Numeric | ABS, CEIL, FLOOR, ROUND, MOD, SQRT, POWER, EXP, LN, LOG, SIGN, DIV0, DIV0NULL, WIDTH_BUCKET, TRUNCATE, CBRT, FACTORIAL, DEGREES, RADIANS, PI, RANDOM, GREATEST, LEAST |
| Aggregate | COUNT, SUM, AVG, MIN, MAX, MEDIAN, LISTAGG, ANY_VALUE, KURTOSIS, SKEW, COVAR_POP, COVAR_SAMP |
| Window | ROW_NUMBER, RANK, DENSE_RANK, LEAD, LAG, FIRST_VALUE, LAST_VALUE |
| JSON | PARSE_JSON, OBJECT_CONSTRUCT, OBJECT_INSERT, GET_PATH, TRY_PARSE_JSON, OBJECT_KEYS, CHECK_JSON, TO_JSON |
| Array | ARRAY_CONSTRUCT, ARRAY_SIZE, ARRAY_CONTAINS, FLATTEN, ARRAY_SLICE, ARRAY_CAT, ARRAY_APPEND, ARRAY_PREPEND, ARRAY_SORT, ARRAY_REVERSE, ARRAY_MIN, ARRAY_MAX, ARRAY_SUM, ARRAYS_OVERLAP, ARRAY_DISTINCT, ARRAY_INTERSECTION, ARRAY_EXCEPT |
| Conditional | NVL, NVL2, DECODE, IFF, COALESCE, NULLIF, EQUAL_NULL, ZEROIFNULL, NULLIFZERO |
| Conversion | TO_CHAR, TO_NUMBER, TO_BOOLEAN, TO_DATE, TRY_CAST, TRY_TO_NUMBER, TRY_TO_DATE, TRY_TO_TIMESTAMP, TRY_TO_BOOLEAN |
| Regex | REGEXP_LIKE, REGEXP_SUBSTR, REGEXP_REPLACE, REGEXP_COUNT |
| Hash | MD5, SHA1, SHA2, SHA256, HASH |
| Encoding | BASE64_ENCODE, BASE64_DECODE_STRING, HEX_ENCODE, HEX_DECODE_STRING |
| Bitwise | BITAND, BITOR, BITXOR, BITNOT, BITAND_AGG, BITOR_AGG, BITXOR_AGG |
| Boolean Agg | BOOLAND_AGG, BOOLOR_AGG |
| Utility | UUID_STRING, TYPEOF |
Cursor Methods
SnowDuck supports all standard Snowflake cursor methods:
execute()- Execute SQL statementsfetchone()- Fetch a single rowfetchmany(size)- Fetch multiple rowsfetchall()- Fetch all rowsfetch_pandas_all()- Fetch all rows as pandas DataFramefetch_pandas_batches()- Fetch rows as iterator of DataFramesget_result_batches()- Get Arrow record batchesdescribe()- Get result schema without execution
Note: SnowDuck is designed for development and testing. Use production Snowflake for production workloads.
Quick Start
Installation
# Using uv (recommended)
uv pip install snowduck
# Or using pip
pip install snowduck
Basic Usage
import snowflake.connector
from snowduck import start_patch_snowflake
# Patch the Snowflake connector to use DuckDB
start_patch_snowflake()
# Use Snowflake connector as normal - it's now backed by DuckDB!
with snowflake.connector.connect() as conn:
cursor = conn.cursor()
cursor.execute("CREATE DATABASE my_database")
cursor.execute("USE DATABASE my_database")
cursor.execute("""
CREATE TABLE employees (id INTEGER, name VARCHAR, salary INTEGER)
""")
cursor.execute("""
INSERT INTO employees VALUES
(1, 'Alice', 95000),
(2, 'Bob', 75000),
(3, 'Carol', 105000)
""")
cursor.execute("""
SELECT name, salary, RANK() OVER (ORDER BY salary DESC) as rank
FROM employees
""")
for row in cursor.fetchall():
print(f"{row[0]}: \${row[1]:,} (Rank: {row[2]})")
Data Persistence
# In-memory (default) - fast, isolated
start_patch_snowflake()
# File-based - persistent across restarts
start_patch_snowflake(db_file='my_data.duckdb')
# Fresh start - reset existing data
start_patch_snowflake(db_file='my_data.duckdb', reset=True)
Test Data Seeding
from snowduck import seed_table
with snowflake.connector.connect() as conn:
# From dict
seed_table(conn, 'customers', {
'id': [1, 2, 3],
'name': ['Acme', 'TechStart', 'DataCo']
})
# From pandas DataFrame
seed_table(conn, 'orders', df)
Testing
Using the Decorator
from snowduck import mock_snowflake
@mock_snowflake
def test_query():
conn = snowflake.connector.connect()
cursor = conn.cursor()
cursor.execute("SELECT 1")
assert cursor.fetchone()[0] == 1
Using the Context Manager
from snowduck import patch_snowflake
def test_with_fixture():
with patch_snowflake():
conn = snowflake.connector.connect()
# Test code here
pytest Fixture
import pytest
from snowduck import patch_snowflake
@pytest.fixture
def conn():
with patch_snowflake():
yield snowflake.connector.connect()
def test_feature(conn):
cursor = conn.cursor()
cursor.execute("SELECT 1")
REST API Server
# Install with server extras
uv pip install snowduck[server]
# Start the server
uvicorn snowduck.server:app --reload
The server provides:
- Execute SQL queries via REST API
- Arrow IPC format responses
- Multi-session support
Architecture
โโโโโโโโโโโโโโโโโโโโโโโ
โ Your Application โ
โ (Snowflake code) โ
โโโโโโโโโโโโฌโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโ
โ SnowDuck Patch โ โ Intercepts connector calls
โโโโโโโโโโโโฌโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโ
โ SQL Translator โ โ Snowflake โ DuckDB dialect
โโโโโโโโโโโโฌโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโ
โ DuckDB Engine โ โ Fast in-memory execution
โโโโโโโโโโโโโโโโโโโโโโโ
Examples
See the examples/ directory for Jupyter notebooks demonstrating:
- Basic operations and queries
- String, date, and numeric functions
- JSON and array operations
- Window functions
- Advanced SQL patterns
Development
git clone https://github.com/hupe1980/snowduck.git
cd snowduck
uv sync
just test
just check
Contributing
Contributions welcome! See issues for areas where help is needed.
License
MIT License - see LICENSE for details.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file snowduck-0.0.5.tar.gz.
File metadata
- Download URL: snowduck-0.0.5.tar.gz
- Upload date:
- Size: 293.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b84d7b11f55e12263e3cfbe2ba78e31bd950e0aa538373216de98eb6bcc41b9c
|
|
| MD5 |
6cb613b39d365e55be0824513de8ccae
|
|
| BLAKE2b-256 |
6a55accac2536a477a75f3655f2998db30fb8f459f5e331520f45e79d5a3d03b
|
Provenance
The following attestation bundles were made for snowduck-0.0.5.tar.gz:
Publisher:
release.yml on hupe1980/snowduck
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
snowduck-0.0.5.tar.gz -
Subject digest:
b84d7b11f55e12263e3cfbe2ba78e31bd950e0aa538373216de98eb6bcc41b9c - Sigstore transparency entry: 971100678
- Sigstore integration time:
-
Permalink:
hupe1980/snowduck@3435da074f30071c4de4bbe51388a5a40901aaa2 -
Branch / Tag:
refs/tags/v0.0.5 - Owner: https://github.com/hupe1980
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@3435da074f30071c4de4bbe51388a5a40901aaa2 -
Trigger Event:
push
-
Statement type:
File details
Details for the file snowduck-0.0.5-py3-none-any.whl.
File metadata
- Download URL: snowduck-0.0.5-py3-none-any.whl
- Upload date:
- Size: 82.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
350170d7ce8bc2025648a9a3c8f3aebb8c6242298f2ade55f33080ec1c279516
|
|
| MD5 |
af5e88936aea7fa503a1627e75839310
|
|
| BLAKE2b-256 |
94facb26de0c7885e063347081a8af926cdebaf3d0a25e867ca95ff20018e392
|
Provenance
The following attestation bundles were made for snowduck-0.0.5-py3-none-any.whl:
Publisher:
release.yml on hupe1980/snowduck
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
snowduck-0.0.5-py3-none-any.whl -
Subject digest:
350170d7ce8bc2025648a9a3c8f3aebb8c6242298f2ade55f33080ec1c279516 - Sigstore transparency entry: 971101193
- Sigstore integration time:
-
Permalink:
hupe1980/snowduck@3435da074f30071c4de4bbe51388a5a40901aaa2 -
Branch / Tag:
refs/tags/v0.0.5 - Owner: https://github.com/hupe1980
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@3435da074f30071c4de4bbe51388a5a40901aaa2 -
Trigger Event:
push
-
Statement type: