A Snowflake account, on localhost.

The official connectors and an unmodified dbt-snowflake point at 127.0.0.1 and do not know the difference. SQL runs on DuckDB. Anything this cannot do is refused by name — never quietly ignored.

v0.3.2 DuckDB 1.2.2 multi-arch image Apache-2.0

Refusal is the feature

An emulator that answers ok to something it did not do is worse than one that answers nothing. Eighteen constructs here once did exactly that — a status of ok, no rows, no error, and nothing to tell you which.

So the rule is that a statement a real account accepts is either answered or refused by name, with the reason. A gap you can read is a gap you can plan around.

85answered
7refused, by name
92statements measured

Every one of those rows is run against the image this repository builds, and the table is regenerated in CI — a job fails if the published parity page disagrees with what the emulator actually answers. It is measured, not written.

What it will not do

MERGEOBJECT_CONSTRUCTREMOVETime TravelCLONEGRANT / rolesStored procedures

Some are next; some never will be. GRANT is refused on purpose — an access model that is accepted and not enforced is worse than one that is absent, because you would test against it. Time Travel and need a versioned store the engine does not have. The roadmap says which is which, and why.

Start

make doctor                             # Go, duckdb, docker on PATH
make build
SNOWFLAKE_DUCKDB_PATH=:memory: make run

Then point a connector at it, or run dbt against it unchanged. The quickstart takes it from here.

The result of every query names its engine: dialect: duckdb. This is not a claim of Snowflake SQL compatibility and never says otherwise.