Snowflake target
DBConvert Streams writes into Snowflake as a target, and the stream's mode decides how.
In Load mode rows never travel one by one: they are staged locally as Parquet, uploaded to your Snowflake stage, and loaded with a single COPY INTO per table.
In CDC mode changes stream in continuously through Snowpipe Streaming, which is serverless — no warehouse runs to receive them. That path has its own page: CDC into Snowflake. Everything else here — the connection, its authentication and the policies — applies to both modes.
Load mode:
Snowflake is not supported as a source.
Prerequisites
- Network reachability to your Snowflake endpoint
- A user with rights to create or write the target database, schema and tables
- A warehouse the user may run
- A Snowflake user stage (
@~) is used for file uploads
Connection settings
| Field | Required | Description |
|---|---|---|
account | Yes | Account identifier — the part before .snowflakecomputing.com in your account URL |
username | Yes | Snowflake username |
| Authentication | Yes | Key pair or password — see below |
database | No | Default database, used for connection testing and as a UI default |
warehouse | No | Warehouse to run on. Left empty, COMPUTE_WH is used — this is a fixed default, not your user's default warehouse |
role | No | Role to assume. Left empty, your user's default role applies |
The connection carries no schema: a stream names its own target database and schema, and does not inherit either from the connection.
Authentication
Snowflake is phasing out password authentication for service users during 2026. Key-pair authentication is the recommended choice for new connections, and existing password connections should be migrated before your account's enforcement date.
Key pair (recommended)
An RSA key pair does not expire and is the method Snowflake intends automation to use. DBConvert Streams keeps the private half; Snowflake only ever receives the public half.
You can bring your own key — PKCS#8 or PKCS#1, encrypted or not — or let Streams generate one:
- Choose Key pair, fill in Account and Username.
- Click Generate key pair. The private key appears in the form and stays with Streams.
- Copy the
ALTER USER … SET RSA_PUBLIC_KEY = …statement shown below the button and run it once in Snowsight, as a role that may alter the user. - Click Test Connection, then save.
If your key is encrypted, enter its passphrase in the field that appears; it is used to open the key locally and is never sent to Snowflake.
Password
Still supported, and still the quicker option for a throwaway connection. It will stop working for service users on your account's enforcement date — Snowflake shows that date in Snowsight, and lets you extend it once while you migrate.
Schema and write policies
A Snowflake target follows the same policies as MySQL and PostgreSQL. They are decided before any data moves: a policy that refuses stops the stream with nothing staged and nothing uploaded.
| Policy | Values |
|---|---|
schemaPolicy | fail_if_exists (default), validate_existing, create_missing_only, drop_and_recreate |
writeMode | fail_if_not_empty (default), append, truncate_and_load |
The defaults refuse rather than overwrite. Re-running a stream into a table it already created therefore stops until you choose what should happen — drop_and_recreate for a clean rebuild, truncate_and_load to keep the table and replace its rows.
Three differences come from the warehouse rather than from Streams:
- No
upsert. It is a MERGE, and loading goes throughCOPY INTO, which appends. Accepting the mode would duplicate rows instead of updating them. - No foreign keys. Snowflake accepts the syntax and never enforces the constraints, so Streams does not create them.
- No indexes. Snowflake has none; it clusters instead, and that choice belongs to whoever owns the warehouse.
For the full behaviour of each policy, see Target Schema and Data Policies.
Stream configuration
The Snowflake target uses spec.snowflake with a staging block that controls how intermediate Parquet files are generated and uploaded.
{
"name": "MySQL to Snowflake",
"mode": "load",
"source": {
"id": "conn_mysql",
"spec": {
"db": {
"database": "commerce"
}
}
},
"target": {
"id": "conn_snowflake",
"spec": {
"snowflake": {
"database": "ANALYTICS",
"schema": "PUBLIC",
"schemaPolicy": "drop_and_recreate",
"writeMode": "truncate_and_load",
"staging": {
"fileFormat": "parquet",
"outputDirectory": "/tmp/snowflake-staging"
}
}
}
}
}
Target fields
| Field | Required | Default | Description |
|---|---|---|---|
database | Yes | — | Target Snowflake database |
schema | No | PUBLIC | Target schema |
schemaPolicy | No | fail_if_exists | How to handle existing tables |
writeMode | No | fail_if_not_empty | How to handle existing rows |
Staging fields
| Field | Required | Default | Description |
|---|---|---|---|
staging.fileFormat | Yes | — | Must be parquet. The field is required by validation, but the Snowflake target always writes Parquet — another value is accepted and then ignored |
staging.outputDirectory | No | System temp dir | Local directory for intermediate files before upload |
Intermediate files are always zstd-compressed Parquet; neither is configurable for a Snowflake target.
How loading works
- Structure. The database, schema and tables are created under the policies above. Nothing is staged until this succeeds.
- Export. Rows are written out as Parquet part files of about 150–200 MB, one set per writer, so memory stays bounded however large the table.
- Upload. Each part file is
PUTto your user stage as soon as it is cut, while the read goes on. Only the last files are left for the end. - Load. One
COPY INTOper table, withMATCH_BY_COLUMN_NAME = CASE_INSENSITIVEandUSE_LOGICAL_TYPE = TRUE. - Verify. The rows Snowflake reports loading are compared against the rows the run read. A mismatch fails the run rather than reporting a short load as success.
What happens after the read is timed separately and shown in the run monitor, so a slow finish points at the right place — our export, the network, or the warehouse:
In the run above — 260 million rows, 49 GB from MySQL — 46 of the 48 part files went up during the read, so the upload after it took 10 seconds. COPY INTO inside Snowflake, 2 minutes 11 seconds, is the largest phase: a bigger warehouse shortens that part, and only that part.
Why Parquet
Parquet is the default and recommended format for Snowflake targets.
| Aspect | Parquet | CSV.gz |
|---|---|---|
| Loading method | Vectorized | Line-by-line parsing |
| Schema | Self-describing | Requires manual mapping |
| Type safety | Validated at write time | Runtime errors |
| Analytical queries | Column pruning | Full scan required |
No additional configuration is needed — the stream generates Parquet files with zstd compression by default.
Type handling
Timestamps
COPY INTO runs with USE_LOGICAL_TYPE = TRUE, so the Parquet logical types survive the load. A zone-aware source column (PostgreSQL TIMESTAMPTZ) becomes TIMESTAMP_TZ; everything else lands as TIMESTAMP_NTZ.
MySQL TIMESTAMP is worth knowing about: MySQL normalizes it to UTC on write, and TIMESTAMP_NTZ is not zone-aware, so the value arrives as it was stored and carries no zone with it.
Wide decimals and zero dates
- A
DECIMALwider than 38 digits becomesVARCHAR. Snowflake'sNUMBERstops at 38 digits, and narrowing would round the values. - A MySQL zero date (
0000-00-00) arrives asNULL.
Binary data (BLOBs)
MySQL BLOB and binary columns are base64-encoded and mapped to VARCHAR in Snowflake, which avoids UTF-8 validation errors during loading. The conversion is transparent.
Identifier case
Table and column names are quoted and keep the source's case, so a source table products becomes SAKILA."PUBLIC"."products". An unquoted SELECT * FROM products folds to PRODUCTS and will not find it — quote the name, or use the Data Explorer, which quotes for you.
Verification
Loaded tables appear in the Data Explorer alongside every other connection, so you can check the result without leaving Streams:
Or in Snowsight:
-- Row count
SELECT COUNT(*) FROM ANALYTICS.PUBLIC."my_table";
-- Timestamp values
SELECT created_at FROM ANALYTICS.PUBLIC."my_table" LIMIT 5;
-- Binary data, stored as base64
SELECT LENGTH(picture_column) FROM ANALYTICS.PUBLIC."my_table" WHERE picture_column IS NOT NULL;
CDC into Snowflake
A Snowflake connection is a target for change replication as well as for loads,
and nothing about the connection itself changes between the two — but CDC asks
for two things a load does not: key-pair authentication, and EXECUTE TASK on
the account.
The mechanism is different enough to have its own page: nothing is staged and nothing is uploaded, changes arrive through Snowpipe Streaming into a change table, and a Snowflake task merges them into the ordinary table you query.
CDC into Snowflake — the objects a CDC stream creates, what CDC needs that a load does not, and how often changes are applied to the table you query.
Limitations
- Target only — Snowflake is not supported as a source
- No
upsertin Load mode — see Schema and write policies. CDC has no write mode at all: it appends changes, and a task merges them. - CDC needs key-pair authentication, a primary key, and
EXECUTE TASKon the account — see CDC into Snowflake - Binary size — base64 encoding adds roughly 33% for BLOB columns
Related docs
- CDC into Snowflake — change replication into a Snowflake target
- Target Schema and Data Policies
- Stream Configuration Reference
- Understanding Load Mode
- Connection Management