While working on another patch, I figured adding a select_common_typmod() to go along with select_common_type() and select_common_collation() would be handy. Typmods were previously combined using hand-coded logic in several places, and not at all in other places. The logic in select_common_typmod() isn't very exciting, but it makes the code more compact and readable in a few locations, and in the future we can perhaps do more complicated things if desired.

There might have been a tiny bug in transformValuesClause() because while consolidating the typmods it does not take into account whether the types are actually the same (as more correctly done in transformSetOperationTree() and buildMergedJoinVar()).

--
Peter Eisentraut              http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
From 8a5af28e7c41055cfbcedd9aa07cf6714b60c178 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <pe...@eisentraut.org>
Date: Tue, 20 Oct 2020 09:28:48 +0200
Subject: [PATCH] Add select_common_typmod()

This accompanies select_common_type() and select_common_collation().
Typmods were previously combined using hand-coded logic in several
places.  The logic in select_common_typmod() isn't very exciting, but
it makes the code more compact and readable in a few locations, and in
the future we can perhaps do more complicated things if desired.
---
 src/backend/parser/analyze.c      | 26 +++++-----------------
 src/backend/parser/parse_clause.c | 23 +++++--------------
 src/backend/parser/parse_coerce.c | 37 +++++++++++++++++++++++++++++++
 src/backend/parser/parse_func.c   |  8 +++++--
 src/include/parser/parse_coerce.h |  2 ++
 5 files changed, 57 insertions(+), 39 deletions(-)

diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index c159fb2957..98a83db8b5 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -1434,9 +1434,8 @@ transformValuesClause(ParseState *pstate, SelectStmt 
*stmt)
        for (i = 0; i < sublist_length; i++)
        {
                Oid                     coltype;
-               int32           coltypmod = -1;
+               int32           coltypmod;
                Oid                     colcoll;
-               bool            first = true;
 
                coltype = select_common_type(pstate, colexprs[i], "VALUES", 
NULL);
 
@@ -1446,19 +1445,9 @@ transformValuesClause(ParseState *pstate, SelectStmt 
*stmt)
 
                        col = coerce_to_common_type(pstate, col, coltype, 
"VALUES");
                        lfirst(lc) = (void *) col;
-                       if (first)
-                       {
-                               coltypmod = exprTypmod(col);
-                               first = false;
-                       }
-                       else
-                       {
-                               /* As soon as we see a non-matching typmod, 
fall back to -1 */
-                               if (coltypmod >= 0 && coltypmod != 
exprTypmod(col))
-                                       coltypmod = -1;
-                       }
                }
 
+               coltypmod = select_common_typmod(pstate, colexprs[i], coltype);
                colcoll = select_common_collation(pstate, colexprs[i], true);
 
                coltypes = lappend_oid(coltypes, coltype);
@@ -2020,8 +2009,6 @@ transformSetOperationTree(ParseState *pstate, SelectStmt 
*stmt,
                        Node       *rcolnode = (Node *) rtle->expr;
                        Oid                     lcoltype = exprType(lcolnode);
                        Oid                     rcoltype = exprType(rcolnode);
-                       int32           lcoltypmod = exprTypmod(lcolnode);
-                       int32           rcoltypmod = exprTypmod(rcolnode);
                        Node       *bestexpr;
                        int                     bestlocation;
                        Oid                     rescoltype;
@@ -2034,11 +2021,6 @@ transformSetOperationTree(ParseState *pstate, SelectStmt 
*stmt,
                                                                                
        context,
                                                                                
        &bestexpr);
                        bestlocation = exprLocation(bestexpr);
-                       /* if same type and same typmod, use typmod; else 
default */
-                       if (lcoltype == rcoltype && lcoltypmod == rcoltypmod)
-                               rescoltypmod = lcoltypmod;
-                       else
-                               rescoltypmod = -1;
 
                        /*
                         * Verify the coercions are actually possible.  If not, 
we'd fail
@@ -2089,6 +2071,10 @@ transformSetOperationTree(ParseState *pstate, SelectStmt 
*stmt,
                                rtle->expr = (Expr *) rcolnode;
                        }
 
+                       rescoltypmod = select_common_typmod(pstate,
+                                                                               
                list_make2(lcolnode, rcolnode),
+                                                                               
                rescoltype);
+
                        /*
                         * Select common collation.  A common collation is 
required for
                         * all set operators except UNION ALL; see SQL:2008 
7.13 <query
diff --git a/src/backend/parser/parse_clause.c 
b/src/backend/parser/parse_clause.c
index edcaf276c0..7460e61160 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -1568,24 +1568,13 @@ buildMergedJoinVar(ParseState *pstate, JoinType 
jointype,
                           *r_node,
                           *res_node;
 
-       /*
-        * Choose output type if input types are dissimilar.
-        */
-       outcoltype = l_colvar->vartype;
-       outcoltypmod = l_colvar->vartypmod;
-       if (outcoltype != r_colvar->vartype)
-       {
-               outcoltype = select_common_type(pstate,
+       outcoltype = select_common_type(pstate,
+                                                                       
list_make2(l_colvar, r_colvar),
+                                                                       
"JOIN/USING",
+                                                                       NULL);
+       outcoltypmod = select_common_typmod(pstate,
                                                                                
list_make2(l_colvar, r_colvar),
-                                                                               
"JOIN/USING",
-                                                                               
NULL);
-               outcoltypmod = -1;              /* ie, unknown */
-       }
-       else if (outcoltypmod != r_colvar->vartypmod)
-       {
-               /* same type, but not same typmod */
-               outcoltypmod = -1;              /* ie, unknown */
-       }
+                                                                               
outcoltype);
 
        /*
         * Insert coercion functions if needed.  Note that a difference in 
typmod
diff --git a/src/backend/parser/parse_coerce.c 
b/src/backend/parser/parse_coerce.c
index 2ffe47026b..38482d24fd 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -1522,6 +1522,43 @@ coerce_to_common_type(ParseState *pstate, Node *node,
        return node;
 }
 
+/*
+ * select_common_typmod()
+ *             Determine the common typmod of a list of input expressions.
+ *
+ * common_type is the selected common type of the expressions, presumbably
+ * computed using select_common_type().
+ */
+int32
+select_common_typmod(ParseState *pstate, List *exprs, Oid common_type)
+{
+       ListCell   *lc;
+       bool            first = true;
+       int32           result = -1;
+
+       foreach(lc, exprs)
+       {
+               Node   *expr = (Node *) lfirst(lc);
+
+               /* Types must match */
+               if (exprType(expr) != common_type)
+                       return -1;
+               else if (first)
+               {
+                       result = exprTypmod(expr);
+                       first = false;
+               }
+               else
+               {
+                       /* As soon as we see a non-matching typmod, fall back 
to -1 */
+                       if (result != exprTypmod(expr))
+                               return -1;
+               }
+       }
+
+       return result;
+}
+
 /*
  * check_generic_type_consistency()
  *             Are the actual arguments potentially compatible with a
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index 9c3b6ad916..a7a31704fb 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -1750,6 +1750,7 @@ unify_hypothetical_args(ParseState *pstate,
                ListCell   *harg = list_nth_cell(fargs, hargpos);
                ListCell   *aarg = list_nth_cell(fargs, aargpos);
                Oid                     commontype;
+               int32           commontypmod;
 
                /* A mismatch means AggregateCreate didn't check properly ... */
                if (declared_arg_types[hargpos] != declared_arg_types[aargpos])
@@ -1768,6 +1769,9 @@ unify_hypothetical_args(ParseState *pstate,
                                                                                
list_make2(lfirst(aarg), lfirst(harg)),
                                                                                
"WITHIN GROUP",
                                                                                
NULL);
+               commontypmod = select_common_typmod(pstate,
+                                                                               
        list_make2(lfirst(aarg), lfirst(harg)),
+                                                                               
        commontype);
 
                /*
                 * Perform the coercions.  We don't need to worry about 
NamedArgExprs
@@ -1776,7 +1780,7 @@ unify_hypothetical_args(ParseState *pstate,
                lfirst(harg) = coerce_type(pstate,
                                                                   (Node *) 
lfirst(harg),
                                                                   
actual_arg_types[hargpos],
-                                                                  commontype, 
-1,
+                                                                  commontype, 
commontypmod,
                                                                   
COERCION_IMPLICIT,
                                                                   
COERCE_IMPLICIT_CAST,
                                                                   -1);
@@ -1784,7 +1788,7 @@ unify_hypothetical_args(ParseState *pstate,
                lfirst(aarg) = coerce_type(pstate,
                                                                   (Node *) 
lfirst(aarg),
                                                                   
actual_arg_types[aargpos],
-                                                                  commontype, 
-1,
+                                                                  commontype, 
commontypmod,
                                                                   
COERCION_IMPLICIT,
                                                                   
COERCE_IMPLICIT_CAST,
                                                                   -1);
diff --git a/src/include/parser/parse_coerce.h 
b/src/include/parser/parse_coerce.h
index 8686eaacbc..33d7cfc8b6 100644
--- a/src/include/parser/parse_coerce.h
+++ b/src/include/parser/parse_coerce.h
@@ -71,6 +71,8 @@ extern Node *coerce_to_common_type(ParseState *pstate, Node 
*node,
                                                                   Oid 
targetTypeId,
                                                                   const char 
*context);
 
+extern int32 select_common_typmod(ParseState *pstate, List *exprs, Oid 
common_type);
+
 extern bool check_generic_type_consistency(const Oid *actual_arg_types,
                                                                                
   const Oid *declared_arg_types,
                                                                                
   int nargs);
-- 
2.28.0

Reply via email to