Migrating from JDBC Driver 4.x to 5.x¶
Version 5.x of the Snowflake JDBC Driver is built on the Universal Core. The jdbc:snowflake: URL and the JDBC API stay the same, except where this page notes differences. net.snowflake.client.jdbc.SnowflakeDriver still loads. It is deprecated in favor of net.snowflake.client.api.driver.SnowflakeDriver, which is the class registered with java.sql.Driver.
Migrating from JDBC Driver 3.x to 5.x requires the 4.x API migration first. See Migrating from JDBC Driver 3.x to JDBC Driver 4.x. This page starts from that API.
The Universal Core (sf_core) is a shared Rust library that implements the networking, authentication, result-set fetching, and stage-transfer logic that every Snowflake driver needs. Each driver wraps that core in a thin, language-specific layer that exposes the interface its ecosystem expects; the Rust layer is not visible to application code.
Snowflake previously maintained a separate implementation of this logic in each driver, so a security fix or protocol change had to be applied separately to every one. Because the core is shared, a change to it reaches every driver built on it in that driver’s next release.
Replace snowflake-jdbc or snowflake-jdbc-thin with one 5.x artifact. Do not leave a 4.x jar on the same classpath: both drivers register jdbc:snowflake:, and DriverManager uses whichever it finds first. Most applications that are already on the 4.x API need no code changes. Validate on a non-production application before you change the production dependency.
Install¶
Important
The driver loads a native library with JNI. It runs only on a JVM that permits JNI and native library loading.
The driver runs only when all of the following are true:
- A 64-bit JVM, Java 8 or later.
- Linux x86_64 or aarch64, macOS x86_64 or aarch64, or Windows x86_64. On Linux the driver selects the glibc or musl library. When it cannot detect the C library, it selects glibc. Set
-Djdbc.libc=muslon a musl host in that case. net.snowflake:snowflake-jdbc-native:5.0.0-RC1with a platform classifier, ornet.snowflake:snowflake-jdbc-native-all:5.0.0-RC1.- A writable
java.io.tmpdir. The driver copies the native library from the jar there and loads it with JNI. - No
snowflake-jdbc,snowflake-jdbc-fips, orsnowflake-jdbc-thinjar on the classpath.
Choose one package. snowflake-jdbc-native is the smaller one: the native library for a single platform, with its other libraries downloaded alongside it. snowflake-jdbc-native-all is one larger jar that contains every platform and those libraries. Use it when the same jar has to run on more than one platform.
A FIPS-compliant 5.x package is not available yet. JNA is not required.
After connecting, DatabaseMetaData.getDriverVersion() returns 5.0.0-RC1.
snowflake-jdbc-native¶
Set the classifier to the platform where the application runs: linux-x86_64, linux-aarch_64, osx-x86_64, osx-aarch_64, or windows-x86_64. The examples use Linux x86_64. With no classifier, the jar contains no native library and the driver does not run.
Maven:
Gradle:
snowflake-jdbc-native-all¶
This jar bundles a native library for every supported architecture and selects the one for the host at runtime.
Maven:
Gradle:
Set the classifier from the build machine¶
Maven and Gradle do not detect the platform. To use the machine that runs the build, add the plugin below and use its property as the classifier in the snowflake-jdbc-native dependency above, in place of linux-x86_64. snowflake-jdbc-native-all does not use a classifier.
The os-maven-plugin extension sets ${os.detected.classifier}:
The osdetector plugin sets osdetector.classifier:
Native library and JVM settings¶
The JDBC classes stay in the JVM. Login, query execution, authentication, TLS, HTTP, and PUT and GET run in the Universal Core native library, loaded with JNI from the jar. JDBC Driver 4.x uses JNA for its credential cache. This driver does not. Do not add the JNA jars.
Result data and network buffers live off the JVM heap, so -Xmx does not limit them. CLIENT_MEMORY_LIMIT limits buffered result chunks, in megabytes. The default is 1536. A value of 0 removes that limit. Values the application reads, such as strings, still occupy the JVM heap.
Snowflake traffic does not use JSSE or the Apache HTTP client. These settings do not apply:
javax.net.ssl.trustStore,javax.net.ssl.trustStorePassword,javax.net.ssl.trustStoreType. Setextra_root_store_pathto a PEM file to add those certificates to the default root store, orcustom_root_store_pathto a PEM file that replaces it.custom_root_store_pathwins, andextra_root_store_pathis then ignored.jdk.tls.client.protocols,jdk.tls.disabledAlgorithms. Setmin_tls_versionandmax_tls_versiontotls12ortls13. Any other spelling,TLSv1.2included, is rejected.- A default
SSLSocketFactoryorHostnameVerifieronHttpsURLConnection. net.snowflake.jdbc.ocspFailOpen,net.snowflake.jdbc.ocspResponseCacheDir. (BD#46)net.snowflake.jdbc.enableBouncyCastle,net.snowflake.jdbc.useBundledBouncyCastleForPrivateKeyDecryption.privateKey,private_key_file, andprivate_key_base64still work.net.snowflake.jdbc.max_connections,net.snowflake.jdbc.max_connections_per_route.net.snowflake.jdbc.http_client_connection_timeout_in_ms,net.snowflake.jdbc.http_client_socket_timeout_in_ms. The connection propertiesHTTP_CLIENT_CONNECTION_TIMEOUT,HTTP_CLIENT_SOCKET_TIMEOUT, andnetworkTimeouttake milliseconds and apply in whole seconds, rounded up. Each HTTP attempt uses a 300-second wall-clock timeout, measured from the start of the attempt to the end of the response body, so arriving bytes do not extend it and a response that takes longer than five minutes fails. Result-chunk downloads are outside that limit and have no read timeout of their own.networkTimeoutdoes not bound retries of query requests. (BD#77, BD#78)
Proxy¶
Set an HTTP or HTTPS proxy with useProxy, proxyHost, and proxyPort, or with -Dhttp.useProxy=true plus http.proxyHost or https.proxyHost and the matching port. The JVM properties apply when the connection does not set a proxy, and both a host and its port must be set. http.nonProxyHosts is included, and the NO_PROXY environment variable is merged into it.
-DsocksProxyHost, -DsocksProxyPort, and java.net.useSystemProxies do not apply. disableSocksProxy does not change routing. (BD#64)
nonProxyHosts=foo.com bypasses foo.com and its subdomains. JDBC 4.x bypasses only the exact host. * bypasses domains and not IP addresses. 10.0.0.0/8 is a CIDR. 192.168.* is not. Matching is case-sensitive. (BD#65)
Logging¶
Driver messages and native-library messages are delivered to the application logger. Native-library messages use net.snowflake.client.CoreLogger. DEBUG is the finest level.
SLF4J is recommended. The driver includes the SLF4J API. Add a binding such as Logback:
Set the level on net.snowflake.client and net.snowflake.client.CoreLogger.
When net.snowflake.jdbc.loggerImpl is unset, delivery uses java.util.logging (net.snowflake.client.log.JDK14Logger). With that logger, a tracing connection property writes %h/snowflake_jdbc%u.log unless java.util.logging.config.file is set.
SNOWFLAKE_TROUBLESHOOTING_ENABLED=true, set before the process starts, writes sf_driver_troubleshooting.log. SNOWFLAKE_TROUBLESHOOTING_REPORT_PATH selects the directory. Otherwise the file is written in the current directory.
Configuration differences¶
Camel-case names such as proxyHost, loginTimeout, putGetMaxRetries, and oauthClientId are mapped to the names the Universal Core uses. browser_response_timeout is mapped to authentication_timeout.
| 4.x name or call | 5.x |
|---|---|
clientRequestMfaToken, setEnableClientRequestMfaToken | Ignored, with a log warning. Use clientStoreTemporaryCredential. (BD#5) |
setEnableClientStoreTemporaryCredential | Removed. Use setClientStoreTemporaryCredential. (BD#31) |
setOcspFailOpen, disableOCSPChecks, insecureMode | No OCSP. Set crl_check_mode to ENABLED (fail-close) or ADVISORY (fail-open). The default is DISABLED. (BD#46) |
ocspFailOpen | Still accepted, but it is no longer a TLS setting: it is forwarded as a login session parameter and checks no certificate. (BD#46) |
setHttpHeadersCustomizers | Removed. (BD#43) |
setClientConfigFile | Removed. Configure logging on the application logger. (BD#31) |
SnowflakeDataSource.setNetworkTimeout | Absent. Connection.setNetworkTimeout does not store the value, and getNetworkTimeout returns 0. (BD#31, BD#41) |
setEnablePutGet | Absent. The connection property enablePutGet still applies. (BD#31) |
setStringsQuotedForColumnDef | Absent. The connection property STRINGS_QUOTED_FOR_COLUMN_DEF still applies. (BD#31) |
setDisableGcsDefaultCredentials, disableGcsDefaultCredentials | Not read. (BD#31) |
ownerOnlyStageFilePermissionsEnabled | Not read. A GET writes mode 0600 on Unix. Set unsafe_file_write to true to restore the process umask. (BD#56) |
putGetMaxRetries unset | PUT and GET retry 6 times. JDBC 4.x retries 25. A zero or negative value also gives 6. A positive value is used as given. (BD#79) |
disableQueryContextCache, private_key_pwd, PRIVATE_KEY_PWD, PRIVATE_KEY_BASE64, wifHost, workloadIdentityHost | No longer applied in a connections.toml or config.toml profile, and the setting is lost without an error. disable_query_context_cache, PRIVATE_KEY_FILE_PWD, and workload_identity_host still apply. (BD#71) |
/snowflake/session/token, SKIP_TOKEN_FILE_PERMISSIONS_VERIFICATION | Not read. Supply the token file path explicitly. SNOWFLAKE_HOME is used only when that directory exists; otherwise the platform config directory is used. (BD#71) |
Drivers built on the Universal Core do not support OCSP-based certificate revocation checking. Revocation checking is available only through CRLs (certificate revocation lists), and it is off by default.
If you require revocation checking, enable CRL checking and evaluate it in a non-production environment under a realistic workload. Confirm that your network allows outbound access to the CRL distribution points named in Snowflake’s certificate chain: a driver that cannot reach a distribution point cannot complete a revocation check.
Behavior differences¶
Authentication¶
clientRequestMfaToken=falsedoes not turn caching off. SetclientStoreTemporaryCredential=false. That one property covers MFA, browser, and OAuth token caching. Both default totrue. (BD#5)- OAuth token and authorization URLs must use HTTPS. A non-loopback
http://value is rejected at connect. (BD#10) - Remove workload-identity settings unless
authenticator=WORKLOAD_IDENTITY. A non-empty provider, Entra resource, Azure client id, impersonation path, orwif_host, orworkloadIdentityAwsUseOutboundToken=true, fails the connection. JDBC 4.x ignored those settings on other authenticators. (BD#48) - Remove
workloadIdentityImpersonationPathwhen the provider isOIDC. JDBC 4.x ignored the path and logged in as the original identity. (BD#49) - Remove
workloadIdentityAwsExternalId. With AWS workload identity a non-empty value fails the connection. The value is not sent onAssumeRole. (BD#73) - Local workload-identity failures have no SQLSTATE or vendor code.
getSQLState()is null andgetErrorCode()is0. JDBC 4.x used08000and200071. (BD#75) - A missing OAuth client id, secret, or token URL is one error naming every missing
oauth_client_id,oauth_client_secret, andoauth_token_request_url. JDBC 4.x reports the first gap as vendor code200001. (BD#6)
Password, key-pair, and external-browser login keep the same authenticator names.
Statements and batches¶
Statement.getQueryID()after a synchronous multi-statement execute returns the parent id. JDBC 4.x returns the first child’s id. (BD#24)getUpdateCount()after a result-set statement returns-1, includingSELECT, PUT, GET, and REMOVE. (BD#58)executeBatch()with an update count aboveInteger.MAX_VALUEreturnsSUCCESS_NO_INFO(-2). JDBC 4.x throwsBatchUpdateExceptionvendor code200058. (BD#2)getBatchQueryIDs()keeps one slot per statement, including a failed statement. An array bind records one id. JDBC 4.x records none. (BD#3)getUpdateCount()afterexecuteBatch()returns-1. JDBC 4.x returns the last count. (BD#4)addBatch()rejects a later row that omits a placeholder bound on the first row. (BD#11)- A failed array-bind
executeBatch()throwsBatchUpdateException.catch (SnowflakeSQLException)does not see it. (BD#72)
Results¶
JDBC_ARROW_TREAT_DECIMAL_AS_INTapplies to JSON results. WithJDBC_QUERY_RESULT_FORMAT=JSON,JDBC_TREAT_DECIMAL_AS_INT=false, andJDBC_ARROW_TREAT_DECIMAL_AS_INTleft at its defaulttrue,getObject()returnsLongfor a scale-0 number. JDBC 4.x returnsBigDecimal. Metadata still followsJDBC_TREAT_DECIMAL_AS_INT, so the SQL type can beDECIMALwhilegetObject()returnsLong. A connection that leaves both properties attruesees no change. (BD#60)- A
GEOGRAPHYorGEOMETRYcolumn inWKBorEWKBoutput reportsVARCHAR.getColumnType()isVARCHARandgetColumnClassName()isjava.lang.String, whilegetObject()andgetBytes()still return the bytes. JDBC 4.x reportsBINARY. Code that reads the column type and then treats the value as text can mangle the bytes.GeoJSONandWKToutput isVARCHARon both drivers. (BD#53) isLast()andisAfterLast()on aDatabaseMetaDataresult set return a boolean. JDBC 4.x throws aSQLFeatureNotSupportedExceptionsubclass from the query-backed metadata calls, so acatcharound those two methods no longer runs. (BD#21)
DataSource¶
DataSource.getProperties()returns a copy. Configure the DataSource with its setters: editing the returned map no longer changes later connections. A user and password passed togetConnection(username, password)apply to that call only and no longer appear in a latergetProperties(). (BD#44)DataSource.getConnection()no longer rejects a missing username up front. A programmatic-access-token,OAUTH_AUTHORIZATION_CODE, orOAUTH_CLIENT_CREDENTIALSlogin, and OAuth or workload identity set outsidesetAuthenticator, connect with no username. Password, key-pair, external-browser, Okta, and MFA login still fail, but at connect and with the messageMissing required parameter: user, so code matching onCannot create connection because username is missing in DataSource properties.has to change. An empty-string username counts as missing. (BD#45)PooledConnection.getConnection()invalidates the handle it returned last. Code still holding the older handle gets vendor code200052and SQLSTATE08003fromcreateStatement()and the other calls that need a live connection, andisClosed()returnstrue. JDBC 4.x leaves both handles working against the same session. (BD#36)- A closed logical handle no longer reaches the physical connection.
setClientInfo, bothgetClientInfomethods,unwrap, andisWrapperForthrow vendor code200052and SQLSTATE08003.unwrap(Connection.class)returns the closed handle itself. (BD#29, BD#33) PooledConnection.getConnection()afterclose(), andabort()on a borrowed handle, throw a checkedSQLException. JDBC 4.x throwsNullPointerExceptionfrom the first and leavesisClosed()false after the second, so acatch (NullPointerException)there no longer runs. (BD#27, BD#40)connectionErrorOccurredno longer fires for caller mistakes. ASQLFeatureNotSupportedException, a rejected client-info property, and anunwrapto an unsupported type reach the caller without the event, so a pool no longer evicts a healthy connection over them. Any otherSQLExceptionstill fires it. (BD#26, BD#32, BD#35)- A listener that throws no longer stops dispatch, and a listener handling
connectionClosedobservesisClosed()astrue. (BD#28, BD#30)
Driver¶
Driver.getPropertyInfoasks foruserandpasswordwhenauthenticatoris empty orUSERNAME_PASSWORD_MFA. (BD#68, BD#69)Driver.getPropertyInfodoes not include a malformed URL. SQLSTATE stays08000and vendor code stays200059. (BD#66)Driver.getPropertyInforejects a non-Stringproxy_host,proxy_port,proxyHost, orproxyPortwith SQLSTATE22023and vendor code200033.connectapplies no such check, so an integerproxy_portstill connects. Only aProperties.putof a non-Stringreaches this; a URL query value is always a string. (BD#67)Driver.getPropertyInfoparses ajdbc:snowflake:autoURL the same wayconnectdoes. A malformed auto URL reportsINVALID_PARAMETER_VALUErather thanINVALID_CONNECTION_STRING, so code matching the latter has to accept it. (BD#71)
Connection¶
jdbc:snowflake:autoalso reads[connections.<name>]fromconfig.toml.connections.tomlwins when both files define the name. Profile selection falls back throughSNOWFLAKE_DEFAULT_CONNECTION_NAME,default_connection_nameinconfig.toml, and thendefault. (BD#71)jdbc:snowflake:autovalidates strictly. A malformed query, a URL fragment, a duplicate alias, and a non-String selector inPropertiesreportINVALID_PARAMETER_VALUE. Two aliases with equal values are accepted, where JDBC 4.x rejected them. (BD#71)- A missing profile carries SQLSTATE
22023and vendor code200047. The message is unchanged, so it can be classified instead of string-matched. JDBC 4.x reported no SQLSTATE and vendor code0. (BD#71) Propertiesoverride URL options, andconnectionNameandconnection_nameare matched case-insensitively. JDBC 4.x matched the exact, case-sensitiveconnectionNameURL key. Aurlentry inPropertiessupplies the auto URL when the JDBC URL argument is blank. (BD#71)
Clob¶
setAsciiStreamwrites characters.'a'then'b'yields"ab". JDBC 4.x yields"98". (BD#14)free()invalidates the handle. JDBC 4.x leaves an empty Clob that still accepts calls. (BD#15)positionsearches forward from 1.position("world", 1)on"hello world"returns7. (BD#16)setString(pos, str, offset, len)copieslencharacters.setString(1, "XXhelloYY", 2, 5)writes"hello". JDBC 4.x writes"hel". (BD#17)setStringoverwrites."hello world"plussetString(1, "HELLO")is"HELLO world". JDBC 4.x yields"HELLOhello world". (BD#13)
PUT and GET¶
- PUT canonicalizes every source path, including
..and a symlink. A symlinked file is uploaded under the target’s base name. (BD#57) - An auto-compressed PUT does not store
Content-Encoding: gzip. GET and COPY still use the compression recorded with the file. (BD#76) - The size columns are
BIGINT, notDECIMAL. Under the defaultJDBC_TREAT_DECIMAL_AS_INT=true, PUTsource_sizeandtarget_sizeand GETsizereportBIGINT, type nameNUMBER, precision 64, andgetObject()returnsLong, so a cast ofgetObject()toBigDecimalthrowsClassCastException.getLong,getString, andgetBigDecimalare unaffected.statusandmessageareVARCHARwith declared length 10000 rather than 20480. (BD#54) - A GET writes mode
0600on Unix rather than following the process umask. A second local account or group that could read downloaded stage files loses access unless the deployment setsunsafe_file_writetotrue. Windows is unaffected. (BD#56) - A failed GET leaves an existing destination file intact. The download is staged as
<dst>.partand renamed on success, so cleanup code that deleted a truncated destination is no longer needed. A hard process kill can leave a.partfile behind. (BD#25) - A GET whose files collapse to one local basename logs a
WARNINGnaming every colliding basename. The result status and the surviving file are unchanged. (BD#61)
Every behavior difference between the generally available driver and the version built on the Universal Core is catalogued in the driver source repository, as a machine-readable file listing each entry’s previous behavior, new behavior, classification, and whether it is a breaking change. Read it in full before you upgrade an application you cannot easily roll back:
The sections above summarize the entries most likely to affect an existing application. The catalog is the complete list.
Differences removed during preview¶
Earlier preview builds differed from JDBC Driver 4.x on these items. Those behavior differences have been removed:
- A programmatic-access-token login with a token and no password connects. (BD#1)
- Binding a
DATEwhenDATE_OUTPUT_FORMATcontainsMONsucceeds. (BD#7) getTransactionIsolation()returns the value last set. The default isTRANSACTION_NONE. (BD#18)- A query-context cache entry is displaced when a new id arrives at the same priority. (BD#59)
- An Okta authenticator rejection includes the Snowflake message. (BD#63)