Skip to content

Commit

Permalink
Add hook in select_common_type for CHAR/NCHAR case (#140)
Browse files Browse the repository at this point in the history
* 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 <[email protected]>
Task: BABEL-3348, BABEL-3392, BABEL-4157, BABEL-1874
  • Loading branch information
wboettge authored Sep 25, 2023
1 parent 26b4e99 commit b327d84
Show file tree
Hide file tree
Showing 6 changed files with 41 additions and 23 deletions.
17 changes: 9 additions & 8 deletions src/backend/parser/analyze.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand Down
22 changes: 12 additions & 10 deletions src/backend/parser/parse_clause.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down Expand Up @@ -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;
}

Expand Down
15 changes: 15 additions & 0 deletions src/backend/parser/parse_coerce.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion src/backend/parser/parse_expr.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
4 changes: 2 additions & 2 deletions src/include/parser/analyze.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
4 changes: 2 additions & 2 deletions src/include/parser/parse_coerce.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */

0 comments on commit b327d84

Please sign in to comment.