Skip to content

Commit

Permalink
Retain result type after UNION as base type in T-SQL (#487)
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
hopebo authored and Bo Li committed Dec 17, 2024
1 parent 10d3078 commit e7693fe
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 0 deletions.
30 changes: 30 additions & 0 deletions src/backend/parser/parse_coerce.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions src/include/parser/parse_coerce.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,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
*/
Expand Down

0 comments on commit e7693fe

Please sign in to comment.