Changelog
View SourceAll notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[Unreleased]
[1.7.2] - 2026-09-30
Enhancements
Snowflex.Transport.Httpnow recovers from a JWT that expires mid-call. When Snowflake rejects a request with390198, the transport signs a new token and retries that request once, but only for requests addressed to an existing statement handle: async polls, partition fetches,statement_status/3,fetch_result/3andcancel_statement/3. A token re-signed while polling is kept for that call's partition fetches and for later calls on the connection. Statement submission is never retried, so a statement is never resubmitted. A retry logs a warning. If the retried request is rejected too, the error is returned as before. (#202)
Bug Fixes
Snowflex.Transport.Httpcould let its cached JWT expire partway through a long call. The token was only re-signed when less than 30 seconds remained, but a call's async polling and partition fetches all reuse the token for up to the call's:timeout, so Snowflake rejected a later request with390198while the statement was still running. The token is now re-signed at the start of any call it cannot outlast (remaining lifetime less than the call's:timeoutplus 30 seconds), and:infinitycalls always start with a new token. When:timeoutis long, set:token_lifetimenear Snowflake's 1 hour maximum (e.g.:timer.minutes(55)) so the token stays cached across calls. (#202)
[1.7.1] - 2026-09-25
Enhancements
Snowflex.submit_async/4accepts a:statement_timeoutoption (milliseconds) that bounds the statement's own server-side execution (STATEMENT_TIMEOUT_IN_SECONDS) independently of:timeout, which bounds only the submission round-trip. For a fire-and-forget submission these two bounds are genuinely independent —async=truereturns a statement handle in milliseconds while the statement itself may run for minutes — so a caller can hold the round-trip to a short timeout while letting the statement run much longer.:statement_timeoutdefaults to:timeoutwhen omitted, so existing callers are unaffected. (#200)
[1.7.0] - 2026-09-24
Breaking Changes
- The minimum supported Req version is now 0.7 (
{:req, "~> 0.7"}, previously"~> 0.5"). Consumers pinned to an older Req should stay on snowflex 1.6.0 until they can upgrade. Snowflex.Transport.Httpsent thetimeoutfield of a statement request in milliseconds, but Snowflake defines it in seconds. The default 45-second timeout therefore asked Snowflake to wait 45,000 seconds (about 12.5 hours), effectively disabling the server-side statement timeout; a configured:timeoutis now converted to seconds. Statements that previously ran unbounded will now be cancelled by Snowflake at the configured timeout, so review:timeoutfor any long-running statement before upgrading.
Enhancements
- Add
Snowflex.submit_async/4,Snowflex.statement_status/3,Snowflex.fetch_result/3andSnowflex.cancel_statement/3for fire-and-forget statement submission.submit_async/4sends Snowflake'sasync=truequery parameter, so the API acknowledges with a statement handle as soon as it accepts the statement instead of holding the response open until the statement finishes. A connection is checked out only for the submission round-trip, so long-running statements no longer occupy a pool slot for their full duration.fetch_result/3returns the statement's rows once it completes (and{:ok, :running}before that, so it can be used to poll), decoded exactly asEcto.Repo.query/4decodes them. Submitted statements are bounded only by Snowflake's statement timeout, since nothing on the client side waits on them. (#197) Snowflex.Transportgains four optional callbacks —submit_async/4,statement_status/3,fetch_result/3andcancel_statement/3— implemented bySnowflex.Transport.Http. They are optional, so existing custom transports continue to satisfy the behaviour without change; calling an async function on a transport that does not implement them returns a descriptiveSnowflex.Errorrather than raisingUndefinedFunctionError.Snowflex.Transport.Httpnow caches its signed JWT for the configured:token_lifetimeinstead of re-signing on every request. Each request previously performed a PEM decode and an RSA private-key operation; this is most noticeable when polling an async statement, where one workflow issues many requests.
Bug Fixes
- JWT
expclaims were computed by adding a millisecond:token_lifetimeto a second-precision timestamp, so tokens advertised an expiry roughly a week away instead of the configured lifetime (10 minutes by default). Snowflex.Transport.Httpno longer triggers Req 0.7's deprecation warning ("setting:finchto a Finch pool name is deprecated, usefinch: [name: name]instead") on every request when a dedicated Finch pool is routed viareq_options: [finch: MyFinch]. The canonical form is nowreq_options: [finch: [name: MyFinch]]; the bare pool name is still accepted and wrapped automatically.
[1.6.0] - 2026-07-13
Enhancements
Ecto.Repo.stream/2is now lazy: it drives the DBConnection cursor and fetches one Snowflake partition at a time instead of executing eagerly (previously it gathered the entire result set before producing the first row). Because the connection must stay checked out while the stream is consumed and Snowflake has no transactions, the enumeration must run insideEcto.Repo.checkout/2— mirroring ecto_sql's stream-inside-transaction requirement — and raises with instructions otherwise. PreferRepo.all/2when you want the whole result set: the eager path fetches partitions in parallel. (#188)- Add
Snowflex.stream_query/5, the raw-SQL counterpart: it lazily streams a statement's result oneSnowflex.Resultpartition at a time into a consumer function, checking out a connection for the duration of the function (or reusing the one held by an enclosingcheckout/2). Streamed partitions carry the statement'srowTypemetadata, so values decode to the same typesexecuteproduces. (#188) - The cursor halts with the final partition, so streams contain exactly one result per partition — no trailing empty result — and skip a needless final fetch round-trip. (#188)
Bug Fixes
Repaired the DBConnection cursor path, which had never been exercisable (#188):
Snowflex.Connection.handle_fetch/4matched{:cont, result}from the transport, but transports return{:ok, result}per theSnowflex.Transportcontract, so the first fetch of every cursor crashed with aCaseClauseError. Its{:cont, ...}branch also returned a{:cont, result, query, state}4-tuple, which DBConnection rejects as a bad return.Snowflex.Connection.handle_status/2disconnected with "Snowflex does not support transactions", butDBConnection.run/3— the required wrapper for streaming, also used by Ecto'scheckout/2— probes connection status around every checkout. It now reports{:idle, state}(a Snowflake connection is never inside a transaction). Explicit transaction callbacks (handle_begin/commit/rollback) still reject as before.Snowflex.Transport.Httpdeclaredid not poll async (HTTP 202) statement execution to completion the wayexecutedoes, so declaring a cursor for any statement running longer than ~45 seconds crashed the transport with aCaseClauseErrorinstead of streaming its result.
[1.5.0] - 2026-07-02
Enhancements
Snowflex.Transport.Httpnow derives the public-key fingerprint from your private key automatically, so:public_key_fingerprintis no longer required in config. The newSnowflex.Transport.Http.KeyFingerprintmodule computesbase64(sha256(DER SubjectPublicKeyInfo))(matching Snowflake'sRSA_PUBLIC_KEY_FP). An explicitly-provided:public_key_fingerprintis still honored. (#184)
[1.4.1] - 2026-06-17
Bug Fixes
- Enable gzip decompression for Snowflake API responses. Req v0.6.0 flipped the
compressedoption fromtruetofalseby default (GHSA-655f-mp8p-96gv), causing partition fetches to return raw gzip bytes instead of decoded JSON.build_options/1now explicitly setscompressed: true. (#181)
[1.4.0] - 2026-06-16
Enhancements
- Add support for
:map,{:map, inner_type}, and{:array, inner_type}Ecto types stored as SnowflakeVARIANTcolumns (#172, #180). Values are automatically JSON-encoded viaPARSE_JSON(?)on insert, update, and update_all, and decoded on read.
Bug Fixes
- Recycle pool workers when their HTTP transport process dies.
Snowflex.Connection.connect/1no longer callsProcess.flag(:trap_exit, true), so the link from worker → transport propagates death normally: when the transport crashes, the worker dies with it, the DBConnection pool starts a replacement, and the next query lands on a fresh transport instead of looping forever on{:noproc, ...}against a stale pid.
[1.3.1] - 2026-06-10
Enhancements
- Support routing Snowflake REST requests at a dedicated Finch pool. When a
:finchinstance is supplied via:req_options,Snowflex.Transport.Httpnow omits:connect_optionsso Req does not reject the request for setting both:finchand:connect_options. This lets callers point a warehouse at an explicitly-sized, isolated Finch pool. (#174)
Changed
- Relax
ectoandecto_sqlrequirements from~> 3.14to~> 3.13so consumers can stay on the 3.13 series.
[1.3.0] - 2026-06-08
Enhancements
- Implement aggregate
filter()via CASE WHEN translation (#168)count(x) |> filter(cond),sum,avg,min,maxtranslate toagg(CASE WHEN cond THEN x END)count() |> filter(cond)(COUNT(*)) maps to Snowflake'sCOUNT_IF(cond)
Bug Fixes
- Raise a clear
ArgumentErrorfor unsupportedon_conflictmodes (:nothing, field list, update query) pointing users towardMERGE INTO(#167)
Changed
- Bump minimum Elixir version to 1.15 (#163)
- Replace ex_check + dialyxir with assay for CI (#166)
- Bump ecto_sql and doctor, enabling plug and decimal upgrades to avoid CVE versions (#165)
- Bump req from 0.5.x to 0.6.0 (#169)
- Bump jose from 1.11.10 to 1.11.12 (#128)
- Bump ecto from 3.13.5 to 3.13.6 (#153)
- Bump ecto_sql from 3.13.4 to 3.13.5 (#147)
- Bump jason from 1.4.4 to 1.4.5 (#157)
- Bump telemetry from 1.3.0 to 1.4.1 (#148)
- Bump assay from 0.5.2 to 0.6.1 (#170)
- Bump credo from 1.7.16 to 1.7.19 (#149, #151, #171)
- Bump ex_doc from 0.40.0 to 0.40.3 (#142, #159)
[1.2.1] - 2026-02-27
Bug Fixes
- Dramatically improve memory usage for large datasets #145
- Fix broken hexdoc links #143 by @henriqueberlesi
Changed
- Bump Credo from 1.7.15 to 1.7.16 #139 by dependabot
[1.2.0] - 2026-02-09
Enhancements
- Add support for
options/1to get Req.options (#140)
Bug Fixes
- Resolve failing integration tests (#140)
- Fix compiler warning on Elixir 1.19 (#140)
- Fix global setting of req options (#140)
Changed
- Bump ex_doc from 0.39.3 to 0.40.0 (#138) by dependabot
- Bump db_connection from 2.8.1 to 2.9.0 (#137) by dependabot
- Bump req from 0.5.16 to 0.5.17 (#136) by dependabot
- Bump credo from 1.7.14 to 1.7.15 (#135) by dependabot
- Bump ex_doc from 0.39.2 to 0.39.3 (#134) by dependabot
- Bump plug from 1.19.0 to 1.19.1 (#133) by dependabot
- Bump ecto_sql from 3.13.2 to 3.13.4 (#132) by dependabot
- Bump plug from 1.18.1 to 1.19.0 (#131) by dependabot
- Bump ex_doc from 0.39.1 to 0.39.2 (#130) by dependabot
- Bump credo from 1.7.13 to 1.7.14 (#129) by dependabot
[1.1.0] - 2025-10-28
Enhancements
- Add support for passing in private keys as files or as raw strings (#113) by @nyo16
Changed
- Bump req from 0.5.15 to 0.5.16 (#126) by dependabot
- Bump dialyxir from 1.4.6 to 1.4.7 (#125) by dependabot
- Bump ecto from 3.13.4 to 3.13.5 (#124) by dependabot
[1.0.5] - 2025-10-28
Enhancements
- Add
ping/1callback toSnowflex.Transportbehavior to allow transports to opt out of periodic ping checks - HTTP transport now skips unnecessary ping queries, eliminating rate limit errors (HTTP 429) from DBConnection's idle connection checks
- Add more detailed metadata into Logger.
[1.0.4] - 2025-09-03
Bug Fixes
- Change to using
identifierinstead ofliteralto match Ecto's changes - Fix compiler warning in tests
Enhancements
- Automatically track application version in User-Agent header by introspecting Application spec.
[1.0.3] - 2025-08-29
Bug Fixes
- Add float_decode support for Decimal types to handle data type mismatches (#107)
[1.0.2] - 2025-08-25
Bug Fixes
- Handle timeouts from execute_statement in
http.ex(#100)
Changed
Bump dialyxir from 1.4.5 to 1.4.6 (#102) by dependabot
Bump ex_doc from 0.38.2 to 0.38.3 (#103) by dependabot
[1.0.1] - 2025-08-25
Bug Fixes
- Fix migration generator depending on
safe_concatfor building module names (#101)
Enhancements
- Accept req
:connect_optionsconfig (#104)
[1.0] - 2025-04-24
This release is a ground up rewrite of the library.
We completely remove ODBC in favor of utilizing Snowflex's HTTP API, and remove support for password based authentication, in line with Snowflake's own approach.
We also now expose a fully Ecto/DBConnection compatible implementation.
For complete details, as well as some helpful migration instructions, see the README.
[0.5.3] - 2023-10-23
Added
- Handle casting maybe date types #78
[0.5.2] - 2022-11-18
Added
- Add unicode_string_param function #60
Fixed
- ODBC error not always a string #53
[0.5.1] - 2022-05-10
Added
- Handle UTF-16 return data from Snowflake #38
[0.5.0] - 2022-01-19
Added
- General improvements, add DBConnection adapter #32
[0.4.5] - 2021-10-29
Added
- Callback that calls gc after queries to try to shuffle binary strings off the heap. We've seen large memory savings in a real application. #23
[0.4.4] - 2021-10-27
Fixed
- Relax dependency requirement on
telemetry
[0.4.3] - 2021-10-27
Fixed
- Hot fix for possible memory leak #27
[0.4.2] - 2021-10-08
Fixed
- Properly convert elixir nil to erlang :null in query params
[0.4.1] - 2021-09-02
Added
- Added telemetry 1.0
- New telemetry events around queries
[0.4.0] - 2021-06-10
Added
- Enabled SQL
INSERTstatements.
[0.3.3] - 2021-06-07
Fixed
- Corrected spelling in type definition
[0.3.2] - 2021-06-02
Added
- Added
map_nulls_to_nil?variable to connection configuration to allow conversion of:nullvalues to:nilin snowflake query response
[0.3.1] - 2021-03-10
Fixed
- Initialize the worker with a propslist instead of a tuple.
- create copies of the
odbctype definitions
[0.3.0] - 2021-03-09
Added
- Added the ability to keep connections alive through configuration on the
Snowflex.Connectionmodule.
[0.2.2] - 2021-02-09
Fixed
- make sure to follow
{:error, reason}convention in all parts of the worker
[0.2.1] - 2020-11-20
Fixed
- fix bug where handle_info is missing argument
[0.2.0] - 2020-11-18
Added
- Added the ability to define a module that will use
Snowflex.Connectionto maintain connection information
Breaking
- Remove
Snowflex.Queryas it was a duplicate - Remove
Snowflex.ConnectionPoolin favor of newSnowflex.Connection
[0.1.1] - 2020-11-17
Changed
- Configure the worker module on the
Snowflex.ConnectionPooland no longer on the application level.
[0.1.0] - 2020-11-16
Breaking
- Users must launch connection pools as part of their application's
supervision tree using
Snowflex.ConnectionPool, and all queries must specify which connection pool to use. No connection pool will be started by default.