feat: default DECIMAL to native Arrow decimal128 on the wire (parity with Python/JDBC) - #436
Closed
msrathore-db wants to merge 1 commit into
Closed
Conversation
…with Python/JDBC) Flip the Thrift backend's `useArrowNativeDecimal` default from false to true so DECIMAL columns are requested as native Arrow decimal128 (a compact fixed-width binary encoding) instead of UTF8 strings. This matches databricks-sql-python (`_use_arrow_native_decimals=True` by default) and databricks-jdbc (`setDecimalAsArrow(true)`), giving the Go driver the same reduced payload / no per-cell string parsing on decimal-heavy results. Scanning through database/sql is unchanged: DECIMAL is still returned as a lossless, scale-applied string, because Go's driver.Value contract permits only int64/float64/bool/[]byte/string/time.Time/nil — there is no decimal type, so a string is the faithful lossless scalar (verified end-to-end against a live warehouse: default and native=false both return string "19.99"). The native decimal128 surfaces only through GetArrowBatches, which is a breaking change for raw-Arrow consumers: DECIMAL columns now arrive as arrow.Decimal128 arrays rather than string arrays. Pass WithArrowNativeDecimal(false) (or DSN useArrowNativeDecimal=false) to restore the legacy string wire format. The kernel backend already sends Decimal128 on the wire and renders DECIMAL exactly, so it is unaffected. The useArrowNativeDecimal DSN carrier becomes a *bool so the connector can distinguish an absent parameter (keep the native-on default) from an explicit useArrowNativeDecimal=false (override and win); a plain bool would silently reset the default to false on the sql.Open(dsn) path. Co-authored-by: Isaac
Contributor
Author
|
Superseding with a branch on the repo itself rather than a fork. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Flip the Thrift backend's
useArrowNativeDecimaldefault fromfalsetotrueso DECIMAL columns are requested as native Arrowdecimal128(a compact fixed-width binary encoding) on the wire instead of UTF8 strings. This brings the Go driver to parity with the other first-party drivers:_use_arrow_native_decimals=True(thrift_backend.py), sendingdecimalAsArrow=true.setDecimalAsArrow(true)(DatabricksThriftServiceClient.java).false, i.e. UTF8-string decimals on the wire.The motivation is latency / payload size: native
decimal128is a compact fixed-width binary encoding, avoiding the larger variable-length UTF8 payloads and per-cell string parsing on decimal-heavy result sets.What does not change
Scanning through
database/sqlis unchanged: a top-level DECIMAL is still returned as a lossless, scale-applied string. Go'sdriver.Valuecontract permits onlyint64/float64/bool/[]byte/string/time.Time/nil— there is no decimal type, so a string remains the faithful lossless scalar (Python returnsDecimaland JDBC returnsBigDecimalbecause their language APIs allow it; Go structurally cannot). The nativedecimal128surfaces only throughGetArrowBatches.Verified end-to-end against a live warehouse:
database/sqlGetArrowBatches(wire)string "19.99"decimal(38,2)nativeWithArrowNativeDecimal(false)string "19.99"utf8(legacy)Breaking change (GetArrowBatches only)
Consumers of the raw-Arrow
GetArrowBatchespath now receive DECIMAL columns asarrow.Decimal128arrays rather than string arrays. To restore the previous string wire format, passWithArrowNativeDecimal(false)or DSNuseArrowNativeDecimal=false. The standarddatabase/sqlpath is source- and value-compatible.The kernel backend already sends
Decimal128on the wire and renders DECIMAL exactly, so it is unaffected.Implementation notes
ArrowConfig.WithDefaults()now setsUseArrowNativeDecimal = true.useArrowNativeDecimalDSN carrier (UserConfig.UseArrowNativeDecimalDSN) becomes a*bool. This is necessary: the connector copies the carrier ontoArrowConfigunconditionally, so with a plainboola silentsql.Open(dsn)would reset the new default back tofalse. As a pointer,nil= "unspecified → keep the native-on default", and a non-nil value = explicit override (souseArrowNativeDecimal=falsestill wins).DeepCopydeep-copies the pointer.Test plan
go build,go vet,go test ./...— 1093 passing).WithArrowNativeDecimal(false)override, DSN=falseoverride, and silent DSN keeps the default.arrowbasedscanner tests that replay theall_types.jsonfixture (whose decimal column is captured in the legacy string wire format) now explicitly opt to the string path, matching the sibling "Retrieve values" test.This pull request and its description were written by Isaac.