Skip to content

Commit cc424a6

Browse files
Ayush-061Ayush-061
andauthored
Fix for infinite recursion for CAST Convert to String to fail And DATETRUNC to fail during DnR (#4854)
sys._trunc_numeric_to_int4 / _int8 and _int2 variants causes infinite recursion and stack overflow when called during dump/restore for tables with CAST(DECIMAL AS INT). The error Repro - #4851 , #4857 During dump/restore, babelfishpg_tsql.dump_restore is set to on, which activates the find_coercion_pathway_hook. This hook overrides PostgreSQL's standard cast resolution for numeric to integer types, routing them through the Babelfish truncation functions (sys._trunc_numeric_to_int* ). These functions internally use CAST(trunc(arg) AS INT*), which triggers the same hook again leadind to infinite recursion. For the datetrunc it fails because the sys schema isn't in search_path during DnR. The type datetime lives in sys schema without sys in the path, PG can't find it. So changed datetime -> sys.datetime and similar for other For the Issue with CONVERT- The reasons- substring(text, pattern) → picks substring(text, int) instead of (text, text) → tries to parse the regex as int. regexp_replace-> picks (text,text,text,int) over (text,text,text,text) → tries to parse 'gi' as int. col IN (CHAR,VARCHAR) → the IN-list array's common type is resolved to float8 instead of text A better fix for these similar issues would be a C level fix in find_coercion_pathway . But this fix unblocks the problem with - #4826 (which crashes for table with datetrunc and CAST in computed column)' Issues Resolved BABEL-6813 and BABEL-6815 --------- Co-authored-by: Ayush-061 <ayushhx@amazon.com>
1 parent 2fa7f2e commit cc424a6

12 files changed

Lines changed: 1546 additions & 26 deletions

contrib/babelfishpg_common/sql/coerce.sql

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,23 +68,23 @@ $$ LANGUAGE plpgsql STABLE;
6868
CREATE OR REPLACE FUNCTION sys._trunc_numeric_to_int8(In arg numeric)
6969
RETURNS INT8 AS $$
7070
BEGIN
71-
RETURN CAST(trunc(arg) AS INT8);
71+
RETURN pg_catalog.int8(trunc(arg));
7272
END;
7373
$$ LANGUAGE plpgsql STABLE;
7474

7575
-- numeric -> int4
7676
CREATE OR REPLACE FUNCTION sys._trunc_numeric_to_int4(In arg numeric)
7777
RETURNS INT4 AS $$
7878
BEGIN
79-
RETURN CAST(trunc(arg) AS INT4);
79+
RETURN pg_catalog.int4(trunc(arg));
8080
END;
8181
$$ LANGUAGE plpgsql STABLE;
8282

8383
-- numeric -> int2
8484
CREATE OR REPLACE FUNCTION sys._trunc_numeric_to_int2(In arg numeric)
8585
RETURNS INT2 AS $$
8686
BEGIN
87-
RETURN CAST(trunc(arg) AS INT2);
87+
RETURN pg_catalog.int2(trunc(arg));
8888
END;
8989
$$ LANGUAGE plpgsql STABLE;
9090

contrib/babelfishpg_common/sql/upgrades/babelfish_common_helper--6.1.0--6.2.0.sql

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,24 @@
44

55
-- complain if script is sourced in psql, rather than via ALTER EXTENSION
66
\echo Use "ALTER EXTENSION ""babelfishpg_common"" UPDATE TO '6.2.0'" to load this file. \quit
7+
8+
CREATE OR REPLACE FUNCTION sys._trunc_numeric_to_int8(In arg numeric)
9+
RETURNS INT8 AS $$
10+
BEGIN
11+
RETURN pg_catalog.int8(trunc(arg));
12+
END;
13+
$$ LANGUAGE plpgsql STABLE;
14+
15+
CREATE OR REPLACE FUNCTION sys._trunc_numeric_to_int4(In arg numeric)
16+
RETURNS INT4 AS $$
17+
BEGIN
18+
RETURN pg_catalog.int4(trunc(arg));
19+
END;
20+
$$ LANGUAGE plpgsql STABLE;
21+
22+
CREATE OR REPLACE FUNCTION sys._trunc_numeric_to_int2(In arg numeric)
23+
RETURNS INT2 AS $$
24+
BEGIN
25+
RETURN pg_catalog.int2(trunc(arg));
26+
END;
27+
$$ LANGUAGE plpgsql STABLE;

contrib/babelfishpg_tsql/sql/sys_function_helpers.sql

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -134,11 +134,11 @@ BEGIN
134134
v_res_datatype := PG_CATALOG.rtrim(split_part(v_datatype, '(', 1));
135135

136136
v_maxlength := CASE
137-
WHEN (v_res_datatype IN ('CHAR', 'VARCHAR')) THEN VARCHAR_MAX
137+
WHEN (v_res_datatype::TEXT IN ('CHAR', 'VARCHAR')) THEN VARCHAR_MAX
138138
ELSE NVARCHAR_MAX
139139
END;
140140

141-
v_lengthexpr := substring(v_datatype, DATATYPE_MASK_REGEXP);
141+
v_lengthexpr := substring(v_datatype, DATATYPE_MASK_REGEXP::text);
142142

143143
IF (v_lengthexpr <> 'MAX' AND char_length(v_lengthexpr) > 4) THEN
144144
RAISE interval_field_overflow;
@@ -174,7 +174,7 @@ BEGIN
174174
RAISE invalid_character_value_for_cast;
175175
END;
176176

177-
v_monthname := (v_lang_metadata_json -> 'months_shortnames') ->> v_month - 1;
177+
v_monthname := (v_lang_metadata_json -> 'months_shortnames'::text) ->> v_month - 1;
178178

179179
v_resmask := CASE
180180
WHEN (v_style IN (1, 22)) THEN 'MM/DD/YY'
@@ -212,7 +212,7 @@ BEGIN
212212
ELSE 60
213213
END);
214214
RETURN CASE
215-
WHEN (v_res_datatype NOT IN ('CHAR', 'NCHAR')) THEN v_resstring
215+
WHEN (v_res_datatype::TEXT NOT IN ('CHAR', 'NCHAR')) THEN v_resstring
216216
ELSE rpad(v_resstring, v_res_length, ' ')
217217
END;
218218
EXCEPTION
@@ -305,7 +305,7 @@ BEGIN
305305

306306
IF (v_src_datatype ~* SRCDATATYPE_MASK_REGEXP)
307307
THEN
308-
v_scale := substring(v_src_datatype, SRCDATATYPE_MASK_REGEXP)::SMALLINT;
308+
v_scale := substring(v_src_datatype, SRCDATATYPE_MASK_REGEXP::text)::SMALLINT;
309309

310310
v_src_datatype := PG_CATALOG.rtrim(split_part(v_src_datatype, '(', 1));
311311

@@ -334,11 +334,11 @@ BEGIN
334334
v_res_datatype := PG_CATALOG.rtrim(split_part(v_datatype, '(', 1));
335335

336336
v_maxlength := CASE
337-
WHEN (v_res_datatype IN ('CHAR', 'VARCHAR')) THEN VARCHAR_MAX
337+
WHEN (v_res_datatype::TEXT IN ('CHAR', 'VARCHAR')) THEN VARCHAR_MAX
338338
ELSE NVARCHAR_MAX
339339
END;
340340

341-
v_lengthexpr := substring(v_datatype, DATATYPE_MASK_REGEXP);
341+
v_lengthexpr := substring(v_datatype, DATATYPE_MASK_REGEXP::text);
342342

343343
IF (v_lengthexpr <> 'MAX' AND char_length(v_lengthexpr) > 4)
344344
THEN
@@ -375,9 +375,9 @@ BEGIN
375375
RAISE invalid_character_value_for_cast;
376376
END;
377377

378-
v_monthname := (v_lang_metadata_json -> 'months_shortnames') ->> v_month - 1;
378+
v_monthname := (v_lang_metadata_json -> 'months_shortnames'::text) ->> v_month - 1;
379379

380-
IF (v_src_datatype IN ('DATETIME', 'SMALLDATETIME')) THEN
380+
IF (v_src_datatype::TEXT IN ('DATETIME', 'SMALLDATETIME')) THEN
381381
v_fseconds := sys.babelfish_round_fractseconds(to_char(v_datetimeval, 'MS'));
382382

383383
IF (v_fseconds::INTEGER = 1000) THEN
@@ -495,7 +495,7 @@ BEGIN
495495
ELSE 60
496496
END);
497497
RETURN CASE
498-
WHEN (v_res_datatype NOT IN ('CHAR', 'NCHAR')) THEN v_resstring
498+
WHEN (v_res_datatype::TEXT NOT IN ('CHAR', 'NCHAR')) THEN v_resstring
499499
ELSE rpad(v_resstring, v_res_length, ' ')
500500
END;
501501
EXCEPTION
@@ -2240,7 +2240,7 @@ BEGIN
22402240

22412241
IF (v_src_datatype ~* SRCDATATYPE_MASK_REGEXP)
22422242
THEN
2243-
v_scale := coalesce(substring(v_src_datatype, SRCDATATYPE_MASK_REGEXP)::SMALLINT, 7);
2243+
v_scale := coalesce(substring(v_src_datatype, SRCDATATYPE_MASK_REGEXP::text)::SMALLINT, 7);
22442244

22452245
IF (v_scale NOT BETWEEN 0 AND 7) THEN
22462246
RAISE invalid_regular_expression;
@@ -2254,11 +2254,11 @@ BEGIN
22542254
v_res_datatype := PG_CATALOG.rtrim(split_part(v_datatype, '(', 1));
22552255

22562256
v_res_maxlength := CASE
2257-
WHEN (v_res_datatype IN ('CHAR', 'VARCHAR')) THEN VARCHAR_MAX
2257+
WHEN (v_res_datatype::TEXT IN ('CHAR', 'VARCHAR')) THEN VARCHAR_MAX
22582258
ELSE NVARCHAR_MAX
22592259
END;
22602260

2261-
v_lengthexpr := substring(v_datatype, DATATYPE_MASK_REGEXP);
2261+
v_lengthexpr := substring(v_datatype, DATATYPE_MASK_REGEXP::text);
22622262

22632263
IF (v_lengthexpr <> 'MAX' AND char_length(v_lengthexpr) > 4) THEN
22642264
RAISE interval_field_overflow;
@@ -2342,7 +2342,7 @@ BEGIN
23422342
ELSE 60
23432343
END);
23442344
RETURN CASE
2345-
WHEN (v_res_datatype NOT IN ('CHAR', 'NCHAR')) THEN v_resstring
2345+
WHEN (v_res_datatype::TEXT NOT IN ('CHAR', 'NCHAR')) THEN v_resstring
23462346
ELSE rpad(v_resstring, v_res_length, ' ')
23472347
END;
23482348
EXCEPTION
@@ -2573,10 +2573,10 @@ BEGIN
25732573

25742574
v_lang_spec_culture := CASE
25752575
WHEN (v_lang_spec_culture !~ '\.') THEN v_lang_spec_culture
2576-
ELSE substring(v_lang_spec_culture, '(.*)(?:\.)')
2576+
ELSE substring(v_lang_spec_culture, '(.*)(?:\.)'::text)
25772577
END;
25782578

2579-
v_lang_spec_culture := pg_catalog.upper(regexp_replace(v_lang_spec_culture, ',\s*', '_', 'gi'));
2579+
v_lang_spec_culture := pg_catalog.upper(regexp_replace(v_lang_spec_culture, ',\s*', '_', 'gi'::text));
25802580

25812581
BEGIN
25822582
v_lang_data_jsonb := nullif(current_setting(format('sys.lang_metadata_json.%s',
@@ -2594,7 +2594,7 @@ BEGIN
25942594
SELECT lang_data_jsonb
25952595
INTO STRICT v_lang_data_jsonb
25962596
FROM sys.babelfish_syslanguages
2597-
WHERE spec_culture = v_lang_spec_culture;
2597+
WHERE spec_culture = v_lang_spec_culture::text;
25982598
ELSE
25992599
v_locale_parts := string_to_array(v_lang_spec_culture, '-');
26002600

@@ -2611,7 +2611,7 @@ BEGIN
26112611
SELECT lang_data_jsonb
26122612
INTO v_lang_data_jsonb
26132613
FROM sys.babelfish_syslanguages
2614-
WHERE spec_culture = v_lang_spec_culture;
2614+
WHERE spec_culture = v_lang_spec_culture::text;
26152615
END;
26162616
ELSE
26172617
v_is_cached := TRUE;
@@ -10984,7 +10984,7 @@ BEGIN
1098410984
END IF;
1098510985

1098610986
IF (pg_catalog.lower(p_datatype) LIKE '%varchar%(%' OR pg_catalog.lower(p_datatype) LIKE '%char%(%') THEN
10987-
v_varchar_length := substring(p_datatype COLLATE "C" FROM '\(([0-9]+|MAX)\)');
10987+
v_varchar_length := substring(p_datatype COLLATE "C" FROM '\(([0-9]+|MAX)\)'::TEXT);
1098810988
IF (v_varchar_length IS NOT NULL AND v_varchar_length <> 'MAX' AND char_length(v_result) > v_varchar_length::SMALLINT) THEN
1098910989
RAISE USING MESSAGE := pg_catalog.format('There is insufficient result space to convert a money value to varchar.'),
1099010990
DETAIL := 'The converted money value exceeds the specified varchar length.',
@@ -11059,7 +11059,7 @@ BEGIN
1105911059
RAISE invalid_parameter_value;
1106011060
END IF;
1106111061

11062-
v_res_length := substring(p_datatype COLLATE "C", MASK_REGEXP)::SMALLINT;
11062+
v_res_length := substring(p_datatype COLLATE "C", MASK_REGEXP::text)::SMALLINT;
1106311063
IF v_res_length IS NULL THEN
1106411064
RETURN ltrim(v_result);
1106511065
ELSE

contrib/babelfishpg_tsql/sql/sys_functions.sql

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5120,17 +5120,17 @@ BEGIN
51205120
RAISE EXCEPTION 'The datepart ''weekday'' is not supported by date function datetrunc for data type ''%''.', date_arg_datatype;
51215121
ELSIF date_arg_datatype = 'date'::regtype AND datepart IN ('hour', 'minute', 'second', 'millisecond', 'microsecond') THEN
51225122
RAISE EXCEPTION 'The datepart ''%'' is not supported by date function datetrunc for data type ''date''.', datepart;
5123-
ELSIF date_arg_datatype = 'datetime'::regtype AND datepart IN ('microsecond') THEN
5123+
ELSIF date_arg_datatype = 'sys.datetime'::regtype AND datepart IN ('microsecond') THEN
51245124
RAISE EXCEPTION 'The datepart ''%'' is not supported by date function datetrunc for data type ''datetime''.', datepart;
5125-
ELSIF date_arg_datatype = 'smalldatetime'::regtype AND datepart IN ('millisecond', 'microsecond') THEN
5125+
ELSIF date_arg_datatype = 'sys.smalldatetime'::regtype AND datepart IN ('millisecond', 'microsecond') THEN
51265126
RAISE EXCEPTION 'The datepart ''%'' is not supported by date function datetrunc for data type ''smalldatetime''.', datepart;
51275127
ELSIF date_arg_datatype = 'time'::regtype THEN
51285128
IF datepart IN ('year', 'quarter', 'month', 'doy', 'day', 'week', 'tsql_week') THEN
51295129
RAISE EXCEPTION 'The datepart ''%'' is not supported by date function datetrunc for data type ''time''.', datepart;
51305130
END IF;
51315131
-- Limitation in determining if the specified fractional scale (if provided any) for time datatype is
51325132
-- insufficient to support provided datepart (millisecond, microsecond) value
5133-
ELSIF date_arg_datatype IN ('datetime2'::regtype, 'datetimeoffset'::regtype) THEN
5133+
ELSIF date_arg_datatype IN ('sys.datetime2'::regtype, 'sys.datetimeoffset'::regtype) THEN
51345134
-- Limitation in determining if the specified fractional scale (if provided any) for the above datatype is
51355135
-- insufficient to support for provided datepart (millisecond, microsecond) value
51365136
END IF;

0 commit comments

Comments
 (0)