Conversation
…lues
DecimalParameter.calculate_decimal_cast_string inferred DECIMAL(precision,
scale) by string-splitting str(value) on ".", which miscounts two forms
that str(Decimal) legitimately produces:
- exponent notation (Decimal("1500").normalize() -> "1.5E+3"): the "E+3"
was counted as fractional digits, giving DECIMAL(5,4) for 1500, which
overflows (an error in ANSI mode, NULL in legacy mode).
- a leading minus sign: counted as an extra integer digit, so a negative
38-digit value produced DECIMAL(39,0), exceeding Databricks' max DECIMAL
precision of 38 and failing the cast.
Derive precision/scale from Decimal.as_tuple() (digits + exponent), ignoring
sign and display format. Existing cast-string tests are unchanged; added
regression cases for exponent notation and negative values.
Signed-off-by: Madan Kumar <winklemad@outlook.com>
|
Confirming this is still needed on 4.6.0. Against a SQL warehouse, a bound |
|
Thanks for checking this against a real warehouse, that is really helpful. Good to know the executemany and SELECT :p paths round-trip too, since those were the two I was least sure about outside the unit tests. |
|
The enhancement to incorperate Exponent and Negative values generally looks good. |
Decimal("0E+38") has a single digit and exponent 38, which the integer
branch turned into DECIMAL(39,0). A zero only needs DECIMAL(1,0).
|
@jay-xiao446 good catch, thanks. A zero like 0E+38 has one digit but a large exponent, so the integer branch counted 39. It's now sized as a single digit when the value is zero, with tests for 0E+38, -0E+5 and 0E-5 (the last one still gives DECIMAL(5,5)). Pushed in 60d58f9. |
Description
DecimalParameter.calculate_decimal_cast_string(parameters/native.py) infers theDECIMAL(precision, scale)to cast a boundDecimalto by string-splittingstr(value)on".". That miscounts two formsstr(Decimal)legitimately produces, so the generated type can't hold the bound value.Exponent notation —
str()emitsEfor many magnitudes (e.g.Decimal("1500").normalize()isDecimal("1.5E+3");.scaleb(), arithmetic, and scientific input do the same):Negative values — the leading
-is counted as an integer digit, over-widening every negative by one precision digit, which becomes a hard failure at the boundary:Both the cast type and the bound literal come from the same value, so the server can't reconcile them — a perfectly valid
Decimalis rejected or silently nulled.Fix
Derive precision/scale from
Decimal.as_tuple()(sign, digits, exponent) — the exact numeric value — instead of the display string, so sign and exponent formatting no longer inflate the counts. Handles exponent notation, negatives, and sub-1 values uniformly.Tests
The six existing
test_calculate_decimal_cast_stringcases are unchanged and still pass. Added regression cases for exponent notation (Decimal("1500").normalize()→DECIMAL(4,0),Decimal("1e5")→DECIMAL(6,0)) and negatives (-9…9(38 digits) →DECIMAL(38,0),Decimal("-12.34")→DECIMAL(4,2)).tests/unit/test_parameters.pypasses (60).