From 074e873aa53e7c2ee0f94c47492a2820ee23fc93 Mon Sep 17 00:00:00 2001 From: Lenin Mookiah Date: Sun, 18 Oct 2026 11:53:48 -0700 Subject: [PATCH] fix(parameters): honor explicit decimal precision and scale Render declared decimal types in SQL precision,scale order and preserve an explicit zero scale. Correct the parameter descriptions and document the compatibility change for callers that followed the reversed descriptions. Add regression coverage through actual parameter binding, preserving automatic sizing and the string payload. This addresses part of issue #901 only. Validated in Linux aarch64 Docker: 1250 ordinary unit tests and 351 subtests, a separate real-kernel test, formatting, type checks and package builds pass. Four existing tests remain skipped; no live Databricks/e2e tests were run. Signed-off-by: Lenin Mookiah --- CHANGELOG.md | 1 + src/databricks/sql/parameters/native.py | 8 ++++---- tests/unit/test_parameters.py | 15 +++++++++++++++ 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a5e33347..543001c0f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # Release History # Unreleased +- Correct explicit `DecimalParameter` precision and scale ordering and honor a declared zero scale. Callers that followed the previously reversed parameter descriptions must use `precision` for total digits and `scale` for fractional digits. Explicit zero precision is now sent as declared instead of silently using automatic sizing. - Add the `escape_catalog_name` connection parameter (default `False`) to opt in to escaping unescaped `_` in metadata catalog names on Thrift and kernel connections. Existing escapes and `%` wildcards are preserved. # 4.6.0 (2026-09-24) diff --git a/src/databricks/sql/parameters/native.py b/src/databricks/sql/parameters/native.py index d0d811940..01339cdac 100644 --- a/src/databricks/sql/parameters/native.py +++ b/src/databricks/sql/parameters/native.py @@ -615,9 +615,9 @@ def __init__( The `name` argument to this function would be `my_param`. :scale: - The maximum precision (total number of digits) of the number between 1 and 38. + The number of digits to the right of the decimal point, from 0 to precision. :precision: - The number of digits to the right of the decimal point. + The maximum precision (total number of digits) of the number between 1 and 38. """ self.value: decimal.Decimal = value self.name = name @@ -638,8 +638,8 @@ def valid_scale_and_precision(self): return False def _cast_expr(self): - if self.scale and self.precision: - return self.CAST_EXPR.format(self.scale, self.precision) + if self.scale is not None and self.precision is not None: + return self.CAST_EXPR.format(self.precision, self.scale) else: return self.calculate_decimal_cast_string(self.value) diff --git a/tests/unit/test_parameters.py b/tests/unit/test_parameters.py index 0588eb499..66b0e48e9 100644 --- a/tests/unit/test_parameters.py +++ b/tests/unit/test_parameters.py @@ -107,6 +107,21 @@ def test_calculate_decimal_cast_string(value, expected): assert p._cast_expr() == expected +@pytest.mark.parametrize( + "value,precision,scale,expected", + ( + (Decimal("123.45"), 10, 2, "DECIMAL(10,2)"), + (Decimal("123"), 10, 0, "DECIMAL(10,0)"), + (Decimal("123.45"), None, None, "DECIMAL(5,2)"), + ), +) +def test_decimal_parameter_explicit_type(value, precision, scale, expected): + parameter = DecimalParameter(value, precision=precision, scale=scale) + bound = parameter.as_tspark_param(named=False) + assert bound.type == expected + assert bound.value.stringValue == str(value) + + class Primitive(Enum): """These are the inferrable types. This Enum is used for parametrized tests."""