From 2f39a95a3f22158b392f5e9aa62bbfa777a44b65 Mon Sep 17 00:00:00 2001 From: Ayush-061 Date: Mon, 15 Jun 2026 08:14:08 +0000 Subject: [PATCH] COnvert failure --- contrib/babelfishpg_common/sql/coerce.sql | 6 +- .../babelfish_common_helper--6.1.0--6.2.0.sql | 21 + .../sql/sys_function_helpers.sql | 6 +- .../babelfishpg_tsql/sql/sys_functions.sql | 6 +- .../babelfishpg_tsql--6.1.0--6.2.0.sql | 617 ++++++++++++++++++ test/JDBC/expected/BABEL-6813-vu-cleanup.out | 29 + test/JDBC/expected/BABEL-6813-vu-prepare.out | 141 ++++ test/JDBC/expected/BABEL-6813-vu-verify.out | 113 ++++ test/JDBC/input/BABEL-6813-vu-cleanup.sql | 29 + test/JDBC/input/BABEL-6813-vu-prepare.sql | 103 +++ test/JDBC/input/BABEL-6813-vu-verify.sql | 42 ++ test/JDBC/upgrade/latest/schedule | 1 + 12 files changed, 1105 insertions(+), 9 deletions(-) create mode 100644 test/JDBC/expected/BABEL-6813-vu-cleanup.out create mode 100644 test/JDBC/expected/BABEL-6813-vu-prepare.out create mode 100644 test/JDBC/expected/BABEL-6813-vu-verify.out create mode 100644 test/JDBC/input/BABEL-6813-vu-cleanup.sql create mode 100644 test/JDBC/input/BABEL-6813-vu-prepare.sql create mode 100644 test/JDBC/input/BABEL-6813-vu-verify.sql diff --git a/contrib/babelfishpg_common/sql/coerce.sql b/contrib/babelfishpg_common/sql/coerce.sql index 94d8135baa2..3b058432698 100644 --- a/contrib/babelfishpg_common/sql/coerce.sql +++ b/contrib/babelfishpg_common/sql/coerce.sql @@ -68,7 +68,7 @@ $$ LANGUAGE plpgsql STABLE; CREATE OR REPLACE FUNCTION sys._trunc_numeric_to_int8(In arg numeric) RETURNS INT8 AS $$ BEGIN - RETURN CAST(trunc(arg) AS INT8); + RETURN pg_catalog.int8(trunc(arg)); END; $$ LANGUAGE plpgsql STABLE; @@ -76,7 +76,7 @@ $$ LANGUAGE plpgsql STABLE; CREATE OR REPLACE FUNCTION sys._trunc_numeric_to_int4(In arg numeric) RETURNS INT4 AS $$ BEGIN - RETURN CAST(trunc(arg) AS INT4); + RETURN pg_catalog.int4(trunc(arg)); END; $$ LANGUAGE plpgsql STABLE; @@ -84,7 +84,7 @@ $$ LANGUAGE plpgsql STABLE; CREATE OR REPLACE FUNCTION sys._trunc_numeric_to_int2(In arg numeric) RETURNS INT2 AS $$ BEGIN - RETURN CAST(trunc(arg) AS INT2); + RETURN pg_catalog.int2(trunc(arg)); END; $$ LANGUAGE plpgsql STABLE; diff --git a/contrib/babelfishpg_common/sql/upgrades/babelfish_common_helper--6.1.0--6.2.0.sql b/contrib/babelfishpg_common/sql/upgrades/babelfish_common_helper--6.1.0--6.2.0.sql index 53770cd5095..0fc8bb5b5b3 100644 --- a/contrib/babelfishpg_common/sql/upgrades/babelfish_common_helper--6.1.0--6.2.0.sql +++ b/contrib/babelfishpg_common/sql/upgrades/babelfish_common_helper--6.1.0--6.2.0.sql @@ -4,3 +4,24 @@ -- complain if script is sourced in psql, rather than via ALTER EXTENSION \echo Use "ALTER EXTENSION ""babelfishpg_common"" UPDATE TO '6.2.0'" to load this file. \quit + +CREATE OR REPLACE FUNCTION sys._trunc_numeric_to_int8(In arg numeric) +RETURNS INT8 AS $$ +BEGIN + RETURN pg_catalog.int8(trunc(arg)); +END; +$$ LANGUAGE plpgsql STABLE; + +CREATE OR REPLACE FUNCTION sys._trunc_numeric_to_int4(In arg numeric) +RETURNS INT4 AS $$ +BEGIN + RETURN pg_catalog.int4(trunc(arg)); +END; +$$ LANGUAGE plpgsql STABLE; + +CREATE OR REPLACE FUNCTION sys._trunc_numeric_to_int2(In arg numeric) +RETURNS INT2 AS $$ +BEGIN + RETURN pg_catalog.int2(trunc(arg)); +END; +$$ LANGUAGE plpgsql STABLE; diff --git a/contrib/babelfishpg_tsql/sql/sys_function_helpers.sql b/contrib/babelfishpg_tsql/sql/sys_function_helpers.sql index 955e30ff1cd..b76d42359c0 100644 --- a/contrib/babelfishpg_tsql/sql/sys_function_helpers.sql +++ b/contrib/babelfishpg_tsql/sql/sys_function_helpers.sql @@ -252,7 +252,7 @@ EXCEPTION WHEN invalid_text_representation THEN GET STACKED DIAGNOSTICS v_err_message = MESSAGE_TEXT; - v_err_message := substring(pg_catalog.lower(v_err_message), 'integer\:\s\"(.*)\"'); + v_err_message := substring(pg_catalog.lower(v_err_message) FROM 'integer\:\s\"(.*)\"' :: TEXT); RAISE USING MESSAGE := pg_catalog.format('Error while trying to convert "%s" value to SMALLINT (or INTEGER) data type.', v_err_message), @@ -545,7 +545,7 @@ EXCEPTION WHEN invalid_text_representation THEN GET STACKED DIAGNOSTICS v_err_message = MESSAGE_TEXT; - v_err_message := substring(pg_catalog.lower(v_err_message), 'integer\:\s\"(.*)\"'); + v_err_message := substring(pg_catalog.lower(v_err_message) FROM 'integer\:\s\"(.*)\"' :: TEXT); RAISE USING MESSAGE := pg_catalog.format('Error while trying to convert "%s" value to SMALLINT data type.', v_err_message), @@ -10984,7 +10984,7 @@ BEGIN END IF; IF (pg_catalog.lower(p_datatype) LIKE '%varchar%(%' OR pg_catalog.lower(p_datatype) LIKE '%char%(%') THEN - v_varchar_length := substring(p_datatype COLLATE "C" FROM '\(([0-9]+|MAX)\)'); + v_varchar_length := substring(p_datatype COLLATE "C" FROM '\(([0-9]+|MAX)\)' :: TEXT); IF (v_varchar_length IS NOT NULL AND v_varchar_length <> 'MAX' AND char_length(v_result) > v_varchar_length::SMALLINT) THEN RAISE USING MESSAGE := pg_catalog.format('There is insufficient result space to convert a money value to varchar.'), DETAIL := 'The converted money value exceeds the specified varchar length.', diff --git a/contrib/babelfishpg_tsql/sql/sys_functions.sql b/contrib/babelfishpg_tsql/sql/sys_functions.sql index 3019427ae81..b335a6e8979 100644 --- a/contrib/babelfishpg_tsql/sql/sys_functions.sql +++ b/contrib/babelfishpg_tsql/sql/sys_functions.sql @@ -5140,9 +5140,9 @@ BEGIN RAISE EXCEPTION 'The datepart ''weekday'' is not supported by date function datetrunc for data type ''%''.', date_arg_datatype; ELSIF date_arg_datatype = 'date'::regtype AND datepart IN ('hour', 'minute', 'second', 'millisecond', 'microsecond') THEN RAISE EXCEPTION 'The datepart ''%'' is not supported by date function datetrunc for data type ''date''.', datepart; - ELSIF date_arg_datatype = 'datetime'::regtype AND datepart IN ('microsecond') THEN + ELSIF date_arg_datatype = 'sys.datetime'::regtype AND datepart IN ('microsecond') THEN RAISE EXCEPTION 'The datepart ''%'' is not supported by date function datetrunc for data type ''datetime''.', datepart; - ELSIF date_arg_datatype = 'smalldatetime'::regtype AND datepart IN ('millisecond', 'microsecond') THEN + ELSIF date_arg_datatype = 'sys.smalldatetime'::regtype AND datepart IN ('millisecond', 'microsecond') THEN RAISE EXCEPTION 'The datepart ''%'' is not supported by date function datetrunc for data type ''smalldatetime''.', datepart; ELSIF date_arg_datatype = 'time'::regtype THEN IF datepart IN ('year', 'quarter', 'month', 'doy', 'day', 'week', 'tsql_week') THEN @@ -5150,7 +5150,7 @@ BEGIN END IF; -- Limitation in determining if the specified fractional scale (if provided any) for time datatype is -- insufficient to support provided datepart (millisecond, microsecond) value - ELSIF date_arg_datatype IN ('datetime2'::regtype, 'datetimeoffset'::regtype) THEN + ELSIF date_arg_datatype IN ('sys.datetime2'::regtype, 'sys.datetimeoffset'::regtype) THEN -- Limitation in determining if the specified fractional scale (if provided any) for the above datatype is -- insufficient to support for provided datepart (millisecond, microsecond) value END IF; diff --git a/contrib/babelfishpg_tsql/sql/upgrades/babelfishpg_tsql--6.1.0--6.2.0.sql b/contrib/babelfishpg_tsql/sql/upgrades/babelfishpg_tsql--6.1.0--6.2.0.sql index bac6acc2315..039dd6075ce 100644 --- a/contrib/babelfishpg_tsql/sql/upgrades/babelfishpg_tsql--6.1.0--6.2.0.sql +++ b/contrib/babelfishpg_tsql/sql/upgrades/babelfishpg_tsql--6.1.0--6.2.0.sql @@ -208,6 +208,623 @@ CREATE OR REPLACE AGGREGATE sys.tsql_select_for_xml_text_agg( FINALFUNC = tsql_query_to_xml_text_ffunc ); +CREATE OR REPLACE FUNCTION sys.DATETRUNC(IN datepart PG_CATALOG.TEXT, IN date ANYELEMENT) RETURNS ANYELEMENT AS +$body$ +DECLARE + days_offset INT; + v_day INT; + result_date timestamp; + input_expr_timestamp timestamp; + date_arg_datatype regtype; + offset_string PG_CATALOG.TEXT; + datefirst_value INT; +BEGIN + BEGIN + /* perform input validation */ + date_arg_datatype := pg_typeof(date); + IF datepart NOT IN ('year', 'quarter', 'month', 'week', 'tsql_week', 'hour', 'minute', 'second', 'millisecond', 'microsecond', + 'doy', 'day', 'nanosecond', 'tzoffset') THEN + RAISE EXCEPTION '''%'' is not a recognized datetrunc option.', datepart; + ELSIF date_arg_datatype NOT IN ('date'::regtype, 'time'::regtype, 'sys.datetime'::regtype, 'sys.datetime2'::regtype, + 'sys.datetimeoffset'::regtype, 'sys.smalldatetime'::regtype) THEN + RAISE EXCEPTION 'Argument data type ''%'' is invalid for argument 2 of datetrunc function.', date_arg_datatype; + ELSIF datepart IN ('nanosecond', 'tzoffset') THEN + RAISE EXCEPTION 'The datepart ''%'' is not supported by date function datetrunc for data type ''%''.',datepart, date_arg_datatype; + ELSIF datepart IN ('dow') THEN + RAISE EXCEPTION 'The datepart ''weekday'' is not supported by date function datetrunc for data type ''%''.', date_arg_datatype; + ELSIF date_arg_datatype = 'date'::regtype AND datepart IN ('hour', 'minute', 'second', 'millisecond', 'microsecond') THEN + RAISE EXCEPTION 'The datepart ''%'' is not supported by date function datetrunc for data type ''date''.', datepart; + ELSIF date_arg_datatype = 'sys.datetime'::regtype AND datepart IN ('microsecond') THEN + RAISE EXCEPTION 'The datepart ''%'' is not supported by date function datetrunc for data type ''datetime''.', datepart; + ELSIF date_arg_datatype = 'sys.smalldatetime'::regtype AND datepart IN ('millisecond', 'microsecond') THEN + RAISE EXCEPTION 'The datepart ''%'' is not supported by date function datetrunc for data type ''smalldatetime''.', datepart; + ELSIF date_arg_datatype = 'time'::regtype THEN + IF datepart IN ('year', 'quarter', 'month', 'doy', 'day', 'week', 'tsql_week') THEN + RAISE EXCEPTION 'The datepart ''%'' is not supported by date function datetrunc for data type ''time''.', datepart; + END IF; + -- Limitation in determining if the specified fractional scale (if provided any) for time datatype is + -- insufficient to support provided datepart (millisecond, microsecond) value + ELSIF date_arg_datatype IN ('sys.datetime2'::regtype, 'sys.datetimeoffset'::regtype) THEN + -- Limitation in determining if the specified fractional scale (if provided any) for the above datatype is + -- insufficient to support for provided datepart (millisecond, microsecond) value + END IF; + + /* input validation is complete, proceed with result calculation. */ + IF date_arg_datatype = 'time'::regtype THEN + RETURN date_trunc(datepart, date); + ELSE + input_expr_timestamp = date::timestamp; + -- preserving offset_string value in the case of datetimeoffset datatype before converting it to timestamps + IF date_arg_datatype = 'sys.datetimeoffset'::regtype THEN + offset_string = PG_CATALOG.RIGHT(date::PG_CATALOG.TEXT, 6); + input_expr_timestamp := PG_CATALOG.LEFT(date::PG_CATALOG.TEXT, -6)::timestamp; + END IF; + CASE + WHEN datepart IN ('year', 'quarter', 'month', 'week', 'hour', 'minute', 'second', 'millisecond', 'microsecond') THEN + result_date := date_trunc(datepart, input_expr_timestamp); + WHEN datepart IN ('doy', 'day') THEN + result_date := date_trunc('day', input_expr_timestamp); + WHEN datepart IN ('tsql_week') THEN + -- sql server datepart 'iso_week' is similar to postgres 'week' datepart + -- handle sql server datepart 'week' here based on the value of set variable 'DATEFIRST' + v_day := EXTRACT(dow from input_expr_timestamp)::INT; + datefirst_value := current_setting('babelfishpg_tsql.datefirst')::INT; + IF v_day = 0 THEN + v_day := 7; + END IF; + result_date := date_trunc('day', input_expr_timestamp); + days_offset := (7 + v_day - datefirst_value)%7; + result_date := result_date - make_interval(days => days_offset); + END CASE; + -- concat offset_string to result_date in case of datetimeoffset before converting it to datetimeoffset datatype. + IF date_arg_datatype = 'sys.datetimeoffset'::regtype THEN + RETURN PG_CATALOG.concat(result_date, ' ', offset_string)::sys.datetimeoffset; + ELSE + RETURN result_date; + END IF; + END IF; + END; +END; +$body$ +LANGUAGE plpgsql STABLE; + +CREATE OR REPLACE FUNCTION sys.babelfish_conv_datetime_to_string(IN p_datatype TEXT, + IN p_src_datatype TEXT, + IN p_datetimeval TIMESTAMP(6) WITHOUT TIME ZONE, + IN p_style NUMERIC DEFAULT 0) +RETURNS TEXT +AS +$BODY$ +DECLARE + v_day VARCHAR COLLATE "C"; + v_hour VARCHAR COLLATE "C"; + v_month SMALLINT; + v_style SMALLINT; + v_scale SMALLINT; + v_resmask VARCHAR COLLATE "C"; + v_language VARCHAR COLLATE "C"; + v_datatype VARCHAR COLLATE "C"; + v_fseconds VARCHAR COLLATE "C"; + v_fractsep VARCHAR COLLATE "C"; + v_monthname VARCHAR COLLATE "C"; + v_resstring VARCHAR COLLATE "C"; + v_lengthexpr VARCHAR COLLATE "C"; + v_maxlength SMALLINT; + v_res_length SMALLINT; + v_err_message VARCHAR COLLATE "C"; + v_src_datatype VARCHAR COLLATE "C"; + v_res_datatype VARCHAR COLLATE "C"; + v_lang_metadata_json JSONB; + VARCHAR_MAX CONSTANT SMALLINT := 8000; + NVARCHAR_MAX CONSTANT SMALLINT := 4000; + CONVERSION_LANG CONSTANT VARCHAR COLLATE "C" := ''; + DATATYPE_REGEXP CONSTANT VARCHAR COLLATE "C" := '^\s*(CHAR|NCHAR|VARCHAR|NVARCHAR|CHARACTER VARYING)\s*$'; + SRCDATATYPE_MASK_REGEXP VARCHAR COLLATE "C" := '^(?:DATETIME|SMALLDATETIME|DATETIME2)\s*(?:\s*\(\s*(\d+)\s*\)\s*)?$'; + DATATYPE_MASK_REGEXP CONSTANT VARCHAR COLLATE "C" := '^\s*(?:CHAR|NCHAR|VARCHAR|NVARCHAR|CHARACTER VARYING)\s*\(\s*(\d+|MAX)\s*\)\s*$'; + v_datetimeval TIMESTAMP(6) WITHOUT TIME ZONE; +BEGIN + v_datatype := pg_catalog.upper(pg_catalog.btrim(p_datatype)); + v_src_datatype := pg_catalog.upper(pg_catalog.btrim(p_src_datatype)); + v_style := floor(p_style)::SMALLINT; + + IF (v_src_datatype ~* SRCDATATYPE_MASK_REGEXP) + THEN + v_scale := substring(v_src_datatype, SRCDATATYPE_MASK_REGEXP)::SMALLINT; + + v_src_datatype := PG_CATALOG.rtrim(split_part(v_src_datatype, '(', 1)); + + IF (v_src_datatype <> 'DATETIME2' AND v_scale IS NOT NULL) THEN + RAISE invalid_indicator_parameter_value; + ELSIF (v_scale NOT BETWEEN 0 AND 7) THEN + RAISE invalid_regular_expression; + END IF; + + v_scale := coalesce(v_scale, 7); + ELSE + RAISE most_specific_type_mismatch; + END IF; + + IF (scale(p_style) > 0) THEN + RAISE escape_character_conflict; + ELSIF (NOT ((v_style BETWEEN 0 AND 14) OR + (v_style BETWEEN 20 AND 25) OR + (v_style BETWEEN 100 AND 114) OR + v_style IN (120, 121, 126, 127, 130, 131))) + THEN + RAISE invalid_parameter_value; + END IF; + + IF (v_datatype ~* DATATYPE_MASK_REGEXP) THEN + v_res_datatype := PG_CATALOG.rtrim(split_part(v_datatype, '(', 1)); + + v_maxlength := CASE + WHEN (v_res_datatype IN ('CHAR', 'VARCHAR')) THEN VARCHAR_MAX + ELSE NVARCHAR_MAX + END; + + v_lengthexpr := substring(v_datatype, DATATYPE_MASK_REGEXP); + + IF (v_lengthexpr <> 'MAX' AND char_length(v_lengthexpr) > 4) + THEN + RAISE interval_field_overflow; + END IF; + + v_res_length := CASE v_lengthexpr + WHEN 'MAX' THEN v_maxlength + ELSE v_lengthexpr::SMALLINT + END; + ELSIF (v_datatype ~* DATATYPE_REGEXP) THEN + v_res_datatype := v_datatype; + ELSE + RAISE datatype_mismatch; + END IF; + + v_datetimeval := CASE + WHEN (v_style NOT IN (130, 131)) THEN p_datetimeval + ELSE sys.babelfish_conv_greg_to_hijri(p_datetimeval) + INTERVAL '1 day' + END; + + v_day := PG_CATALOG.ltrim(to_char(v_datetimeval, 'DD'), '0'); + v_hour := PG_CATALOG.ltrim(to_char(v_datetimeval, 'HH12'), '0'); + v_month := to_char(v_datetimeval, 'MM')::SMALLINT; + + v_language := CASE + WHEN (v_style IN (130, 131)) THEN 'HIJRI' + ELSE CONVERSION_LANG + END; + BEGIN + v_lang_metadata_json := sys.babelfish_get_lang_metadata_json(v_language); + EXCEPTION + WHEN OTHERS THEN + RAISE invalid_character_value_for_cast; + END; + + v_monthname := (v_lang_metadata_json -> 'months_shortnames') ->> v_month - 1; + + IF (v_src_datatype IN ('DATETIME', 'SMALLDATETIME')) THEN + v_fseconds := sys.babelfish_round_fractseconds(to_char(v_datetimeval, 'MS')); + + IF (v_fseconds::INTEGER = 1000) THEN + v_fseconds := '000'; + v_datetimeval := v_datetimeval + INTERVAL '1 second'; + ELSE + v_fseconds := lpad(v_fseconds, 3, '0'); + END IF; + ELSE + v_fseconds := sys.babelfish_get_microsecs_from_fractsecs_v2(to_char(v_datetimeval, 'US'), v_scale); + + -- Following condition will handle overflow of fractsecs + IF (v_fseconds::INTEGER < 0) THEN + v_fseconds := PG_CATALOG.repeat('0', LEAST(v_scale, 6)); + v_datetimeval := v_datetimeval + INTERVAL '1 second'; + END IF; + + IF (v_scale = 7) THEN + v_fseconds := pg_catalog.concat(v_fseconds, '0'); + END IF; + END IF; + + v_fractsep := CASE v_src_datatype + WHEN 'DATETIME2' THEN '.' + ELSE ':' + END; + + IF ((v_style = -1 AND v_src_datatype <> 'DATETIME2') OR + v_style IN (0, 9, 100, 109)) + THEN + v_resmask := pg_catalog.format('$mnme$ %s YYYY %s:MI%s', + lpad(v_day, 2, ' '), + lpad(v_hour, 2, ' '), + CASE + WHEN (v_style IN (-1, 0, 100)) THEN 'AM' + ELSE pg_catalog.format(':SS:%sAM', v_fseconds) + END); + ELSIF (v_style = 1) THEN + v_resmask := 'MM/DD/YY'; + ELSIF (v_style = 101) THEN + v_resmask := 'MM/DD/YYYY'; + ELSIF (v_style = 2) THEN + v_resmask := 'YY.MM.DD'; + ELSIF (v_style = 102) THEN + v_resmask := 'YYYY.MM.DD'; + ELSIF (v_style = 3) THEN + v_resmask := 'DD/MM/YY'; + ELSIF (v_style = 103) THEN + v_resmask := 'DD/MM/YYYY'; + ELSIF (v_style = 4) THEN + v_resmask := 'DD.MM.YY'; + ELSIF (v_style = 104) THEN + v_resmask := 'DD.MM.YYYY'; + ELSIF (v_style = 5) THEN + v_resmask := 'DD-MM-YY'; + ELSIF (v_style = 105) THEN + v_resmask := 'DD-MM-YYYY'; + ELSIF (v_style = 6) THEN + v_resmask := 'DD $mnme$ YY'; + ELSIF (v_style = 106) THEN + v_resmask := 'DD $mnme$ YYYY'; + ELSIF (v_style = 7) THEN + v_resmask := '$mnme$ DD, YY'; + ELSIF (v_style = 107) THEN + v_resmask := '$mnme$ DD, YYYY'; + ELSIF (v_style IN (8, 24, 108)) THEN + v_resmask := 'HH24:MI:SS'; + ELSIF (v_style = 10) THEN + v_resmask := 'MM-DD-YY'; + ELSIF (v_style = 110) THEN + v_resmask := 'MM-DD-YYYY'; + ELSIF (v_style = 11) THEN + v_resmask := 'YY/MM/DD'; + ELSIF (v_style = 111) THEN + v_resmask := 'YYYY/MM/DD'; + ELSIF (v_style = 12) THEN + v_resmask := 'YYMMDD'; + ELSIF (v_style = 112) THEN + v_resmask := 'YYYYMMDD'; + ELSIF (v_style IN (13, 113)) THEN + v_resmask := pg_catalog.format('DD $mnme$ YYYY HH24:MI:SS%s%s', v_fractsep, v_fseconds); + ELSIF (v_style IN (14, 114)) THEN + v_resmask := pg_catalog.format('HH24:MI:SS%s%s', v_fractsep, v_fseconds); + ELSIF (v_style IN (20, 120)) THEN + v_resmask := 'YYYY-MM-DD HH24:MI:SS'; + ELSIF ((v_style = -1 AND v_src_datatype = 'DATETIME2') OR + v_style IN (21, 25, 121)) + THEN + v_resmask := pg_catalog.format('YYYY-MM-DD HH24:MI:SS.%s', v_fseconds); + ELSIF (v_style = 22) THEN + v_resmask := pg_catalog.format('MM/DD/YY %s:MI:SS AM', lpad(v_hour, 2, ' ')); + ELSIF (v_style = 23) THEN + v_resmask := 'YYYY-MM-DD'; + ELSIF (v_style IN (126, 127)) THEN + v_resmask := CASE v_src_datatype + WHEN 'SMALLDATETIME' THEN 'YYYY-MM-DDT$rem$HH24:MI:SS' + ELSE pg_catalog.format('YYYY-MM-DDT$rem$HH24:MI:SS.%s', v_fseconds) + END; + ELSIF (v_style IN (130, 131)) THEN + v_resmask := pg_catalog.concat(CASE p_style + WHEN 131 THEN pg_catalog.format('%s/MM/YYYY ', lpad(v_day, 2, ' ')) + ELSE pg_catalog.format('%s $mnme$ YYYY ', lpad(v_day, 2, ' ')) + END, + pg_catalog.format('%s:MI:SS%s%sAM', lpad(v_hour, 2, ' '), v_fractsep, v_fseconds)); + END IF; + + v_resstring := to_char(v_datetimeval, v_resmask); + v_resstring := pg_catalog.replace(v_resstring, '$mnme$', v_monthname); + v_resstring := pg_catalog.replace(v_resstring, '$rem$', ''); + + v_resstring := substring(v_resstring, 1, coalesce(v_res_length, char_length(v_resstring))); + v_res_length := coalesce(v_res_length, + CASE v_res_datatype + WHEN 'CHAR' THEN 30 + ELSE 60 + END); + RETURN CASE + WHEN (v_res_datatype NOT IN ('CHAR', 'NCHAR')) THEN v_resstring + ELSE rpad(v_resstring, v_res_length, ' ') + END; +EXCEPTION + WHEN most_specific_type_mismatch THEN + RAISE USING MESSAGE := 'Source data type should be one of these values: ''DATETIME'', ''SMALLDATETIME'', ''DATETIME2'' or ''DATETIME2(n)''.', + DETAIL := 'Use of incorrect "src_datatype" parameter value during conversion process.', + HINT := 'Change "srcdatatype" parameter to the proper value and try again.'; + + WHEN invalid_regular_expression THEN + RAISE USING MESSAGE := pg_catalog.format('The source data type scale (%s) given to the convert specification exceeds the maximum allowable value (7).', + v_scale), + DETAIL := 'Use of incorrect scale value of source data type parameter during conversion process.', + HINT := 'Change scale component of source data type parameter to the allowable value and try again.'; + + WHEN invalid_indicator_parameter_value THEN + RAISE USING MESSAGE := pg_catalog.format('Invalid attributes specified for data type %s.', v_src_datatype), + DETAIL := 'Use of incorrect scale value, which is not corresponding to specified data type.', + HINT := 'Change data type scale component or select different data type and try again.'; + + WHEN escape_character_conflict THEN + RAISE USING MESSAGE := 'Argument data type NUMERIC is invalid for argument 4 of convert function.', + DETAIL := 'Use of incorrect "style" parameter value during conversion process.', + HINT := 'Change "style" parameter to the proper value and try again.'; + + WHEN invalid_parameter_value THEN + RAISE USING MESSAGE := pg_catalog.format('%s is not a valid style number when converting from %s to a character string.', + v_style, v_src_datatype), + DETAIL := 'Use of incorrect "style" parameter value during conversion process.', + HINT := 'Change "style" parameter to the proper value and try again.'; + + WHEN interval_field_overflow THEN + RAISE USING MESSAGE := pg_catalog.format('The size (%s) given to the convert specification ''%s'' exceeds the maximum allowed for any data type (%s).', + v_lengthexpr, pg_catalog.lower(v_res_datatype), v_maxlength), + DETAIL := 'Use of incorrect size value of data type parameter during conversion process.', + HINT := 'Change size component of data type parameter to the allowable value and try again.'; + + WHEN datatype_mismatch THEN + RAISE USING MESSAGE := 'Data type should be one of these values: ''CHAR(n|MAX)'', ''NCHAR(n|MAX)'', ''VARCHAR(n|MAX)'', ''NVARCHAR(n|MAX)''.', + DETAIL := 'Use of incorrect "datatype" parameter value during conversion process.', + HINT := 'Change "datatype" parameter to the proper value and try again.'; + + WHEN invalid_character_value_for_cast THEN + RAISE USING MESSAGE := pg_catalog.format('Invalid CONVERSION_LANG constant value - ''%s''. Allowed values are: ''English'', ''Deutsch'', etc.', + CONVERSION_LANG), + DETAIL := 'Compiled incorrect CONVERSION_LANG constant value in function''s body.', + HINT := 'Correct CONVERSION_LANG constant value in function''s body, recompile it and try again.'; + + WHEN invalid_text_representation THEN + GET STACKED DIAGNOSTICS v_err_message = MESSAGE_TEXT; + v_err_message := substring(pg_catalog.lower(v_err_message) FROM 'integer\:\s\"(.*)\"' :: TEXT); + + RAISE USING MESSAGE := pg_catalog.format('Error while trying to convert "%s" value to SMALLINT data type.', + v_err_message), + DETAIL := 'Supplied value contains illegal characters.', + HINT := 'Correct supplied value, remove all illegal characters.'; +END; +$BODY$ +LANGUAGE plpgsql +STABLE +RETURNS NULL ON NULL INPUT; + +CREATE OR REPLACE FUNCTION sys.babelfish_conv_date_to_string(IN p_datatype TEXT, + IN p_dateval DATE, + IN p_style NUMERIC DEFAULT 20) +RETURNS TEXT +AS +$BODY$ +DECLARE + v_day VARCHAR COLLATE "C"; + v_dateval DATE; + v_style SMALLINT; + v_month SMALLINT; + v_resmask VARCHAR COLLATE "C"; + v_datatype VARCHAR COLLATE "C"; + v_language VARCHAR COLLATE "C"; + v_monthname VARCHAR COLLATE "C"; + v_resstring VARCHAR COLLATE "C"; + v_lengthexpr VARCHAR COLLATE "C"; + v_maxlength SMALLINT; + v_res_length SMALLINT; + v_err_message VARCHAR COLLATE "C"; + v_res_datatype VARCHAR COLLATE "C"; + v_lang_metadata_json JSONB; + VARCHAR_MAX CONSTANT SMALLINT := 8000; + NVARCHAR_MAX CONSTANT SMALLINT := 4000; + CONVERSION_LANG CONSTANT VARCHAR COLLATE "C" := ''; + DATATYPE_REGEXP CONSTANT VARCHAR COLLATE "C" := '^\s*(CHAR|NCHAR|VARCHAR|NVARCHAR|CHARACTER VARYING)\s*$'; + DATATYPE_MASK_REGEXP CONSTANT VARCHAR COLLATE "C" := '^\s*(?:CHAR|NCHAR|VARCHAR|NVARCHAR|CHARACTER VARYING)\s*\(\s*(\d+|MAX)\s*\)\s*$'; +BEGIN + v_datatype := pg_catalog.upper(pg_catalog.btrim(p_datatype)); + v_style := floor(p_style)::SMALLINT; + + IF (scale(p_style) > 0) THEN + RAISE most_specific_type_mismatch; + ELSIF (NOT ((v_style BETWEEN 0 AND 13) OR + (v_style BETWEEN 20 AND 25) OR + (v_style BETWEEN 100 AND 113) OR + v_style IN (120, 121, 126, 127, 130, 131))) + THEN + RAISE invalid_parameter_value; + ELSIF (v_style IN (8, 24, 108)) THEN + RAISE invalid_datetime_format; + END IF; + + IF (v_datatype ~* DATATYPE_MASK_REGEXP) THEN + v_res_datatype := PG_CATALOG.rtrim(split_part(v_datatype, '(', 1)); + + v_maxlength := CASE + WHEN (v_res_datatype IN ('CHAR', 'VARCHAR')) THEN VARCHAR_MAX + ELSE NVARCHAR_MAX + END; + + v_lengthexpr := substring(v_datatype, DATATYPE_MASK_REGEXP); + + IF (v_lengthexpr <> 'MAX' AND char_length(v_lengthexpr) > 4) THEN + RAISE interval_field_overflow; + END IF; + + v_res_length := CASE v_lengthexpr + WHEN 'MAX' THEN v_maxlength + ELSE v_lengthexpr::SMALLINT + END; + ELSIF (v_datatype ~* DATATYPE_REGEXP) THEN + v_res_datatype := v_datatype; + ELSE + RAISE datatype_mismatch; + END IF; + + v_dateval := CASE + WHEN (v_style NOT IN (130, 131)) THEN p_dateval + ELSE sys.babelfish_conv_greg_to_hijri(p_dateval) + 1 + END; + + v_day := PG_CATALOG.ltrim(to_char(v_dateval, 'DD'), '0'); + v_month := to_char(v_dateval, 'MM')::SMALLINT; + + v_language := CASE + WHEN (v_style IN (130, 131)) THEN 'HIJRI' + ELSE CONVERSION_LANG + END; + RAISE NOTICE 'v_language=[%]', v_language; + BEGIN + v_lang_metadata_json := sys.babelfish_get_lang_metadata_json(v_language); + EXCEPTION + WHEN OTHERS THEN + RAISE invalid_character_value_for_cast; + END; + + v_monthname := (v_lang_metadata_json -> 'months_shortnames') ->> v_month - 1; + + v_resmask := CASE + WHEN (v_style IN (1, 22)) THEN 'MM/DD/YY' + WHEN (v_style = 101) THEN 'MM/DD/YYYY' + WHEN (v_style = 2) THEN 'YY.MM.DD' + WHEN (v_style = 102) THEN 'YYYY.MM.DD' + WHEN (v_style = 3) THEN 'DD/MM/YY' + WHEN (v_style = 103) THEN 'DD/MM/YYYY' + WHEN (v_style = 4) THEN 'DD.MM.YY' + WHEN (v_style = 104) THEN 'DD.MM.YYYY' + WHEN (v_style = 5) THEN 'DD-MM-YY' + WHEN (v_style = 105) THEN 'DD-MM-YYYY' + WHEN (v_style = 6) THEN 'DD $mnme$ YY' + WHEN (v_style IN (13, 106, 113)) THEN 'DD $mnme$ YYYY' + WHEN (v_style = 7) THEN '$mnme$ DD, YY' + WHEN (v_style = 107) THEN '$mnme$ DD, YYYY' + WHEN (v_style = 10) THEN 'MM-DD-YY' + WHEN (v_style = 110) THEN 'MM-DD-YYYY' + WHEN (v_style = 11) THEN 'YY/MM/DD' + WHEN (v_style = 111) THEN 'YYYY/MM/DD' + WHEN (v_style = 12) THEN 'YYMMDD' + WHEN (v_style = 112) THEN 'YYYYMMDD' + WHEN (v_style IN (20, 21, 23, 25, 120, 121, 126, 127)) THEN 'YYYY-MM-DD' + WHEN (v_style = 130) THEN 'DD $mnme$ YYYY' + WHEN (v_style = 131) THEN pg_catalog.format('%s/MM/YYYY', lpad(v_day, 2, ' ')) + WHEN (v_style IN (0, 9, 100, 109)) THEN pg_catalog.format('$mnme$ %s YYYY', lpad(v_day, 2, ' ')) + END; + + v_resstring := to_char(v_dateval, v_resmask); + v_resstring := pg_catalog.replace(v_resstring, '$mnme$', v_monthname); + v_resstring := substring(v_resstring, 1, coalesce(v_res_length, char_length(v_resstring))); + v_res_length := coalesce(v_res_length, + CASE v_res_datatype + WHEN 'CHAR' THEN 30 + ELSE 60 + END); + RETURN CASE + WHEN (v_res_datatype NOT IN ('CHAR', 'NCHAR')) THEN v_resstring + ELSE rpad(v_resstring, v_res_length, ' ') + END; +EXCEPTION + WHEN most_specific_type_mismatch THEN + RAISE USING MESSAGE := 'Argument data type NUMERIC is invalid for argument 3 of convert function.', + DETAIL := 'Use of incorrect "style" parameter value during conversion process.', + HINT := 'Change "style" parameter to the proper value and try again.'; + + WHEN invalid_parameter_value THEN + RAISE USING MESSAGE := pg_catalog.format('%s is not a valid style number when converting from DATE to a character string.', v_style), + DETAIL := 'Use of incorrect "style" parameter value during conversion process.', + HINT := 'Change "style" parameter to the proper value and try again.'; + + WHEN invalid_datetime_format THEN + RAISE USING MESSAGE := pg_catalog.format('Error converting data type DATE to %s.', pg_catalog.btrim(p_datatype)), + DETAIL := 'Incorrect using of pair of input parameters values during conversion process.', + HINT := 'Check the input parameters values, correct them if needed, and try again.'; + + WHEN interval_field_overflow THEN + RAISE USING MESSAGE := pg_catalog.format('The size (%s) given to the convert specification ''%s'' exceeds the maximum allowed for any data type (%s).', + v_lengthexpr, + pg_catalog.lower(v_res_datatype), + v_maxlength), + DETAIL := 'Use of incorrect size value of data type parameter during conversion process.', + HINT := 'Change size component of data type parameter to the allowable value and try again.'; + + WHEN datatype_mismatch THEN + RAISE USING MESSAGE := 'Data type should be one of these values: ''CHAR(n|MAX)'', ''NCHAR(n|MAX)'', ''VARCHAR(n|MAX)'', ''NVARCHAR(n|MAX)''.', + DETAIL := 'Use of incorrect "datatype" parameter value during conversion process.', + HINT := 'Change "datatype" parameter to the proper value and try again.'; + + WHEN invalid_character_value_for_cast THEN + RAISE USING MESSAGE := pg_catalog.format('Invalid CONVERSION_LANG constant value - ''%s''. Allowed values are: ''English'', ''Deutsch'', etc.', + CONVERSION_LANG), + DETAIL := 'Compiled incorrect CONVERSION_LANG constant value in function''s body.', + HINT := 'Correct CONVERSION_LANG constant value in function''s body, recompile it and try again.'; + + WHEN invalid_text_representation THEN + GET STACKED DIAGNOSTICS v_err_message = MESSAGE_TEXT; + v_err_message := substring(pg_catalog.lower(v_err_message) FROM 'integer\:\s\"(.*)\"' :: TEXT); + + RAISE USING MESSAGE := pg_catalog.format('Error while trying to convert "%s" value to SMALLINT (or INTEGER) data type.', + v_err_message), + DETAIL := 'Supplied value contains illegal characters.', + HINT := 'Correct supplied value, remove all illegal characters.'; +END; +$BODY$ +LANGUAGE plpgsql +STABLE +RETURNS NULL ON NULL INPUT; + +CREATE OR REPLACE FUNCTION sys.babelfish_conv_money_to_string(IN p_datatype TEXT, + IN p_moneyval NUMERIC, + IN p_style NUMERIC DEFAULT 0) +RETURNS TEXT +AS +$BODY$ +DECLARE + v_style SMALLINT; + v_format VARCHAR COLLATE "C"; + v_moneyval NUMERIC(19,4) := p_moneyval::NUMERIC(19,4); + v_moneysign NUMERIC(19,4) := sign(v_moneyval); + v_moneyabs NUMERIC(19,4) := abs(v_moneyval); + v_digits SMALLINT; + v_integral_digits SMALLINT; + v_decimal_digits SMALLINT; + v_result TEXT; + v_varchar_length TEXT; + v_default_format VARCHAR COLLATE "C" = '999,999,999,999,990.99'; +BEGIN + v_style := floor(p_style)::SMALLINT; + v_digits := length(v_moneyabs::TEXT); + v_decimal_digits := scale(v_moneyabs); + + IF (scale(p_style) > 0) THEN + RAISE USING MESSAGE := pg_catalog.format('Argument data type numeric is invalid for argument 3 of convert function.'), + DETAIL := 'Use of incorrect "style" parameter value during conversion process.', + HINT := 'Change "style" parameter to the proper value and try again.'; + END IF; + + IF (v_decimal_digits > 0) THEN + v_integral_digits := v_digits - v_decimal_digits - 1; + ELSE + v_integral_digits := v_digits; + END IF; + + IF (v_style = 0) THEN + v_format := (pow(10, v_integral_digits) - 10)::TEXT || 'D99'; + v_result := pg_catalog.btrim(to_char(v_moneyval, v_format)); + ELSIF (v_style = 2 OR v_style = 126) THEN + v_format := (pow(10, v_integral_digits) - 10)::TEXT || 'D9999'; + v_result := pg_catalog.btrim(to_char(v_moneyval, v_format)); + ELSE + v_result := pg_catalog.btrim(to_char(v_moneyval, v_default_format)); + END IF; + + IF (v_moneysign = -1 AND pg_catalog.left(ltrim(v_result), 1) COLLATE "C" != '-' COLLATE "C") THEN + v_result := '-' || pg_catalog.ltrim(v_result); + ELSIF (v_moneysign != -1 AND pg_catalog.left(ltrim(v_result), 1) COLLATE "C" = '-' COLLATE "C") THEN + v_result := pg_catalog.ltrim(v_result, '-'); + END IF; + + IF (pg_catalog.lower(p_datatype) LIKE '%varchar%(%' OR pg_catalog.lower(p_datatype) LIKE '%char%(%') THEN + v_varchar_length := substring(p_datatype COLLATE "C" FROM '\(([0-9]+|MAX)\)' :: TEXT); + IF (v_varchar_length IS NOT NULL AND v_varchar_length <> 'MAX' AND char_length(v_result) > v_varchar_length::SMALLINT) THEN + RAISE USING MESSAGE := pg_catalog.format('There is insufficient result space to convert a money value to varchar.'), + DETAIL := 'The converted money value exceeds the specified varchar length.', + HINT := 'Use a larger varchar size or a different conversion style.'; + END IF; + END IF; + + RETURN v_result; +END; +$BODY$ +LANGUAGE plpgsql +STABLE +RETURNS NULL ON NULL INPUT; + -- Drops the temporary procedure used by the upgrade script. -- Please have this be one of the last statements executed in this upgrade script. DROP PROCEDURE sys.babelfish_drop_deprecated_object(varchar, varchar, varchar, varchar); diff --git a/test/JDBC/expected/BABEL-6813-vu-cleanup.out b/test/JDBC/expected/BABEL-6813-vu-cleanup.out new file mode 100644 index 00000000000..f6f3dfe6523 --- /dev/null +++ b/test/JDBC/expected/BABEL-6813-vu-cleanup.out @@ -0,0 +1,29 @@ + +-- Cleanup +DROP TABLE IF EXISTS babel_6813_t1 +GO + +DROP TABLE IF EXISTS babel_6813_t2 +GO + +DROP TABLE IF EXISTS babel_6813_t3 +GO + +DROP TABLE IF EXISTS babel_6813_datetrunc +GO + +DROP TABLE IF EXISTS babel_6813_date +GO + +DROP TABLE IF EXISTS babel_6813_datetime +GO + +DROP TABLE IF EXISTS babel_6813_time +GO + +DROP TABLE IF EXISTS babel_6813_money +GO + +DROP TABLE IF EXISTS babel_6813_float +GO + diff --git a/test/JDBC/expected/BABEL-6813-vu-prepare.out b/test/JDBC/expected/BABEL-6813-vu-prepare.out new file mode 100644 index 00000000000..278c76ec69d --- /dev/null +++ b/test/JDBC/expected/BABEL-6813-vu-prepare.out @@ -0,0 +1,141 @@ + +-- BABEL-6813: Fix infinite CAST recursion and DATETRUNC regtype resolution +-- Test CAST(numeric AS INT) in CHECK constraint ( uses _trunc_numeric_to_int*) +CREATE TABLE babel_6813_t1 ( + id int, + val DECIMAL(10,2), + CONSTRAINT chk_val CHECK (CAST(val AS INT) > 0) +) +GO + +INSERT INTO babel_6813_t1 VALUES (1, 5.7) +GO +~~ROW COUNT: 1~~ + + +-- Test CAST to different integer sizes +CREATE TABLE babel_6813_t2 ( + id int, + val DECIMAL(10,2), + CONSTRAINT chk_val_int2 CHECK (CAST(val AS SMALLINT) > 0) +) +GO + +INSERT INTO babel_6813_t2 VALUES (1, 3.9) +GO +~~ROW COUNT: 1~~ + + +CREATE TABLE babel_6813_t3 ( + id int, + val DECIMAL(10,2), + CONSTRAINT chk_val_int8 CHECK (CAST(val AS BIGINT) > 0) +) +GO + +INSERT INTO babel_6813_t3 VALUES (1, 10.5) +GO +~~ROW COUNT: 1~~ + + +-- DATETRUNC tests with various datetime types +SELECT DATETRUNC(year, CAST('2023-06-15 10:30:00' AS datetime)) +GO +~~START~~ +datetime +2023-01-01 00:00:00.0 +~~END~~ + + +SELECT DATETRUNC(month, CAST('2023-06-15 10:30:00' AS smalldatetime)) +GO +~~START~~ +smalldatetime +2023-06-01 00:00:00.0 +~~END~~ + + +SELECT DATETRUNC(day, CAST('2023-06-15 10:30:00.1234567' AS datetime2)) +GO +~~START~~ +datetime2 +2023-06-15 00:00:00.0000000 +~~END~~ + + +SELECT DATETRUNC(hour, CAST('2023-06-15 10:30:00.1234567 +05:30' AS datetimeoffset)) +GO +~~START~~ +datetimeoffset +2023-06-15 10:00:00.0000000 +05:30 +~~END~~ + + +CREATE TABLE babel_6813_datetrunc ( + id INT, + dt DATETIME2, + CONSTRAINT chk_dtrunc CHECK (DATETRUNC(month, dt) IS NOT NULL) +) +GO +INSERT INTO babel_6813_datetrunc (id, dt) VALUES (1, '2024-03-15 10:30:45'), (2, '2024-07-22 08:15:00') +GO +~~ROW COUNT: 2~~ + + +--datetime, date, time, money +CREATE TABLE babel_6813_date ( + id INT, + d DATE, + CONSTRAINT chk_date CHECK (CONVERT(VARCHAR(10), d, 101) IS NOT NULL) +) +GO +INSERT INTO babel_6813_date (id, d) VALUES (1, '2024-01-15'), (2, '2024-12-25') +GO +~~ROW COUNT: 2~~ + + +CREATE TABLE babel_6813_datetime ( + id INT, + dt DATETIME, + CONSTRAINT chk_dt CHECK (CONVERT(VARCHAR(30), dt, 121) IS NOT NULL) +) +GO +INSERT INTO babel_6813_datetime (id, dt) VALUES (1, '2024-01-15 10:30:45'), (2, '2024-06-20 08:15:00') +GO +~~ROW COUNT: 2~~ + + + +CREATE TABLE babel_6813_time ( + id INT, + t TIME, + CONSTRAINT chk_time CHECK (CONVERT(VARCHAR(30), t, 121) IS NOT NULL) +) +GO +INSERT INTO babel_6813_time (id, t) VALUES (1, '10:30:45.123'), (2, '23:59:59.999') +GO +~~ROW COUNT: 2~~ + + +CREATE TABLE babel_6813_money ( + id INT, + m MONEY, + CONSTRAINT chk_money CHECK (CONVERT(VARCHAR(30), m, 1) IS NOT NULL) +) +GO +INSERT INTO babel_6813_money (id, m) VALUES (1, 1234.56), (2, 99999.99) +GO +~~ROW COUNT: 2~~ + + +CREATE TABLE babel_6813_float ( + id INT, + f FLOAT, + CONSTRAINT chk_float CHECK (CONVERT(VARCHAR(30), f, 2) IS NOT NULL) +) +GO + +INSERT INTO babel_6813_float (id, f) VALUES (1, 1234.56), (2, 0.001) +GO +~~ROW COUNT: 2~~ + diff --git a/test/JDBC/expected/BABEL-6813-vu-verify.out b/test/JDBC/expected/BABEL-6813-vu-verify.out new file mode 100644 index 00000000000..fb09353ca0d --- /dev/null +++ b/test/JDBC/expected/BABEL-6813-vu-verify.out @@ -0,0 +1,113 @@ + +-- BABEL-6813: Fix infinite CAST recursion and DATETRUNC regtype resolution +-- Verify CAST(numeric AS INT) in CHECK constraints worked +SELECT * FROM babel_6813_t1 +GO +~~START~~ +int#!#numeric +1#!#5.70 +~~END~~ + + +SELECT * FROM babel_6813_t2 +GO +~~START~~ +int#!#numeric +1#!#3.90 +~~END~~ + + +SELECT * FROM babel_6813_t3 +GO +~~START~~ +int#!#numeric +1#!#10.50 +~~END~~ + + +-- DATETRUNC tests with various datetime types +SELECT DATETRUNC(year, CAST('2023-06-15 10:30:00' AS datetime)) +GO +~~START~~ +datetime +2023-01-01 00:00:00.0 +~~END~~ + + +SELECT DATETRUNC(month, CAST('2023-06-15 10:30:00' AS smalldatetime)) +GO +~~START~~ +smalldatetime +2023-06-01 00:00:00.0 +~~END~~ + + +SELECT DATETRUNC(day, CAST('2023-06-15 10:30:00.1234567' AS datetime2)) +GO +~~START~~ +datetime2 +2023-06-15 00:00:00.0000000 +~~END~~ + + +SELECT DATETRUNC(hour, CAST('2023-06-15 10:30:00.1234567 +05:30' AS datetimeoffset)) +GO +~~START~~ +datetimeoffset +2023-06-15 10:00:00.0000000 +05:30 +~~END~~ + + +SELECT * FROM babel_6813_datetrunc +GO +~~START~~ +int#!#datetime2 +1#!#2024-03-15 10:30:45.0000000 +2#!#2024-07-22 08:15:00.0000000 +~~END~~ + + +SELECT * FROM babel_6813_date +GO +~~START~~ +int#!#date +1#!#2024-01-15 +2#!#2024-12-25 +~~END~~ + + +SELECT * FROM babel_6813_datetime +GO +~~START~~ +int#!#datetime +1#!#2024-01-15 10:30:45.0 +2#!#2024-06-20 08:15:00.0 +~~END~~ + + +SELECT * FROM babel_6813_time +GO +~~START~~ +int#!#time +1#!#10:30:45.1230000 +2#!#23:59:59.9990000 +~~END~~ + + +SELECT * FROM babel_6813_money +GO +~~START~~ +int#!#money +1#!#1234.5600 +2#!#99999.9900 +~~END~~ + + +SELECT * from babel_6813_float +GO +~~START~~ +int#!#float +1#!#1234.56 +2#!#0.001 +~~END~~ + diff --git a/test/JDBC/input/BABEL-6813-vu-cleanup.sql b/test/JDBC/input/BABEL-6813-vu-cleanup.sql new file mode 100644 index 00000000000..841bbed4121 --- /dev/null +++ b/test/JDBC/input/BABEL-6813-vu-cleanup.sql @@ -0,0 +1,29 @@ +-- Cleanup + +DROP TABLE IF EXISTS babel_6813_t1 +GO + +DROP TABLE IF EXISTS babel_6813_t2 +GO + +DROP TABLE IF EXISTS babel_6813_t3 +GO + +DROP TABLE IF EXISTS babel_6813_datetrunc +GO + +DROP TABLE IF EXISTS babel_6813_date +GO + +DROP TABLE IF EXISTS babel_6813_datetime +GO + +DROP TABLE IF EXISTS babel_6813_time +GO + +DROP TABLE IF EXISTS babel_6813_money +GO + +DROP TABLE IF EXISTS babel_6813_float +GO + diff --git a/test/JDBC/input/BABEL-6813-vu-prepare.sql b/test/JDBC/input/BABEL-6813-vu-prepare.sql new file mode 100644 index 00000000000..882b9cd46d9 --- /dev/null +++ b/test/JDBC/input/BABEL-6813-vu-prepare.sql @@ -0,0 +1,103 @@ +-- BABEL-6813: Fix infinite CAST recursion and DATETRUNC regtype resolution + +-- Test CAST(numeric AS INT) in CHECK constraint ( uses _trunc_numeric_to_int*) +CREATE TABLE babel_6813_t1 ( + id int, + val DECIMAL(10,2), + CONSTRAINT chk_val CHECK (CAST(val AS INT) > 0) +) +GO + +INSERT INTO babel_6813_t1 VALUES (1, 5.7) +GO + +-- Test CAST to different integer sizes +CREATE TABLE babel_6813_t2 ( + id int, + val DECIMAL(10,2), + CONSTRAINT chk_val_int2 CHECK (CAST(val AS SMALLINT) > 0) +) +GO + +INSERT INTO babel_6813_t2 VALUES (1, 3.9) +GO + +CREATE TABLE babel_6813_t3 ( + id int, + val DECIMAL(10,2), + CONSTRAINT chk_val_int8 CHECK (CAST(val AS BIGINT) > 0) +) +GO + +INSERT INTO babel_6813_t3 VALUES (1, 10.5) +GO + +-- DATETRUNC tests with various datetime types +SELECT DATETRUNC(year, CAST('2023-06-15 10:30:00' AS datetime)) +GO + +SELECT DATETRUNC(month, CAST('2023-06-15 10:30:00' AS smalldatetime)) +GO + +SELECT DATETRUNC(day, CAST('2023-06-15 10:30:00.1234567' AS datetime2)) +GO + +SELECT DATETRUNC(hour, CAST('2023-06-15 10:30:00.1234567 +05:30' AS datetimeoffset)) +GO + +CREATE TABLE babel_6813_datetrunc ( + id INT, + dt DATETIME2, + CONSTRAINT chk_dtrunc CHECK (DATETRUNC(month, dt) IS NOT NULL) +) +GO +INSERT INTO babel_6813_datetrunc (id, dt) VALUES (1, '2024-03-15 10:30:45'), (2, '2024-07-22 08:15:00') +GO + +--datetime, date, time, money +CREATE TABLE babel_6813_date ( + id INT, + d DATE, + CONSTRAINT chk_date CHECK (CONVERT(VARCHAR(10), d, 101) IS NOT NULL) +) +GO +INSERT INTO babel_6813_date (id, d) VALUES (1, '2024-01-15'), (2, '2024-12-25') +GO + +CREATE TABLE babel_6813_datetime ( + id INT, + dt DATETIME, + CONSTRAINT chk_dt CHECK (CONVERT(VARCHAR(30), dt, 121) IS NOT NULL) +) +GO +INSERT INTO babel_6813_datetime (id, dt) VALUES (1, '2024-01-15 10:30:45'), (2, '2024-06-20 08:15:00') +GO + + +CREATE TABLE babel_6813_time ( + id INT, + t TIME, + CONSTRAINT chk_time CHECK (CONVERT(VARCHAR(30), t, 121) IS NOT NULL) +) +GO +INSERT INTO babel_6813_time (id, t) VALUES (1, '10:30:45.123'), (2, '23:59:59.999') +GO + +CREATE TABLE babel_6813_money ( + id INT, + m MONEY, + CONSTRAINT chk_money CHECK (CONVERT(VARCHAR(30), m, 1) IS NOT NULL) +) +GO +INSERT INTO babel_6813_money (id, m) VALUES (1, 1234.56), (2, 99999.99) +GO + +CREATE TABLE babel_6813_float ( + id INT, + f FLOAT, + CONSTRAINT chk_float CHECK (CONVERT(VARCHAR(30), f, 2) IS NOT NULL) +) +GO + +INSERT INTO babel_6813_float (id, f) VALUES (1, 1234.56), (2, 0.001) +GO diff --git a/test/JDBC/input/BABEL-6813-vu-verify.sql b/test/JDBC/input/BABEL-6813-vu-verify.sql new file mode 100644 index 00000000000..e18c2a49fe9 --- /dev/null +++ b/test/JDBC/input/BABEL-6813-vu-verify.sql @@ -0,0 +1,42 @@ +-- BABEL-6813: Fix infinite CAST recursion and DATETRUNC regtype resolution + +-- Verify CAST(numeric AS INT) in CHECK constraints worked +SELECT * FROM babel_6813_t1 +GO + +SELECT * FROM babel_6813_t2 +GO + +SELECT * FROM babel_6813_t3 +GO + +-- DATETRUNC tests with various datetime types +SELECT DATETRUNC(year, CAST('2023-06-15 10:30:00' AS datetime)) +GO + +SELECT DATETRUNC(month, CAST('2023-06-15 10:30:00' AS smalldatetime)) +GO + +SELECT DATETRUNC(day, CAST('2023-06-15 10:30:00.1234567' AS datetime2)) +GO + +SELECT DATETRUNC(hour, CAST('2023-06-15 10:30:00.1234567 +05:30' AS datetimeoffset)) +GO + +SELECT * FROM babel_6813_datetrunc +GO + +SELECT * FROM babel_6813_date +GO + +SELECT * FROM babel_6813_datetime +GO + +SELECT * FROM babel_6813_time +GO + +SELECT * FROM babel_6813_money +GO + +SELECT * from babel_6813_float +GO diff --git a/test/JDBC/upgrade/latest/schedule b/test/JDBC/upgrade/latest/schedule index 2468e5c9d2c..07dc3d2b343 100644 --- a/test/JDBC/upgrade/latest/schedule +++ b/test/JDBC/upgrade/latest/schedule @@ -688,3 +688,4 @@ BABEL-5264 BABEL-6037 babel_sqlvariant_coerce_type BABEL-login-db-long-identifiers +BABEL-6813