From ac51c42bf5cc3b1526897564909bd9afb0b71681 Mon Sep 17 00:00:00 2001 From: Bo Date: Mon, 16 Dec 2024 22:44:47 -0800 Subject: [PATCH] Retain result type after UNION as base type in T-SQL (#487) Description ----------- When a base type such as `money` is UNIONed with another integer type, the result type will be `fixeddecimal` instead of `money`, which is inconsistent with SQL Server's behaviour. And could also cause some characters not displaying well. Analysis -------- The root cause of the issue is Postgres will consider the base type of domain type as the result type. In Babelfish, `money` is implemented as a domain type of `fixeddecimal`. Actually, `money` is a base type in T-SQL, thus Babelfish should retain `money` type after UNION. Fix --- After a final type has determined by the optimizer, check if it's a base type in T-SQL. If it is, try to use the domain type as result type if the domain type has higher precision than other types. Task: BABEL-5242 Signed-off-by: Bo Li --- src/backend/parser/parse_coerce.c | 30 ++++++++++++++++++++++++++++++ src/include/parser/parse_coerce.h | 5 +++++ 2 files changed, 35 insertions(+) diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c index 25edfa26601..66e28c44f3e 100644 --- a/src/backend/parser/parse_coerce.c +++ b/src/backend/parser/parse_coerce.c @@ -35,6 +35,7 @@ find_coercion_pathway_hook_type find_coercion_pathway_hook = NULL; determine_datatype_precedence_hook_type determine_datatype_precedence_hook = NULL; +is_tsql_base_datatype_hook_type is_tsql_base_datatype_hook = NULL; coerce_string_literal_hook_type coerce_string_literal_hook = NULL; validate_implicit_conversion_from_string_literal_hook_type validate_implicit_conversion_from_string_literal_hook = NULL; select_common_type_hook_type select_common_type_hook = NULL; @@ -1504,9 +1505,38 @@ select_common_type(ParseState *pstate, List *exprs, const char *context, pcategory = ncategory; pispreferred = nispreferred; } + } else if (sql_dialect == SQL_DIALECT_TSQL && ntype == ptype) + { + /* + * For the columns which have the same base type, we choose the + * expression with higher precedence type in T-SQL. + * For example, smallmoney UNION money, the base type of + * them are both fixeddecimal. But we shouldn't use smallmoney as + * the result type, it could loss precision. + * Here we don't need to update other variables since they are the + * same. + */ + if (is_tsql_base_datatype_hook && + (*is_tsql_base_datatype_hook)(exprType(nexpr)) && + determine_datatype_precedence_hook && + determine_datatype_precedence_hook(exprType(nexpr), + exprType(pexpr))) + pexpr = nexpr; } } + /* + * If the preferred type is not the result type of corresponding + * expression, it means the result type is a domain type in Postgres. Some + * base data types in T-SQL are implemented as domain types in Babelfish. + * From SQL Server's perspective, we should try to retain those types as + * result types. + */ + if (sql_dialect == SQL_DIALECT_TSQL && ptype != exprType(pexpr) && + is_tsql_base_datatype_hook && + (*is_tsql_base_datatype_hook)(exprType(pexpr))) + ptype = exprType(pexpr); + /* * If all the inputs were UNKNOWN type --- ie, unknown-type literals --- * then resolve as type TEXT. This situation comes up with constructs diff --git a/src/include/parser/parse_coerce.h b/src/include/parser/parse_coerce.h index c6fe31621dd..9055aaaf474 100644 --- a/src/include/parser/parse_coerce.h +++ b/src/include/parser/parse_coerce.h @@ -113,6 +113,11 @@ typedef CoercionPathType (*find_coercion_pathway_hook_type) (Oid sourceTypeId, */ typedef bool (*determine_datatype_precedence_hook_type) (Oid typeId1, Oid typeId2); +/* + * Hook interface to determine if a data type is a base type in T-SQL + */ +typedef bool (*is_tsql_base_datatype_hook_type) (Oid typeId); + /* * T-SQL has different rules for string literal datatype coercions */