From b327d8452b8457e773cb8a88697c2ef4c53d16f5 Mon Sep 17 00:00:00 2001 From: Walt Boettge Date: Mon, 25 Sep 2023 14:19:43 -0700 Subject: [PATCH] Add hook in select_common_type for CHAR/NCHAR case (#140) * Fix SELECT DISTINCT NULL result type With distinct, Postgres coerces the type of unknown-typed targets so it may find an equality function. It selects TEXT as the type to coerce to. The problem in T-SQL is if we are unioning the result of SELECT DISTINCT NULL with a different type. Some types, BIT for example, do not have conversion functions between TEXT. Therefore, SELECT CAST(1 as BIT) UNION SELECT DISTINCT NULL fails unexpectantly in Babelfish. This change coerces NULL values to INT4 instead of TEXT when using Babelfish, fixing the result type and the union behavior. Signed-off-by: Walt Boettge Task: BABEL-3348, BABEL-3392, BABEL-4157, BABEL-1874 --- src/backend/parser/analyze.c | 17 +++++++++-------- src/backend/parser/parse_clause.c | 22 ++++++++++++---------- src/backend/parser/parse_coerce.c | 15 +++++++++++++++ src/backend/parser/parse_expr.c | 2 +- src/include/parser/analyze.h | 4 ++-- src/include/parser/parse_coerce.h | 4 ++-- 6 files changed, 41 insertions(+), 23 deletions(-) diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index c02394cd751..6bf7cc21c5b 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -45,6 +45,7 @@ #include "parser/parse_relation.h" #include "parser/parse_target.h" #include "parser/parse_type.h" +#include "parser/parser.h" #include "parser/parsetree.h" #include "rewrite/rewriteManip.h" #include "utils/backend_status.h" @@ -85,7 +86,7 @@ set_target_table_alternative_hook_type set_target_table_alternative_hook = NULL; pre_transform_setop_tree_hook_type pre_transform_setop_tree_hook = NULL; /* Hook to reset a query's targetlist after modification in pre_transfrom_sort_clause */ -post_transform_sort_clause_hook_type post_transform_sort_clause_hook = NULL; +pre_transform_setop_sort_clause_hook_type pre_transform_setop_sort_clause_hook = NULL; /* Hooks for handling unquoted string argumentss in T-SQL procedure calls */ call_argument_unquoted_string_hook_type call_argument_unquoted_string_hook = NULL; @@ -1938,15 +1939,15 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) */ tllen = list_length(qry->targetList); + if (pre_transform_setop_sort_clause_hook) + pre_transform_setop_sort_clause_hook(pstate, qry, sortClause, leftmostQuery); + qry->sortClause = transformSortClause(pstate, sortClause, &qry->targetList, EXPR_KIND_ORDER_BY, false /* allow SQL92 rules */ ); - if (post_transform_sort_clause_hook) - post_transform_sort_clause_hook(qry, leftmostQuery); - /* restore namespace, remove join RTE from rtable */ pstate->p_namespace = sv_namespace; pstate->p_rtable = list_truncate(pstate->p_rtable, sv_rtable_length); @@ -2282,8 +2283,8 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt, if (lcoltype != UNKNOWNOID) lcolnode = coerce_to_common_type(pstate, lcolnode, rescoltype, context); - else if (IsA(lcolnode, Const) || - IsA(lcolnode, Param)) + else if ((IsA(lcolnode, Const) || + IsA(lcolnode, Param)) && sql_dialect != SQL_DIALECT_TSQL) { lcolnode = coerce_to_common_type(pstate, lcolnode, rescoltype, context); @@ -2293,8 +2294,8 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt, if (rcoltype != UNKNOWNOID) rcolnode = coerce_to_common_type(pstate, rcolnode, rescoltype, context); - else if (IsA(rcolnode, Const) || - IsA(rcolnode, Param)) + else if ((IsA(lcolnode, Const) || + IsA(lcolnode, Param)) && sql_dialect != SQL_DIALECT_TSQL) { rcolnode = coerce_to_common_type(pstate, rcolnode, rescoltype, context); diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index bec0593b09d..bf055b34dbe 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -3294,11 +3294,12 @@ addTargetToSortList(ParseState *pstate, TargetEntry *tle, /* if tlist item is an UNKNOWN literal, change it to TEXT */ if (restype == UNKNOWNOID) { - tle->expr = (Expr *) coerce_type(pstate, (Node *) tle->expr, - restype, TEXTOID, -1, - COERCION_IMPLICIT, - COERCE_IMPLICIT_CAST, - -1); + if (sql_dialect != SQL_DIALECT_TSQL || pstate->p_resolve_unknowns) + tle->expr = (Expr *) coerce_type(pstate, (Node *) tle->expr, + restype, TEXTOID, -1, + COERCION_IMPLICIT, + COERCE_IMPLICIT_CAST, + -1); restype = TEXTOID; } @@ -3435,11 +3436,12 @@ addTargetToGroupList(ParseState *pstate, TargetEntry *tle, /* if tlist item is an UNKNOWN literal, change it to TEXT */ if (restype == UNKNOWNOID) { - tle->expr = (Expr *) coerce_type(pstate, (Node *) tle->expr, - restype, TEXTOID, -1, - COERCION_IMPLICIT, - COERCE_IMPLICIT_CAST, - -1); + if (sql_dialect != SQL_DIALECT_TSQL || pstate->p_resolve_unknowns) + tle->expr = (Expr *) coerce_type(pstate, (Node *) tle->expr, + restype, TEXTOID, -1, + COERCION_IMPLICIT, + COERCE_IMPLICIT_CAST, + -1); restype = TEXTOID; } diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c index 01babbbce84..88b37bbf6f1 100644 --- a/src/backend/parser/parse_coerce.c +++ b/src/backend/parser/parse_coerce.c @@ -38,6 +38,7 @@ determine_datatype_precedence_hook_type determine_datatype_precedence_hook = NUL 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; +select_common_typmod_hook_type select_common_typmod_hook = NULL; static Node *coerce_type_typmod(Node *node, Oid targetTypeId, int32 targetTypMod, @@ -1384,6 +1385,13 @@ select_common_type(ParseState *pstate, List *exprs, const char *context, ListCell *lc; const char *dump_restore = GetConfigOption("babelfishpg_tsql.dump_restore", true, false); + if (select_common_type_hook) + { + Oid result = (*select_common_type_hook)(pstate, exprs, context, which_expr); + if (result != InvalidOid) + return result; + } + Assert(exprs != NIL); pexpr = (Node *) linitial(exprs); lc = list_second_cell(exprs); @@ -1719,6 +1727,13 @@ select_common_typmod(ParseState *pstate, List *exprs, Oid common_type) bool first = true; int32 result = -1; + if (select_common_typmod_hook) + { + result = (*select_common_typmod_hook)(pstate, exprs, common_type); + if (result != -1) + return result; + } + foreach(lc, exprs) { Node *expr = (Node *) lfirst(lc); diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 9a093190df3..a779fa28b26 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -2311,7 +2311,7 @@ transformCoalesceExpr(ParseState *pstate, CoalesceExpr *c) } if (sql_dialect == SQL_DIALECT_TSQL && select_common_type_hook && c->tsql_is_null) - newc->coalescetype = (*select_common_type_hook)(pstate, newargs); + newc->coalescetype = select_common_type(pstate, newargs, "ISNULL", NULL); else newc->coalescetype = select_common_type(pstate, newargs, "COALESCE", NULL); /* coalescecollid will be set by parse_collate.c */ diff --git a/src/include/parser/analyze.h b/src/include/parser/analyze.h index 4659556b0f1..8d3e7a1528a 100644 --- a/src/include/parser/analyze.h +++ b/src/include/parser/analyze.h @@ -61,8 +61,8 @@ typedef void (*pre_transform_setop_tree_hook_type) (SelectStmt *stmt, SelectStmt extern PGDLLIMPORT pre_transform_setop_tree_hook_type pre_transform_setop_tree_hook; /* Hook for handle target table before transforming from clause */ -typedef void (*post_transform_sort_clause_hook_type) (Query *qry, Query *leftmostQuery); -extern PGDLLIMPORT post_transform_sort_clause_hook_type post_transform_sort_clause_hook; +typedef void (*pre_transform_setop_sort_clause_hook_type) (ParseState *pstate, Query *qry, List *sortClause, Query *leftmostQuery); +extern PGDLLIMPORT pre_transform_setop_sort_clause_hook_type pre_transform_setop_sort_clause_hook; /* Hooks for handling unquoted string argumentss in T-SQL procedure calls */ typedef Node* (*call_argument_unquoted_string_hook_type)(Node *arg); diff --git a/src/include/parser/parse_coerce.h b/src/include/parser/parse_coerce.h index 0470e86af53..1326d8a198c 100644 --- a/src/include/parser/parse_coerce.h +++ b/src/include/parser/parse_coerce.h @@ -129,8 +129,8 @@ typedef Node *(*coerce_string_literal_hook_type) (ParseCallbackState *pcbstate, */ typedef void (*validate_implicit_conversion_from_string_literal_hook_type) (Const *newcon, const char *value); -/* Generic hook to override the behavior of select_common_type(...) */ -typedef Oid (*select_common_type_hook_type) (ParseState *pstate, List *exprs); +typedef Oid (*select_common_type_hook_type) (ParseState *pstate, List *exprs, const char *context, Node **which_expr); extern PGDLLIMPORT select_common_type_hook_type select_common_type_hook; +typedef int32 (*select_common_typmod_hook_type) (ParseState *pstate, List *exprs, Oid common_type); #endif /* PARSE_COERCE_H */