This is an automated email from the ASF dual-hosted git repository. stigahuang pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/impala.git
commit 266d7ec51c2de00e796d6c29ed0f5eb0972e74c1 Author: Riza Suminto <[email protected]> AuthorDate: Wed Mar 20 10:31:49 2024 -0700 IMPALA-4545: Simplify test dimension in test_decimal_casting.py This patch splits precision and scale as independent dimensions and then constrains them to yield a valid decimal type. With this split, core exploration will have the same test dimension as pairwise exploration, while exhaustive exploration still permutes all possible decimal types. Also did minor refactoring to reduce test skipping and pass flake8. After this patch, core exploration has 214 test items and exhaustive exploration has 12312 test items. Before, they were 408 and 12464 respectively. Testing: - Pass test_decimal_casting.py in core and exhaustive exploration. Change-Id: Ibe269e08a955097ad9e924d5d64b42438ad15be2 Reviewed-on: http://gerrit.cloudera.org:8080/21174 Reviewed-by: Impala Public Jenkins <[email protected]> Tested-by: Impala Public Jenkins <[email protected]> --- tests/query_test/test_decimal_casting.py | 77 ++++++++++++++++++++------------ 1 file changed, 48 insertions(+), 29 deletions(-) diff --git a/tests/query_test/test_decimal_casting.py b/tests/query_test/test_decimal_casting.py index e0435a2c1..43e587094 100644 --- a/tests/query_test/test_decimal_casting.py +++ b/tests/query_test/test_decimal_casting.py @@ -19,16 +19,15 @@ # from __future__ import absolute_import, division, print_function from builtins import range -import pytest from decimal import Decimal, getcontext, ROUND_DOWN, ROUND_HALF_UP -from allpairspy import AllPairs as all_pairs from random import randint from tests.common.impala_test_suite import ImpalaTestSuite from tests.common.test_dimensions import create_exec_option_dimension_from_dict from tests.common.test_vector import ImpalaTestDimension, ImpalaTestMatrix -class TestDecimalCasting(ImpalaTestSuite): + +class TestDecimalCastingBase(ImpalaTestSuite): """Test Suite to verify that casting to Decimal works. Specifically, this test suite ensures that: @@ -37,15 +36,6 @@ class TestDecimalCasting(ImpalaTestSuite): - max/min/NULL/0 can be expressed with their respective decimal types. - TODO: Add cases for cast from float/double to decimal types. """ - DECIMAL_TYPES_MAP = { - # All possible decimal types. - # (0 < precision <= 38 && 0 <= scale <= 38 && scale <= precision) - 'exhaustive': [(p, s) for p in range(1, 39) for s in range(0, p + 1)], - # Core only deals with precision 6,16,26 (different integer types) - 'core': [(p, s) for p in [6, 16, 26] for s in range(0, p + 1)], - # mimics test_vectors.py and takes a subset of all decimal types - 'pairwise': all_pairs([(p, s) for p in range(1, 39) for s in range(0, p + 1)]) - } # We can cast for numerics or string types. CAST_FROM = ['string', 'number'] # Set the default precision to 38 to operate on decimal values. @@ -60,14 +50,27 @@ class TestDecimalCasting(ImpalaTestSuite): @classmethod def add_test_dimensions(cls): cls.ImpalaTestMatrix = ImpalaTestMatrix() - cls.ImpalaTestMatrix.add_dimension(ImpalaTestDimension('decimal_type', - *TestDecimalCasting.DECIMAL_TYPES_MAP[cls.exploration_strategy()])) + + # Core/pairwise only deals with precision 6, 16, 26 (different integer types) + precision = [6, 16, 26] + # (0 <= scale <= 38) + scale = list(range(0, 39)) + if cls.exploration_strategy() == 'exhaustive': + # (0 < precision <= 38) + precision = list(range(1, 39)) + + cls.ImpalaTestMatrix.add_dimension(ImpalaTestDimension('precision', *precision)) + cls.ImpalaTestMatrix.add_dimension(ImpalaTestDimension('scale', *scale)) cls.ImpalaTestMatrix.add_dimension( ImpalaTestDimension('cast_from', *TestDecimalCasting.CAST_FROM)) cls.ImpalaTestMatrix.add_dimension(create_exec_option_dimension_from_dict( - {'decimal_v2': ['false','true']})) + {'decimal_v2': ['false', 'true']})) cls.iterations = 1 + # Scale must be less than or equal to precision. + cls.ImpalaTestMatrix.add_constraint( + lambda v: v.get_value('scale') <= v.get_value('precision')) + def _gen_decimal_val(self, precision, scale): """Generates a Decimal object with the exact number of digits as the precision.""" # Generates numeric string which has as many digits as the precision. @@ -76,10 +79,10 @@ class TestDecimalCasting(ImpalaTestSuite): if scale != 0: num = "{0}.{1}".format(num[:-scale], num[precision - scale:]) # Convert the generated decimal string into a Decimal object and return a -ive/+ive # version of it with equal probability. - return Decimal(num) if randint(0,1) else Decimal("-{0}".format(num)) + return Decimal(num) if randint(0, 1) else Decimal("-{0}".format(num)) def _assert_decimal_result(self, cast, actual, expected): - assert expected == actual, "Cast: {0}, Expected: {1}, Actual: {2}".format(cast,\ + assert expected == actual, "Cast: {0}, Expected: {1}, Actual: {2}".format(cast, expected, actual) def _normalize_cast_expr(self, decimal_val, precision, cast_from): @@ -88,6 +91,13 @@ class TestDecimalCasting(ImpalaTestSuite): else: return "select cast({0} as Decimal({1},{2}))" + +class TestDecimalCasting(TestDecimalCastingBase): + + @classmethod + def add_test_dimensions(cls): + super(TestDecimalCasting, cls).add_test_dimensions() + def test_min_max_zero_null(self, vector): """Sanity test at limits. @@ -96,7 +106,8 @@ class TestDecimalCasting(ImpalaTestSuite): - 0 is expressible in all decimal types. - NULL is expressible in all decimal types """ - precision, scale = vector.get_value('decimal_type') + precision = vector.get_value('precision') + scale = vector.get_value('scale') dec_max = Decimal('{0}.{1}'.format('9' * (precision - scale), '9' * scale)) # Multiplying large values eith -1 can produce an overflow. dec_min = Decimal('-{0}'.format(str(dec_max))) @@ -108,7 +119,7 @@ class TestDecimalCasting(ImpalaTestSuite): res = Decimal(self.execute_scalar(cast.format(dec_min, precision, scale))) self._assert_decimal_result(cast, res, dec_min) # Test zero - res = Decimal(self.execute_scalar(\ + res = Decimal(self.execute_scalar( cast.format(TestDecimalCasting.DECIMAL_ZERO, precision, scale))) self._assert_decimal_result(cast, res, TestDecimalCasting.DECIMAL_ZERO) # Test NULL @@ -119,9 +130,8 @@ class TestDecimalCasting(ImpalaTestSuite): def test_exact(self, vector): """Test to verify that an exact representation of the desired Decimal type is maintained.""" - precision, scale = vector.get_value('decimal_type') - if vector.get_value('cast_from') == 'decimal': - pytest.skip("Casting between the same decimal type isn't interesting") + precision = vector.get_value('precision') + scale = vector.get_value('scale') for i in range(self.iterations): val = self._gen_decimal_val(precision, scale) cast = self._normalize_cast_expr(val, precision, vector.get_value('cast_from'))\ @@ -132,32 +142,41 @@ class TestDecimalCasting(ImpalaTestSuite): def test_overflow(self, vector): """Test to verify that we always return NULL when trying to cast a number with greater precision that its intended decimal type""" - precision, scale = vector.get_value('decimal_type') + precision = vector.get_value('precision') + scale = vector.get_value('scale') for i in range(self.iterations): # Generate a decimal with a larger precision than the one we're casting to. from_precision = randint(precision + 1, 39) val = self._gen_decimal_val(from_precision, scale) - cast = self._normalize_cast_expr(val, from_precision,\ + cast = self._normalize_cast_expr(val, from_precision, vector.get_value('cast_from')).format(val, precision, scale) - res = self.execute_query_expect_failure(self.client, cast) + self.execute_query_expect_failure(self.client, cast) + + +class TestDecimalCastingUnderflow(TestDecimalCastingBase): + + @classmethod + def add_test_dimensions(cls): + super(TestDecimalCastingUnderflow, cls).add_test_dimensions() + # "Cannot underflow scale when precision and scale are equal" + cls.ImpalaTestMatrix.add_constraint( + lambda v: v.get_value('scale') < v.get_value('precision')) def test_underflow(self, vector): """Test to verify that we truncate when the scale of the number being cast is higher than the target decimal type (with no change in precision). """ - precision, scale = vector.get_value('decimal_type') + precision = vector.get_value('precision') + scale = vector.get_value('scale') is_decimal_v2 = vector.get_value('exec_option')['decimal_v2'] == 'true' cast_from = vector.get_value('cast_from') - if precision == scale: - pytest.skip("Cannot underflow scale when precision and scale are equal") for i in range(self.iterations): from_scale = randint(scale + 1, precision) val = self._gen_decimal_val(precision, from_scale) cast = self._normalize_cast_expr(val, precision, cast_from)\ .format(val, precision, scale) res = Decimal(self.execute_scalar(cast, vector.get_value('exec_option'))) - # TODO: Remove check for cast_from once string to decimal is supported in decimal_v2. if is_decimal_v2: expected_val = val.quantize(Decimal('0e-%s' % scale), rounding=ROUND_HALF_UP) else:
