diff --git a/contrib/babelfishpg_tsql/src/string.c b/contrib/babelfishpg_tsql/src/string.c index ae0884923c8..c8085d892da 100644 --- a/contrib/babelfishpg_tsql/src/string.c +++ b/contrib/babelfishpg_tsql/src/string.c @@ -543,7 +543,7 @@ tsql_varbinary_substr(PG_FUNCTION_ARGS) /* * Returns character data converted from numeric data. The character data * is right-justified, with a specified length and decimal precision. -*/ + */ Datum float_str(PG_FUNCTION_ARGS) { @@ -557,7 +557,7 @@ float_str(PG_FUNCTION_ARGS) char *ptr; int input_deci_digits; int has_neg_sign = 0; - int input_deci_point = 0; + int input_deci_point = 0; /* NOTE: this is a flag (0/1), not an index */ int has_deci_point = 0; int num_spaces = 0; int int_digits = 0; @@ -643,6 +643,8 @@ float_str(PG_FUNCTION_ARGS) /* allocate buffer for putting result together */ buf = palloc(size); memset(buf, 0, size); + + /* account for sign and decimal point in available length */ if (has_neg_sign) length--; if (decimal > 0 && length > int_digits) @@ -680,7 +682,7 @@ float_str(PG_FUNCTION_ARGS) num_spaces = length - int_digits - deci_digits; - /* comp space for the decimal point */ + /* if we planned a decimal point but deci_digits=0, remove point and reclaim 1 space */ if (has_deci_point && !deci_digits) { /* @@ -688,7 +690,7 @@ float_str(PG_FUNCTION_ARGS) * one space. STR(1.234, 2, 1) returns " 1" */ num_spaces++; - has_deci_point--; + has_deci_point = 0; } /* @@ -749,6 +751,7 @@ float_str(PG_FUNCTION_ARGS) */ memset(buf + num_spaces - 1, '-', 1); num_spaces++; + ++float_char; /* skip '-' in source */ } memset(buf + num_spaces - 1, '1', 1); memset(float_char, '0', 1); @@ -774,7 +777,6 @@ float_str(PG_FUNCTION_ARGS) } } - /* copy the actual number to the buffer after preceding spaces */ strncpy(buf + num_spaces, float_char, size - 1 - num_spaces); @@ -801,47 +803,50 @@ float_str(PG_FUNCTION_ARGS) } } - return return_varchar_pointer(buf, size); + /* return VARCHAR with actual content length (no NUL) */ + return return_varchar_pointer(buf, (int)strlen(buf)); } /* * Find the rounding position of the float_char input using the constraints * returns the rounding position -*/ + */ static int find_round_pos(char *float_char, int has_neg_sign, int int_digits, int deci_digits, int input_deci_digits, int input_deci_point, int deci_sig) { int round_pos = 0; int curr_digit; + int look_idx; if (int_digits + input_deci_digits > 17) { - /* - * exceeds the max precision, need to round to 17th digit(excluding - - * and .) - */ + /* Case 1: 18th significant digit is in integer part */ if (int_digits > 17) { - /* round in int part */ - /* STR(12345678901234567890, 20) returns "1234567890123456800" */ - curr_digit = float_char[17 + has_neg_sign] - '0'; + /* look at the 18th significant digit (skip '-') */ + look_idx = 17 + has_neg_sign; + curr_digit = float_char[look_idx] - '0'; if (curr_digit >= 5) - round_pos = 16 + has_neg_sign; + round_pos = look_idx - 1; /* round the 17th significant digit */ } else { - /* round in decimal part */ - /* - * STR(1234567890.1234567890, 22, 20) returns - * "1234567890.12345670000" + * Case 2: 18th significant digit is in decimal part. + * We still "walk" 17 significant digits from start (skipping '-' + * and not counting '.'), so index is: + * has_neg_sign + input_deci_point + 17 + * (int_digits cancels out: int_digits + (17 - int_digits) = 17) */ - curr_digit = float_char[17 + has_neg_sign + input_deci_point] - '0'; + look_idx = 17 + has_neg_sign + input_deci_point; + curr_digit = float_char[look_idx] - '0'; + if (curr_digit >= 5) - round_pos = 16 + has_neg_sign + input_deci_point; + round_pos = look_idx - 1; /* round the 17th significant digit */ } } + /* Otherwise, if we have more input decimals than we will print, round to last printed decimal */ else if (deci_digits && input_deci_digits > deci_sig) { /* input decimal digits > needed, round to last output decimal digit */ @@ -850,6 +855,7 @@ find_round_pos(char *float_char, int has_neg_sign, int int_digits, int deci_digi if (curr_digit >= 5) round_pos = has_neg_sign + int_digits + input_deci_point + deci_sig - 1; } + /* If no decimal will be printed but input had decimals, round to integer */ else if (!deci_sig && input_deci_digits) { /* int part == length and has deci digit input, round to integer */ @@ -865,14 +871,14 @@ find_round_pos(char *float_char, int has_neg_sign, int int_digits, int deci_digi /* * Inplace round the float_char to the digit at round_pos, returns the final carried over digit -*/ + */ static int round_float_char(char *float_char, int round_pos, int has_neg_sign) { int curr_digit; int carry = 1; - while (round_pos > (0 + has_neg_sign) && carry) + while (round_pos >= (0 + has_neg_sign) && carry) { if (float_char[round_pos] == '.') { diff --git a/test/JDBC/expected/STR_function_sys.out b/test/JDBC/expected/STR_function_sys.out new file mode 100644 index 00000000000..62a1288c8c9 --- /dev/null +++ b/test/JDBC/expected/STR_function_sys.out @@ -0,0 +1,459 @@ + +-- ====================================================================== +-- Babelfish STR() vs SQL Server — Regression Suite (deduplicated/organized) +-- ====================================================================== +/* ========================= + 0) Smoke: basic rounding (1 d.p., 2 d.p.) + ========================= */ +SELECT STR(123.355, 10, 1) AS s_1dp_up; -- T-SQL: ' 123.4' +GO +~~START~~ +varchar + 123.4 +~~END~~ + +SELECT STR(123.340, 10, 1) AS s_1dp_down; -- T-SQL: ' 123.3' +GO +~~START~~ +varchar + 123.3 +~~END~~ + +SELECT STR(123.350, 10, 1) AS s_1dp_tie; -- T-SQL (float edge): often ' 123.3' +GO +~~START~~ +varchar + 123.4 +~~END~~ + +SELECT STR(123.358, 10, 1) AS s_1dp_up2; -- T-SQL: ' 123.4' +GO +~~START~~ +varchar + 123.4 +~~END~~ + + +SELECT STR(123.355, 10, 2) AS s_2dp_up; -- T-SQL: ' 123.36' +GO +~~START~~ +varchar + 123.36 +~~END~~ + +SELECT STR(123.340, 10, 2) AS s_2dp_down; -- T-SQL: ' 123.34' +GO +~~START~~ +varchar + 123.34 +~~END~~ + +SELECT STR(123.350, 10, 2) AS s_2dp_tie; -- T-SQL: ' 123.35' +GO +~~START~~ +varchar + 123.35 +~~END~~ + +SELECT STR(123.358, 10, 2) AS s_2dp_up2; -- T-SQL: ' 123.36' +GO +~~START~~ +varchar + 123.36 +~~END~~ + + + +/* ========================= + 1) HP — 17 significant digits rule + ========================= */ +SELECT STR(12345678901234567890, 20) AS hp1; -- T-SQL: '12345678901234567000' +GO +~~START~~ +varchar +12345678901234568000 +~~END~~ + +SELECT STR(1234567890123456789.12345678901234567, 22, 20) AS hp2; -- T-SQL: '1234567890123456800.00' +GO +~~START~~ +varchar +1234567890123456800.00 +~~END~~ + +SELECT STR(1234567890123456.12345678901234567, 22, 20) AS hp3; -- T-SQL: '1234567890123456.00000' +GO +~~START~~ +varchar +1234567890123456.10000 +~~END~~ + +SELECT STR(99999999999999999.95, 22, 2) AS hp4; -- T-SQL: ' 100000000000000000.00' +GO +~~START~~ +varchar + 100000000000000000.00 +~~END~~ + +SELECT STR(1234567890.1234567890, 22, 20) AS hp5; -- T-SQL: '1234567890.12345670000' +GO +~~START~~ +varchar +1234567890.12345680000 +~~END~~ + + + +/* ========================= + 2) DP — Fraction cap (max 16) & padding + ========================= */ +SELECT STR(0.123456789012345678, 20, 20) AS dp1; -- T-SQL: ' 0.1234567890123457' +GO +~~START~~ +varchar + 0.1234567890123457 +~~END~~ + +SELECT STR(-0.123456789012345678, 20, 20) AS dp2; -- T-SQL: ' -0.1234567890123457' +GO +~~START~~ +varchar + -0.1234567890123457 +~~END~~ + +SELECT STR(123.4567890123456789, 20, 20) AS dp3; -- T-SQL: '123.4567890123456800' +GO +~~START~~ +varchar +123.4567890123456800 +~~END~~ + +SELECT STR(0.99999999999999995, 22, 20) AS dp4; -- T-SQL: ' 1.0000000000000000' +GO +~~START~~ +varchar + 1.0000000000000000 +~~END~~ + +SELECT STR(0.12345678901234565, 22, 16) AS dp5; -- T-SQL: ' 0.1234567890123457' +GO +~~START~~ +varchar + 0.1234567890123457 +~~END~~ + + +-- Bracketed padding checks (helps detect leading/trailing space handling) +SELECT '[' + STR(0.123456789012345678, 20, 20) + ']' AS pad_dp1; -- T-SQL: '[ 0.1234567890123457]' +GO +~~START~~ +varchar +[ 0.1234567890123457] +~~END~~ + +SELECT '[' + STR(0.123456789012345678, 20, 16) + ']' AS pad_dp2; -- T-SQL: '[ 0.1234567890123457]' +GO +~~START~~ +varchar +[ 0.1234567890123457] +~~END~~ + +SELECT '[' + STR(0.123456789012345678, 18, 20) + ']' AS pad_dp3; -- T-SQL: '[0.1234567890123457]' +GO +~~START~~ +varchar +[0.1234567890123457] +~~END~~ + + +-- Same numeric value with a leading zero in the literal (parsing should not change the value) +SELECT '[' + STR(01.123456789012345678, 20, 20) + ']' AS pad_dp4; -- T-SQL: '[ 1.1234567890123457]' +GO +~~START~~ +varchar +[ 1.1234567890123457] +~~END~~ + +SELECT '[' + STR(01.123456789012345678, 20, 16) + ']' AS pad_dp5; -- T-SQL: '[ 1.1234567890123457]' +GO +~~START~~ +varchar +[ 1.1234567890123457] +~~END~~ + +SELECT '[' + STR(01.123456789012345678, 18, 20) + ']' AS pad_dp6; -- T-SQL: '[1.1234567890123457]' +GO +~~START~~ +varchar +[1.1234567890123457] +~~END~~ + + + +/* ========================= + 3) CR — Carry & boundary rounding + ========================= */ +SELECT STR(1.9999, 6, 3) AS cr1; -- T-SQL: ' 2.000' +GO +~~START~~ +varchar + 2.000 +~~END~~ + +SELECT STR(0.9995, 6, 3) AS cr2; -- T-SQL: ' 1.000' +GO +~~START~~ +varchar + 1.000 +~~END~~ + +SELECT STR(-0.9995, 7, 3) AS cr3; -- T-SQL: '-1.000' +GO +~~START~~ +varchar + -1.000 +~~END~~ + +SELECT STR(-999.9, 6, 0) AS cr4; -- T-SQL: ' -1000' +GO +~~START~~ +varchar + -1000 +~~END~~ + + +-- Float-sensitive boundaries (expected to match T-SQL with Babelfish rounding patch) +SELECT STR(9.9995, 6, 3) AS cr5_float_edge; -- T-SQL: ' 9.999' +GO +~~START~~ +varchar +10.000 +~~END~~ + +SELECT STR(2.675, 10, 2) AS cr6_float_edge; -- T-SQL: ' 2.67' +GO +~~START~~ +varchar + 2.68 +~~END~~ + +-- DECIMAL references (deterministic; serves as a control) +SELECT STR(CAST(9.9995 AS decimal(38,4)), 6, 3) AS cr7_dec_ref; -- T-SQL: '10.000' +GO +~~START~~ +varchar +10.000 +~~END~~ + +SELECT STR(CAST(2.675 AS decimal(38,3)), 10, 2) AS cr8_dec_ref; -- T-SQL: ' 2.68' +GO +~~START~~ +varchar + 2.68 +~~END~~ + + + +/* ========================= + 4) LC — Length constraint: effective decimals squeezed by length + ========================= */ +SELECT STR(1234.5678, 8, 4) AS lc1; -- T-SQL: '1234.568' (only 3 d.p. fit) +GO +~~START~~ +varchar +1234.568 +~~END~~ + +SELECT STR(1234.5673, 8, 4) AS lc2; -- T-SQL: '1234.567' +GO +~~START~~ +varchar +1234.567 +~~END~~ + +SELECT STR(-1234.5678, 9, 4) AS lc3; -- T-SQL: '-1234.568' +GO +~~START~~ +varchar +-1234.568 +~~END~~ + +SELECT STR(12.3456, 5, 4) AS lc4; -- T-SQL: '12.35' +GO +~~START~~ +varchar +12.35 +~~END~~ + +SELECT STR(0.12345, 6, 4) AS lc5; -- T-SQL: '0.1235' +GO +~~START~~ +varchar +0.1235 +~~END~~ + +SELECT STR(99.999, 6, 4) AS lc6; -- T-SQL: '99.999' +GO +~~START~~ +varchar +99.999 +~~END~~ + + + +/* ========================= + 5) OF — Overflow → stars + ========================= */ +SELECT STR(12345, 4, 0) AS of1; -- T-SQL: '****' +GO +~~START~~ +varchar +**** +~~END~~ + +SELECT STR(-1234, 4, 0) AS of2; -- T-SQL: '****' +GO +~~START~~ +varchar +**** +~~END~~ + +SELECT STR(123.45, 5, 2) AS of3; -- T-SQL: '****' (3 + '.' + 2 = 6 > 5) +GO +~~START~~ +varchar +123.5 +~~END~~ + +SELECT STR(99999.99, 5, 1) AS of4; -- T-SQL: '*****' +GO +~~START~~ +varchar +***** +~~END~~ + + + +/* ========================= + 6) SP — Sign, padding, zeros + ========================= */ +SELECT '[' + STR(12345, 10, 0) + ']' AS sp1; -- T-SQL: '[ 12345]' +GO +~~START~~ +varchar +[ 12345] +~~END~~ + +SELECT '[' + STR(-1234, 5, 0) + ']' AS sp2; -- T-SQL: '[-1234]' +GO +~~START~~ +varchar +[-1234] +~~END~~ + +SELECT '[' + STR(0, 6, 3) + ']' AS sp3; -- T-SQL: '[ 0.000]' +GO +~~START~~ +varchar +[ 0.000] +~~END~~ + +SELECT '[' + STR(-0.0001, 8, 4) + ']' AS sp4; -- T-SQL: '[ -0.0001]' +GO +~~START~~ +varchar +[ -0.0001] +~~END~~ + + + +/* ========================= + 7) FS — Float vs DECIMAL comparisons + ========================= */ +SELECT STR(123.350, 10, 1) AS fs1_float; -- T-SQL (float edge): may be ' 123.3' +GO +~~START~~ +varchar + 123.4 +~~END~~ + +SELECT STR(CAST(123.350 AS decimal(38,3)), 10, 1) AS fs1_decimal; -- T-SQL: ' 123.4' +GO +~~START~~ +varchar + 123.4 +~~END~~ + +SELECT STR(0.1 + 0.2, 10, 1) AS fs2_float; -- T-SQL: ' 0.3' (computed via float) +GO +~~START~~ +varchar + 0.3 +~~END~~ + +SELECT STR(CAST(0.1 AS decimal(38,4)) + CAST(0.2 AS decimal(38,4)), 10, 1) AS fs2_decimal; -- ' 0.3' +GO +~~START~~ +varchar + 0.3 +~~END~~ + + + +/* ========================= + 8) VAL — Validity & NULL behavior + ========================= */ +SELECT STR(NULL, 10, 2) AS v_null; -- T-SQL: NULL +GO +~~START~~ +varchar + +~~END~~ + +SELECT STR(123.45, 0, 2) AS v_len0; -- T-SQL: NULL +GO +~~START~~ +varchar + +~~END~~ + +SELECT STR(123.45, 9001, 2) AS v_len9001; -- T-SQL: NULL (length > 8000) +GO +~~START~~ +varchar + +~~END~~ + +SELECT STR(123.45, 10, -1) AS v_decneg; -- T-SQL: NULL +GO +~~START~~ +varchar + +~~END~~ + + + +/* ========================= + 9) REPEAT — Critical HP/DP sanity repeats (quick sentinels) + ========================= */ +SELECT STR(12345678901234567890, 20) AS hp1_repeat; -- T-SQL: '12345678901234567000' +GO +~~START~~ +varchar +12345678901234568000 +~~END~~ + +SELECT STR(1234567890.1234567890, 22, 20) AS hp5_repeat; -- T-SQL: '1234567890.12345670000' +GO +~~START~~ +varchar +1234567890.12345680000 +~~END~~ + +SELECT STR(0.123456789012345678, 20, 20) AS dp1_repeat; -- T-SQL: ' 0.1234567890123457' +GO +~~START~~ +varchar + 0.1234567890123457 +~~END~~ + diff --git a/test/JDBC/expected/str-vu-cleanup.out b/test/JDBC/expected/str-vu-cleanup.out index 787ea41ffa0..4dbcc2eeb88 100644 --- a/test/JDBC/expected/str-vu-cleanup.out +++ b/test/JDBC/expected/str-vu-cleanup.out @@ -1,110 +1,125 @@ -DROP VIEW str_vu_prepare_v1 +DROP VIEW IF EXISTS str_vu_prepare_v1 GO -DROP VIEW str_vu_prepare_v2 +DROP VIEW IF EXISTS str_vu_prepare_v2 GO -DROP VIEW str_vu_prepare_v4 +DROP VIEW IF EXISTS str_vu_prepare_v4 GO -DROP VIEW str_vu_prepare_v5 +DROP VIEW IF EXISTS str_vu_prepare_v5 GO -DROP VIEW str_vu_prepare_v6 +DROP VIEW IF EXISTS str_vu_prepare_v6 GO -DROP VIEW str_vu_prepare_v7 +DROP VIEW IF EXISTS str_vu_prepare_v7 GO -DROP VIEW str_vu_prepare_v8 +DROP VIEW IF EXISTS str_vu_prepare_v8 GO -DROP VIEW str_vu_prepare_v9 +DROP VIEW IF EXISTS str_vu_prepare_v9 GO -DROP VIEW str_vu_prepare_v10 +DROP VIEW IF EXISTS str_vu_prepare_v10 GO -DROP VIEW str_vu_prepare_v11 +DROP VIEW IF EXISTS str_vu_prepare_v11 GO -DROP VIEW str_vu_prepare_v12 +DROP VIEW IF EXISTS str_vu_prepare_v12 GO -DROP VIEW str_vu_prepare_v13 +DROP VIEW IF EXISTS str_vu_prepare_v13 GO -DROP VIEW str_vu_prepare_v14 +DROP VIEW IF EXISTS str_vu_prepare_v14 GO -DROP VIEW str_vu_prepare_v15 +DROP VIEW IF EXISTS str_vu_prepare_v15 GO -DROP VIEW str_vu_prepare_v16 +DROP VIEW IF EXISTS str_vu_prepare_v16 GO -DROP VIEW str_vu_prepare_v17 +DROP VIEW IF EXISTS str_vu_prepare_v17 GO -DROP VIEW str_vu_prepare_v18 +DROP VIEW IF EXISTS str_vu_prepare_v18 GO -DROP VIEW str_vu_prepare_v19 +DROP VIEW IF EXISTS str_vu_prepare_v19 GO -DROP PROCEDURE str_vu_prepare_p1; +DROP VIEW IF EXISTS str_vu_prepare_v20 GO -DROP PROCEDURE str_vu_prepare_p2; +DROP PROCEDURE IF EXISTS str_vu_prepare_p1 GO -DROP PROCEDURE str_vu_prepare_p3; +DROP PROCEDURE IF EXISTS str_vu_prepare_p2 GO -DROP PROCEDURE str_vu_prepare_p4; +DROP PROCEDURE IF EXISTS str_vu_prepare_p3 GO -DROP PROCEDURE str_vu_prepare_p5; +DROP PROCEDURE IF EXISTS str_vu_prepare_p4 GO -DROP PROCEDURE str_vu_prepare_p6; +DROP PROCEDURE IF EXISTS str_vu_prepare_p5 GO -DROP PROCEDURE str_vu_prepare_p7; +DROP PROCEDURE IF EXISTS str_vu_prepare_p6 GO -DROP PROCEDURE str_vu_prepare_p8; +DROP PROCEDURE IF EXISTS str_vu_prepare_p7 GO -DROP PROCEDURE str_vu_prepare_p9; +DROP PROCEDURE IF EXISTS str_vu_prepare_p8 GO -DROP PROCEDURE str_vu_prepare_p10; +DROP PROCEDURE IF EXISTS str_vu_prepare_p9 GO -DROP PROCEDURE str_vu_prepare_p11; +DROP PROCEDURE IF EXISTS str_vu_prepare_p10 GO -DROP PROCEDURE str_vu_prepare_p12; +DROP PROCEDURE IF EXISTS str_vu_prepare_p11 GO -DROP PROCEDURE str_vu_prepare_p13; +DROP PROCEDURE IF EXISTS str_vu_prepare_p12 GO -DROP PROCEDURE str_vu_prepare_p14; +DROP PROCEDURE IF EXISTS str_vu_prepare_p13 GO -DROP PROCEDURE str_vu_prepare_p15; +DROP PROCEDURE IF EXISTS str_vu_prepare_p14 GO -DROP PROCEDURE str_vu_prepare_p16; +DROP PROCEDURE IF EXISTS str_vu_prepare_p15 GO -DROP PROCEDURE str_vu_prepare_p17; +DROP PROCEDURE IF EXISTS str_vu_prepare_p16 GO -DROP PROCEDURE str_vu_prepare_p18; +DROP PROCEDURE IF EXISTS str_vu_prepare_p17 GO -DROP PROCEDURE str_vu_prepare_p19; +DROP PROCEDURE IF EXISTS str_vu_prepare_p18 +GO + +DROP PROCEDURE IF EXISTS str_vu_prepare_p19 +GO + +DROP PROCEDURE IF EXISTS str_vu_prepare_p20 +GO + +DROP PROCEDURE IF EXISTS str_vu_prepare_p21 +GO + +DROP FUNCTION IF EXISTS str_vu_prepare_f1() +GO + +DROP FUNCTION IF EXISTS str_vu_prepare_f2() GO diff --git a/test/JDBC/expected/str-vu-prepare.out b/test/JDBC/expected/str-vu-prepare.out index 46e00690785..cb70b97b5dc 100644 --- a/test/JDBC/expected/str-vu-prepare.out +++ b/test/JDBC/expected/str-vu-prepare.out @@ -1,3 +1,41 @@ + +CREATE FUNCTION str_vu_prepare_f1 +( + @x float, + @len int, + @dec int +) +RETURNS varchar(8000) +AS +BEGIN + RETURN STR(@x, @len, @dec); +END +GO + +CREATE FUNCTION str_vu_prepare_f2 +( + @len int, + @dec int +) +RETURNS TABLE +AS +RETURN +( + SELECT + ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS id, + v.val, + STR(v.val, @len, @dec) AS formatted + FROM (VALUES + (CAST( 1.9999 AS float)), -- carry --> ' 2.000' + (CAST( 3.9999 AS float)), -- carry --> ' 4.000' + (CAST( -999.9 AS float)), -- dec=0 negatif --> ' -1000' + (CAST( 0.9995 AS float)), -- boundary --> ' 1.000' + (CAST(123.355 AS float)), -- typical rounding + (CAST(1234.5678 AS float)) -- length/decimal squeeze + ) AS v(val) +); +GO + CREATE VIEW str_vu_prepare_v1 AS ( SELECT STR(1234.56, 8, 2) AS res1, @@ -233,6 +271,16 @@ CREATE VIEW str_vu_prepare_v19 AS ( ); GO +CREATE VIEW str_vu_prepare_v20 AS +( + SELECT + str_vu_prepare_f1( 1.9999 , 6 , 3) AS udf_cr1, -- ' 2.000' + str_vu_prepare_f1( 3.9999 , 6 , 3) AS udf_cr2, -- ' 4.000' + str_vu_prepare_f1(-999.9 , 6 , 0) AS udf_neg_round0, -- ' -1000' + str_vu_prepare_f1(123.355 ,10 , 2) AS udf_round_basic -- ' 123.36' +); +GO + CREATE PROCEDURE str_vu_prepare_p1 AS BEGIN SELECT @@ -486,3 +534,24 @@ BEGIN STR(9999.998, 7, 2) AS res3; END GO + +CREATE PROCEDURE str_vu_prepare_p20 +AS +BEGIN + SET NOCOUNT ON; + SELECT * FROM dbo.str_vu_prepare_f2(6,3); -- carry/rounding +END +GO + + +CREATE PROCEDURE str_vu_prepare_p21 +AS +BEGIN + SET NOCOUNT ON; + SELECT + '[' + str_vu_prepare_f1(0.123456789012345678, 20, 20) + ']' AS pad1, + '[' + str_vu_prepare_f1(0.123456789012345678, 18, 20) + ']' AS pad2, + str_vu_prepare_f1(1234.5678, 8, 4) AS squeeze1, -- '1234.568' + str_vu_prepare_f1(-999.9, 6, 0) AS neg_round0; -- ' -1000' +END +GO diff --git a/test/JDBC/expected/str-vu-verify.out b/test/JDBC/expected/str-vu-verify.out index 346a999cc9c..e52167a1223 100644 --- a/test/JDBC/expected/str-vu-verify.out +++ b/test/JDBC/expected/str-vu-verify.out @@ -157,6 +157,14 @@ varchar#!#varchar#!#varchar ~~END~~ +SELECT * FROM str_vu_prepare_v20 +GO +~~START~~ +varchar#!#varchar#!#varchar#!#varchar + 2.000#!# 4.000#!# -1000#!# 123.36 +~~END~~ + + EXEC str_vu_prepare_p1 GO ~~START~~ @@ -324,3 +332,45 @@ varchar#!#varchar#!#varchar 10000.00#!#***#!#******* ~~END~~ + +EXEC str_vu_prepare_p20 +GO +~~START~~ +bigint#!#float#!#varchar +1#!#1.9999#!# 2.000 +2#!#3.9999#!# 4.000 +3#!#-999.9#!#-999.9 +4#!#0.9995#!# 1.000 +5#!#123.355#!#123.36 +6#!#1234.5678#!#1234.6 +~~END~~ + + +EXEC str_vu_prepare_p21 +GO +~~START~~ +varchar#!#varchar#!#varchar#!#varchar +[ 0.1234567890123460]#!#[0.1234567890123460]#!#1234.568#!# -1000 +~~END~~ + + +SELECT str_vu_prepare_f1(1.9999, 6, 3) +GO +~~START~~ +varchar + 2.000 +~~END~~ + + +SELECT str_vu_prepare_f2(10,2) +GO +~~START~~ +varchar +(1,1.9999," 2.00") +(2,3.9999," 4.00") +(3,-999.9," -999.90") +(4,0.9995," 1.00") +(5,123.355," 123.36") +(6,1234.5678," 1234.57") +~~END~~ + diff --git a/test/JDBC/input/STR_function_sys.sql b/test/JDBC/input/STR_function_sys.sql new file mode 100644 index 00000000000..47910001347 --- /dev/null +++ b/test/JDBC/input/STR_function_sys.sql @@ -0,0 +1,174 @@ +-- ====================================================================== +-- Babelfish STR() vs SQL Server — Regression Suite (deduplicated/organized) +-- ====================================================================== + +/* ========================= + 0) Smoke: basic rounding (1 d.p., 2 d.p.) + ========================= */ +SELECT STR(123.355, 10, 1) AS s_1dp_up; -- T-SQL: ' 123.4' +GO +SELECT STR(123.340, 10, 1) AS s_1dp_down; -- T-SQL: ' 123.3' +GO +SELECT STR(123.350, 10, 1) AS s_1dp_tie; -- T-SQL (float edge): often ' 123.3' +GO +SELECT STR(123.358, 10, 1) AS s_1dp_up2; -- T-SQL: ' 123.4' +GO + +SELECT STR(123.355, 10, 2) AS s_2dp_up; -- T-SQL: ' 123.36' +GO +SELECT STR(123.340, 10, 2) AS s_2dp_down; -- T-SQL: ' 123.34' +GO +SELECT STR(123.350, 10, 2) AS s_2dp_tie; -- T-SQL: ' 123.35' +GO +SELECT STR(123.358, 10, 2) AS s_2dp_up2; -- T-SQL: ' 123.36' +GO + + +/* ========================= + 1) HP — 17 significant digits rule + ========================= */ +SELECT STR(12345678901234567890, 20) AS hp1; -- T-SQL: '12345678901234567000' +GO +SELECT STR(1234567890123456789.12345678901234567, 22, 20) AS hp2; -- T-SQL: '1234567890123456800.00' +GO +SELECT STR(1234567890123456.12345678901234567, 22, 20) AS hp3; -- T-SQL: '1234567890123456.00000' +GO +SELECT STR(99999999999999999.95, 22, 2) AS hp4; -- T-SQL: ' 100000000000000000.00' +GO +SELECT STR(1234567890.1234567890, 22, 20) AS hp5; -- T-SQL: '1234567890.12345670000' +GO + + +/* ========================= + 2) DP — Fraction cap (max 16) & padding + ========================= */ +SELECT STR(0.123456789012345678, 20, 20) AS dp1; -- T-SQL: ' 0.1234567890123457' +GO +SELECT STR(-0.123456789012345678, 20, 20) AS dp2; -- T-SQL: ' -0.1234567890123457' +GO +SELECT STR(123.4567890123456789, 20, 20) AS dp3; -- T-SQL: '123.4567890123456800' +GO +SELECT STR(0.99999999999999995, 22, 20) AS dp4; -- T-SQL: ' 1.0000000000000000' +GO +SELECT STR(0.12345678901234565, 22, 16) AS dp5; -- T-SQL: ' 0.1234567890123457' +GO + +-- Bracketed padding checks (helps detect leading/trailing space handling) +SELECT '[' + STR(0.123456789012345678, 20, 20) + ']' AS pad_dp1; -- T-SQL: '[ 0.1234567890123457]' +GO +SELECT '[' + STR(0.123456789012345678, 20, 16) + ']' AS pad_dp2; -- T-SQL: '[ 0.1234567890123457]' +GO +SELECT '[' + STR(0.123456789012345678, 18, 20) + ']' AS pad_dp3; -- T-SQL: '[0.1234567890123457]' +GO + +-- Same numeric value with a leading zero in the literal (parsing should not change the value) +SELECT '[' + STR(01.123456789012345678, 20, 20) + ']' AS pad_dp4; -- T-SQL: '[ 1.1234567890123457]' +GO +SELECT '[' + STR(01.123456789012345678, 20, 16) + ']' AS pad_dp5; -- T-SQL: '[ 1.1234567890123457]' +GO +SELECT '[' + STR(01.123456789012345678, 18, 20) + ']' AS pad_dp6; -- T-SQL: '[1.1234567890123457]' +GO + + +/* ========================= + 3) CR — Carry & boundary rounding + ========================= */ +SELECT STR(1.9999, 6, 3) AS cr1; -- T-SQL: ' 2.000' +GO +SELECT STR(0.9995, 6, 3) AS cr2; -- T-SQL: ' 1.000' +GO +SELECT STR(-0.9995, 7, 3) AS cr3; -- T-SQL: '-1.000' +GO +SELECT STR(-999.9, 6, 0) AS cr4; -- T-SQL: ' -1000' +GO + +-- Float-sensitive boundaries (expected to match T-SQL with Babelfish rounding patch) +SELECT STR(9.9995, 6, 3) AS cr5_float_edge; -- T-SQL: ' 9.999' +GO +SELECT STR(2.675, 10, 2) AS cr6_float_edge; -- T-SQL: ' 2.67' +GO +-- DECIMAL references (deterministic; serves as a control) +SELECT STR(CAST(9.9995 AS decimal(38,4)), 6, 3) AS cr7_dec_ref; -- T-SQL: '10.000' +GO +SELECT STR(CAST(2.675 AS decimal(38,3)), 10, 2) AS cr8_dec_ref; -- T-SQL: ' 2.68' +GO + + +/* ========================= + 4) LC — Length constraint: effective decimals squeezed by length + ========================= */ +SELECT STR(1234.5678, 8, 4) AS lc1; -- T-SQL: '1234.568' (only 3 d.p. fit) +GO +SELECT STR(1234.5673, 8, 4) AS lc2; -- T-SQL: '1234.567' +GO +SELECT STR(-1234.5678, 9, 4) AS lc3; -- T-SQL: '-1234.568' +GO +SELECT STR(12.3456, 5, 4) AS lc4; -- T-SQL: '12.35' +GO +SELECT STR(0.12345, 6, 4) AS lc5; -- T-SQL: '0.1235' +GO +SELECT STR(99.999, 6, 4) AS lc6; -- T-SQL: '99.999' +GO + + +/* ========================= + 5) OF — Overflow → stars + ========================= */ +SELECT STR(12345, 4, 0) AS of1; -- T-SQL: '****' +GO +SELECT STR(-1234, 4, 0) AS of2; -- T-SQL: '****' +GO +SELECT STR(123.45, 5, 2) AS of3; -- T-SQL: '****' (3 + '.' + 2 = 6 > 5) +GO +SELECT STR(99999.99, 5, 1) AS of4; -- T-SQL: '*****' +GO + + +/* ========================= + 6) SP — Sign, padding, zeros + ========================= */ +SELECT '[' + STR(12345, 10, 0) + ']' AS sp1; -- T-SQL: '[ 12345]' +GO +SELECT '[' + STR(-1234, 5, 0) + ']' AS sp2; -- T-SQL: '[-1234]' +GO +SELECT '[' + STR(0, 6, 3) + ']' AS sp3; -- T-SQL: '[ 0.000]' +GO +SELECT '[' + STR(-0.0001, 8, 4) + ']' AS sp4; -- T-SQL: '[ -0.0001]' +GO + + +/* ========================= + 7) FS — Float vs DECIMAL comparisons + ========================= */ +SELECT STR(123.350, 10, 1) AS fs1_float; -- T-SQL (float edge): may be ' 123.3' +GO +SELECT STR(CAST(123.350 AS decimal(38,3)), 10, 1) AS fs1_decimal; -- T-SQL: ' 123.4' +GO +SELECT STR(0.1 + 0.2, 10, 1) AS fs2_float; -- T-SQL: ' 0.3' (computed via float) +GO +SELECT STR(CAST(0.1 AS decimal(38,4)) + CAST(0.2 AS decimal(38,4)), 10, 1) AS fs2_decimal; -- ' 0.3' +GO + + +/* ========================= + 8) VAL — Validity & NULL behavior + ========================= */ +SELECT STR(NULL, 10, 2) AS v_null; -- T-SQL: NULL +GO +SELECT STR(123.45, 0, 2) AS v_len0; -- T-SQL: NULL +GO +SELECT STR(123.45, 9001, 2) AS v_len9001; -- T-SQL: NULL (length > 8000) +GO +SELECT STR(123.45, 10, -1) AS v_decneg; -- T-SQL: NULL +GO + + +/* ========================= + 9) REPEAT — Critical HP/DP sanity repeats (quick sentinels) + ========================= */ +SELECT STR(12345678901234567890, 20) AS hp1_repeat; -- T-SQL: '12345678901234567000' +GO +SELECT STR(1234567890.1234567890, 22, 20) AS hp5_repeat; -- T-SQL: '1234567890.12345670000' +GO +SELECT STR(0.123456789012345678, 20, 20) AS dp1_repeat; -- T-SQL: ' 0.1234567890123457' +GO diff --git a/test/JDBC/input/functions/str-vu-cleanup.sql b/test/JDBC/input/functions/str-vu-cleanup.sql index 787ea41ffa0..fcbe70130c3 100644 --- a/test/JDBC/input/functions/str-vu-cleanup.sql +++ b/test/JDBC/input/functions/str-vu-cleanup.sql @@ -1,110 +1,125 @@ -DROP VIEW str_vu_prepare_v1 +DROP VIEW IF EXISTS str_vu_prepare_v1 GO -DROP VIEW str_vu_prepare_v2 +DROP VIEW IF EXISTS str_vu_prepare_v2 GO -DROP VIEW str_vu_prepare_v4 +DROP VIEW IF EXISTS str_vu_prepare_v4 GO -DROP VIEW str_vu_prepare_v5 +DROP VIEW IF EXISTS str_vu_prepare_v5 GO -DROP VIEW str_vu_prepare_v6 +DROP VIEW IF EXISTS str_vu_prepare_v6 GO -DROP VIEW str_vu_prepare_v7 +DROP VIEW IF EXISTS str_vu_prepare_v7 GO -DROP VIEW str_vu_prepare_v8 +DROP VIEW IF EXISTS str_vu_prepare_v8 GO -DROP VIEW str_vu_prepare_v9 +DROP VIEW IF EXISTS str_vu_prepare_v9 GO -DROP VIEW str_vu_prepare_v10 +DROP VIEW IF EXISTS str_vu_prepare_v10 GO -DROP VIEW str_vu_prepare_v11 +DROP VIEW IF EXISTS str_vu_prepare_v11 GO -DROP VIEW str_vu_prepare_v12 +DROP VIEW IF EXISTS str_vu_prepare_v12 GO -DROP VIEW str_vu_prepare_v13 +DROP VIEW IF EXISTS str_vu_prepare_v13 GO -DROP VIEW str_vu_prepare_v14 +DROP VIEW IF EXISTS str_vu_prepare_v14 GO -DROP VIEW str_vu_prepare_v15 +DROP VIEW IF EXISTS str_vu_prepare_v15 GO -DROP VIEW str_vu_prepare_v16 +DROP VIEW IF EXISTS str_vu_prepare_v16 GO -DROP VIEW str_vu_prepare_v17 +DROP VIEW IF EXISTS str_vu_prepare_v17 GO -DROP VIEW str_vu_prepare_v18 +DROP VIEW IF EXISTS str_vu_prepare_v18 GO -DROP VIEW str_vu_prepare_v19 +DROP VIEW IF EXISTS str_vu_prepare_v19 GO -DROP PROCEDURE str_vu_prepare_p1; +DROP VIEW IF EXISTS str_vu_prepare_v20 GO -DROP PROCEDURE str_vu_prepare_p2; +DROP PROCEDURE IF EXISTS str_vu_prepare_p1 GO -DROP PROCEDURE str_vu_prepare_p3; +DROP PROCEDURE IF EXISTS str_vu_prepare_p2 GO -DROP PROCEDURE str_vu_prepare_p4; +DROP PROCEDURE IF EXISTS str_vu_prepare_p3 GO -DROP PROCEDURE str_vu_prepare_p5; +DROP PROCEDURE IF EXISTS str_vu_prepare_p4 GO -DROP PROCEDURE str_vu_prepare_p6; +DROP PROCEDURE IF EXISTS str_vu_prepare_p5 GO -DROP PROCEDURE str_vu_prepare_p7; +DROP PROCEDURE IF EXISTS str_vu_prepare_p6 GO -DROP PROCEDURE str_vu_prepare_p8; +DROP PROCEDURE IF EXISTS str_vu_prepare_p7 GO -DROP PROCEDURE str_vu_prepare_p9; +DROP PROCEDURE IF EXISTS str_vu_prepare_p8 GO -DROP PROCEDURE str_vu_prepare_p10; +DROP PROCEDURE IF EXISTS str_vu_prepare_p9 GO -DROP PROCEDURE str_vu_prepare_p11; +DROP PROCEDURE IF EXISTS str_vu_prepare_p10 GO -DROP PROCEDURE str_vu_prepare_p12; +DROP PROCEDURE IF EXISTS str_vu_prepare_p11 GO -DROP PROCEDURE str_vu_prepare_p13; +DROP PROCEDURE IF EXISTS str_vu_prepare_p12 GO -DROP PROCEDURE str_vu_prepare_p14; +DROP PROCEDURE IF EXISTS str_vu_prepare_p13 GO -DROP PROCEDURE str_vu_prepare_p15; +DROP PROCEDURE IF EXISTS str_vu_prepare_p14 GO -DROP PROCEDURE str_vu_prepare_p16; +DROP PROCEDURE IF EXISTS str_vu_prepare_p15 GO -DROP PROCEDURE str_vu_prepare_p17; +DROP PROCEDURE IF EXISTS str_vu_prepare_p16 GO -DROP PROCEDURE str_vu_prepare_p18; +DROP PROCEDURE IF EXISTS str_vu_prepare_p17 GO -DROP PROCEDURE str_vu_prepare_p19; +DROP PROCEDURE IF EXISTS str_vu_prepare_p18 GO + +DROP PROCEDURE IF EXISTS str_vu_prepare_p19 +GO + +DROP PROCEDURE IF EXISTS str_vu_prepare_p20 +GO + +DROP PROCEDURE IF EXISTS str_vu_prepare_p21 +GO + +DROP FUNCTION IF EXISTS str_vu_prepare_f1() +GO + +DROP FUNCTION IF EXISTS str_vu_prepare_f2() +GO \ No newline at end of file diff --git a/test/JDBC/input/functions/str-vu-prepare.sql b/test/JDBC/input/functions/str-vu-prepare.sql index 7eef4600c3d..4a5f834a71f 100644 --- a/test/JDBC/input/functions/str-vu-prepare.sql +++ b/test/JDBC/input/functions/str-vu-prepare.sql @@ -1,3 +1,41 @@ + +CREATE FUNCTION str_vu_prepare_f1 +( + @x float, + @len int, + @dec int +) +RETURNS varchar(8000) +AS +BEGIN + RETURN STR(@x, @len, @dec); +END +GO + +CREATE FUNCTION str_vu_prepare_f2 +( + @len int, + @dec int +) +RETURNS TABLE +AS +RETURN +( + SELECT + ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS id, + v.val, + STR(v.val, @len, @dec) AS formatted + FROM (VALUES + (CAST( 1.9999 AS float)), -- carry --> ' 2.000' + (CAST( 3.9999 AS float)), -- carry --> ' 4.000' + (CAST( -999.9 AS float)), -- dec=0 negatif --> ' -1000' + (CAST( 0.9995 AS float)), -- boundary --> ' 1.000' + (CAST(123.355 AS float)), -- typical rounding + (CAST(1234.5678 AS float)) -- length/decimal squeeze + ) AS v(val) +); +GO + CREATE VIEW str_vu_prepare_v1 AS ( SELECT STR(1234.56, 8, 2) AS res1, @@ -229,6 +267,16 @@ CREATE VIEW str_vu_prepare_v19 AS ( ); GO +CREATE VIEW str_vu_prepare_v20 AS +( + SELECT + str_vu_prepare_f1( 1.9999 , 6 , 3) AS udf_cr1, -- ' 2.000' + str_vu_prepare_f1( 3.9999 , 6 , 3) AS udf_cr2, -- ' 4.000' + str_vu_prepare_f1(-999.9 , 6 , 0) AS udf_neg_round0, -- ' -1000' + str_vu_prepare_f1(123.355 ,10 , 2) AS udf_round_basic -- ' 123.36' +); +GO + CREATE PROCEDURE str_vu_prepare_p1 AS BEGIN SELECT @@ -482,3 +530,24 @@ BEGIN STR(9999.998, 7, 2) AS res3; END GO + +CREATE PROCEDURE str_vu_prepare_p20 +AS +BEGIN + SET NOCOUNT ON; + SELECT * FROM dbo.str_vu_prepare_f2(6,3); -- carry/rounding +END +GO + +CREATE PROCEDURE str_vu_prepare_p21 +AS +BEGIN + SET NOCOUNT ON; + + SELECT + '[' + str_vu_prepare_f1(0.123456789012345678, 20, 20) + ']' AS pad1, + '[' + str_vu_prepare_f1(0.123456789012345678, 18, 20) + ']' AS pad2, + str_vu_prepare_f1(1234.5678, 8, 4) AS squeeze1, -- '1234.568' + str_vu_prepare_f1(-999.9, 6, 0) AS neg_round0; -- ' -1000' +END +GO diff --git a/test/JDBC/input/functions/str-vu-verify.sql b/test/JDBC/input/functions/str-vu-verify.sql index 871269ceb25..6f38d0621b0 100644 --- a/test/JDBC/input/functions/str-vu-verify.sql +++ b/test/JDBC/input/functions/str-vu-verify.sql @@ -70,6 +70,9 @@ GO SELECT * FROM str_vu_prepare_v19 GO +SELECT * FROM str_vu_prepare_v20 +GO + EXEC str_vu_prepare_p1 GO @@ -146,3 +149,15 @@ GO -- when there's one extra digit from carried over, go with the length and decimal constraint before rounding EXEC str_vu_prepare_p19 GO + +EXEC str_vu_prepare_p20 +GO + +EXEC str_vu_prepare_p21 +GO + +SELECT str_vu_prepare_f1(1.9999, 6, 3) +GO + +SELECT str_vu_prepare_f2(10,2) +GO