Re: Typmod associated with multi-row VALUES constructs - Mailing list pgsql-hackers

From Tom Lane
Subject Re: Typmod associated with multi-row VALUES constructs
Date
Msg-id 15128.1481172259@sss.pgh.pa.us
Whole thread Raw
In response to Re: Typmod associated with multi-row VALUES constructs  ("David G. Johnston" <david.g.johnston@gmail.com>)
Responses Re: Typmod associated with multi-row VALUES constructs
List pgsql-hackers
Attached is a patch that fixes this by storing typmod info in the RTE.
This turned out to be straightforward, and I think it's definitely
what we should do in HEAD.  I have mixed emotions about whether it's
worth doing anything about it in the back branches.

I chose to redefine the existing coltypes/coltypmods/colcollations
lists for CTE RTEs as also applying to VALUES RTEs.  That saves a
little space in the RangeTblEntry nodes and allows sharing code
in a couple of places.  It's tempting to consider making that apply
to all RTE types, which would permit collapsing expandRTE() and
get_rte_attribute_type() into a single case.  But AFAICS there would
be no benefit elsewhere, so I'm not sure the extra code churn is
justified.

BTW, I noticed that the CTE case of expandRTE() fails to assign the
specified location to the generated Vars, which is clearly a bug
though a very minor one; it would result in failing to display a
parse error location in some cases where we would do so for Vars from
other RTE types.  That part might be worth back-patching, not sure.

            regards, tom lane

diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index e30c57e..d973225 100644
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
*************** _copyRangeTblEntry(const RangeTblEntry *
*** 2149,2161 ****
      COPY_NODE_FIELD(functions);
      COPY_SCALAR_FIELD(funcordinality);
      COPY_NODE_FIELD(values_lists);
-     COPY_NODE_FIELD(values_collations);
      COPY_STRING_FIELD(ctename);
      COPY_SCALAR_FIELD(ctelevelsup);
      COPY_SCALAR_FIELD(self_reference);
!     COPY_NODE_FIELD(ctecoltypes);
!     COPY_NODE_FIELD(ctecoltypmods);
!     COPY_NODE_FIELD(ctecolcollations);
      COPY_NODE_FIELD(alias);
      COPY_NODE_FIELD(eref);
      COPY_SCALAR_FIELD(lateral);
--- 2149,2160 ----
      COPY_NODE_FIELD(functions);
      COPY_SCALAR_FIELD(funcordinality);
      COPY_NODE_FIELD(values_lists);
      COPY_STRING_FIELD(ctename);
      COPY_SCALAR_FIELD(ctelevelsup);
      COPY_SCALAR_FIELD(self_reference);
!     COPY_NODE_FIELD(coltypes);
!     COPY_NODE_FIELD(coltypmods);
!     COPY_NODE_FIELD(colcollations);
      COPY_NODE_FIELD(alias);
      COPY_NODE_FIELD(eref);
      COPY_SCALAR_FIELD(lateral);
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index b7a109c..edc1797 100644
*** a/src/backend/nodes/equalfuncs.c
--- b/src/backend/nodes/equalfuncs.c
*************** _equalRangeTblEntry(const RangeTblEntry
*** 2460,2472 ****
      COMPARE_NODE_FIELD(functions);
      COMPARE_SCALAR_FIELD(funcordinality);
      COMPARE_NODE_FIELD(values_lists);
-     COMPARE_NODE_FIELD(values_collations);
      COMPARE_STRING_FIELD(ctename);
      COMPARE_SCALAR_FIELD(ctelevelsup);
      COMPARE_SCALAR_FIELD(self_reference);
!     COMPARE_NODE_FIELD(ctecoltypes);
!     COMPARE_NODE_FIELD(ctecoltypmods);
!     COMPARE_NODE_FIELD(ctecolcollations);
      COMPARE_NODE_FIELD(alias);
      COMPARE_NODE_FIELD(eref);
      COMPARE_SCALAR_FIELD(lateral);
--- 2460,2471 ----
      COMPARE_NODE_FIELD(functions);
      COMPARE_SCALAR_FIELD(funcordinality);
      COMPARE_NODE_FIELD(values_lists);
      COMPARE_STRING_FIELD(ctename);
      COMPARE_SCALAR_FIELD(ctelevelsup);
      COMPARE_SCALAR_FIELD(self_reference);
!     COMPARE_NODE_FIELD(coltypes);
!     COMPARE_NODE_FIELD(coltypmods);
!     COMPARE_NODE_FIELD(colcollations);
      COMPARE_NODE_FIELD(alias);
      COMPARE_NODE_FIELD(eref);
      COMPARE_SCALAR_FIELD(lateral);
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 0d858f5..7258c03 100644
*** a/src/backend/nodes/outfuncs.c
--- b/src/backend/nodes/outfuncs.c
*************** _outRangeTblEntry(StringInfo str, const
*** 2841,2855 ****
              break;
          case RTE_VALUES:
              WRITE_NODE_FIELD(values_lists);
!             WRITE_NODE_FIELD(values_collations);
              break;
          case RTE_CTE:
              WRITE_STRING_FIELD(ctename);
              WRITE_UINT_FIELD(ctelevelsup);
              WRITE_BOOL_FIELD(self_reference);
!             WRITE_NODE_FIELD(ctecoltypes);
!             WRITE_NODE_FIELD(ctecoltypmods);
!             WRITE_NODE_FIELD(ctecolcollations);
              break;
          default:
              elog(ERROR, "unrecognized RTE kind: %d", (int) node->rtekind);
--- 2841,2857 ----
              break;
          case RTE_VALUES:
              WRITE_NODE_FIELD(values_lists);
!             WRITE_NODE_FIELD(coltypes);
!             WRITE_NODE_FIELD(coltypmods);
!             WRITE_NODE_FIELD(colcollations);
              break;
          case RTE_CTE:
              WRITE_STRING_FIELD(ctename);
              WRITE_UINT_FIELD(ctelevelsup);
              WRITE_BOOL_FIELD(self_reference);
!             WRITE_NODE_FIELD(coltypes);
!             WRITE_NODE_FIELD(coltypmods);
!             WRITE_NODE_FIELD(colcollations);
              break;
          default:
              elog(ERROR, "unrecognized RTE kind: %d", (int) node->rtekind);
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index c587d4e..d608530 100644
*** a/src/backend/nodes/readfuncs.c
--- b/src/backend/nodes/readfuncs.c
*************** _readRangeTblEntry(void)
*** 1314,1328 ****
              break;
          case RTE_VALUES:
              READ_NODE_FIELD(values_lists);
!             READ_NODE_FIELD(values_collations);
              break;
          case RTE_CTE:
              READ_STRING_FIELD(ctename);
              READ_UINT_FIELD(ctelevelsup);
              READ_BOOL_FIELD(self_reference);
!             READ_NODE_FIELD(ctecoltypes);
!             READ_NODE_FIELD(ctecoltypmods);
!             READ_NODE_FIELD(ctecolcollations);
              break;
          default:
              elog(ERROR, "unrecognized RTE kind: %d",
--- 1314,1330 ----
              break;
          case RTE_VALUES:
              READ_NODE_FIELD(values_lists);
!             READ_NODE_FIELD(coltypes);
!             READ_NODE_FIELD(coltypmods);
!             READ_NODE_FIELD(colcollations);
              break;
          case RTE_CTE:
              READ_STRING_FIELD(ctename);
              READ_UINT_FIELD(ctelevelsup);
              READ_BOOL_FIELD(self_reference);
!             READ_NODE_FIELD(coltypes);
!             READ_NODE_FIELD(coltypmods);
!             READ_NODE_FIELD(colcollations);
              break;
          default:
              elog(ERROR, "unrecognized RTE kind: %d",
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index d91bc3b..2fe1c8c 100644
*** a/src/backend/optimizer/plan/setrefs.c
--- b/src/backend/optimizer/plan/setrefs.c
*************** add_rte_to_flat_rtable(PlannerGlobal *gl
*** 396,405 ****
      newrte->joinaliasvars = NIL;
      newrte->functions = NIL;
      newrte->values_lists = NIL;
!     newrte->values_collations = NIL;
!     newrte->ctecoltypes = NIL;
!     newrte->ctecoltypmods = NIL;
!     newrte->ctecolcollations = NIL;
      newrte->securityQuals = NIL;

      glob->finalrtable = lappend(glob->finalrtable, newrte);
--- 396,404 ----
      newrte->joinaliasvars = NIL;
      newrte->functions = NIL;
      newrte->values_lists = NIL;
!     newrte->coltypes = NIL;
!     newrte->coltypmods = NIL;
!     newrte->colcollations = NIL;
      newrte->securityQuals = NIL;

      glob->finalrtable = lappend(glob->finalrtable, newrte);
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 7364346..5e65fe7 100644
*** a/src/backend/parser/analyze.c
--- b/src/backend/parser/analyze.c
*************** transformInsertStmt(ParseState *pstate,
*** 633,642 ****
           * RTE.
           */
          List       *exprsLists = NIL;
!         List       *collations = NIL;
          int            sublist_length = -1;
          bool        lateral = false;
-         int            i;

          Assert(selectStmt->intoClause == NULL);

--- 633,643 ----
           * RTE.
           */
          List       *exprsLists = NIL;
!         List       *coltypes = NIL;
!         List       *coltypmods = NIL;
!         List       *colcollations = NIL;
          int            sublist_length = -1;
          bool        lateral = false;

          Assert(selectStmt->intoClause == NULL);

*************** transformInsertStmt(ParseState *pstate,
*** 703,713 ****
          }

          /*
!          * Although we don't really need collation info, let's just make sure
!          * we provide a correctly-sized list in the VALUES RTE.
           */
!         for (i = 0; i < sublist_length; i++)
!             collations = lappend_oid(collations, InvalidOid);

          /*
           * Ordinarily there can't be any current-level Vars in the expression
--- 704,723 ----
          }

          /*
!          * Construct column type/typmod/collation lists for the VALUES RTE.
!          * Every expression in each column has been coerced to the type/typmod
!          * of the corresponding target column or subfield, so it's sufficient
!          * to look at the exprType/exprTypmod of the first row.  We don't care
!          * about the collation labeling, so just fill in InvalidOid for that.
           */
!         foreach(lc, (List *) linitial(exprsLists))
!         {
!             Node       *val = (Node *) lfirst(lc);
!
!             coltypes = lappend_oid(coltypes, exprType(val));
!             coltypmods = lappend_int(coltypmods, exprTypmod(val));
!             colcollations = lappend_oid(colcollations, InvalidOid);
!         }

          /*
           * Ordinarily there can't be any current-level Vars in the expression
*************** transformInsertStmt(ParseState *pstate,
*** 722,728 ****
          /*
           * Generate the VALUES RTE
           */
!         rte = addRangeTableEntryForValues(pstate, exprsLists, collations,
                                            NULL, lateral, true);
          rtr = makeNode(RangeTblRef);
          /* assume new rte is at end */
--- 732,739 ----
          /*
           * Generate the VALUES RTE
           */
!         rte = addRangeTableEntryForValues(pstate, exprsLists,
!                                           coltypes, coltypmods, colcollations,
                                            NULL, lateral, true);
          rtr = makeNode(RangeTblRef);
          /* assume new rte is at end */
*************** transformValuesClause(ParseState *pstate
*** 1274,1280 ****
  {
      Query       *qry = makeNode(Query);
      List       *exprsLists;
!     List       *collations;
      List      **colexprs = NULL;
      int            sublist_length = -1;
      bool        lateral = false;
--- 1285,1293 ----
  {
      Query       *qry = makeNode(Query);
      List       *exprsLists;
!     List       *coltypes = NIL;
!     List       *coltypmods = NIL;
!     List       *colcollations = NIL;
      List      **colexprs = NULL;
      int            sublist_length = -1;
      bool        lateral = false;
*************** transformValuesClause(ParseState *pstate
*** 1360,1367 ****

      /*
       * Now resolve the common types of the columns, and coerce everything to
!      * those types.  Then identify the common collation, if any, of each
!      * column.
       *
       * We must do collation processing now because (1) assign_query_collations
       * doesn't process rangetable entries, and (2) we need to label the VALUES
--- 1373,1380 ----

      /*
       * Now resolve the common types of the columns, and coerce everything to
!      * those types.  Then identify the common typmod and common collation, if
!      * any, of each column.
       *
       * We must do collation processing now because (1) assign_query_collations
       * doesn't process rangetable entries, and (2) we need to label the VALUES
*************** transformValuesClause(ParseState *pstate
*** 1372,1382 ****
       *
       * Note we modify the per-column expression lists in-place.
       */
-     collations = NIL;
      for (i = 0; i < sublist_length; i++)
      {
          Oid            coltype;
          Oid            colcoll;

          coltype = select_common_type(pstate, colexprs[i], "VALUES", NULL);

--- 1385,1396 ----
       *
       * Note we modify the per-column expression lists in-place.
       */
      for (i = 0; i < sublist_length; i++)
      {
          Oid            coltype;
+         int32        coltypmod = -1;
          Oid            colcoll;
+         bool        first = true;

          coltype = select_common_type(pstate, colexprs[i], "VALUES", NULL);

*************** transformValuesClause(ParseState *pstate
*** 1386,1396 ****

              col = coerce_to_common_type(pstate, col, coltype, "VALUES");
              lfirst(lc) = (void *) col;
          }

          colcoll = select_common_collation(pstate, colexprs[i], true);

!         collations = lappend_oid(collations, colcoll);
      }

      /*
--- 1400,1423 ----

              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;
+             }
          }

          colcoll = select_common_collation(pstate, colexprs[i], true);

!         coltypes = lappend_oid(coltypes, coltype);
!         coltypmods = lappend_int(coltypmods, coltypmod);
!         colcollations = lappend_oid(colcollations, colcoll);
      }

      /*
*************** transformValuesClause(ParseState *pstate
*** 1432,1438 ****
      /*
       * Generate the VALUES RTE
       */
!     rte = addRangeTableEntryForValues(pstate, exprsLists, collations,
                                        NULL, lateral, true);
      addRTEtoQuery(pstate, rte, true, true, true);

--- 1459,1466 ----
      /*
       * Generate the VALUES RTE
       */
!     rte = addRangeTableEntryForValues(pstate, exprsLists,
!                                       coltypes, coltypmods, colcollations,
                                        NULL, lateral, true);
      addRTEtoQuery(pstate, rte, true, true, true);

diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c
index 1e3ecbc..58f7050 100644
*** a/src/backend/parser/parse_relation.c
--- b/src/backend/parser/parse_relation.c
*************** addRangeTableEntryForFunction(ParseState
*** 1635,1641 ****
  RangeTblEntry *
  addRangeTableEntryForValues(ParseState *pstate,
                              List *exprs,
!                             List *collations,
                              Alias *alias,
                              bool lateral,
                              bool inFromCl)
--- 1635,1643 ----
  RangeTblEntry *
  addRangeTableEntryForValues(ParseState *pstate,
                              List *exprs,
!                             List *coltypes,
!                             List *coltypmods,
!                             List *colcollations,
                              Alias *alias,
                              bool lateral,
                              bool inFromCl)
*************** addRangeTableEntryForValues(ParseState *
*** 1652,1658 ****
      rte->relid = InvalidOid;
      rte->subquery = NULL;
      rte->values_lists = exprs;
!     rte->values_collations = collations;
      rte->alias = alias;

      eref = alias ? copyObject(alias) : makeAlias(refname, NIL);
--- 1654,1662 ----
      rte->relid = InvalidOid;
      rte->subquery = NULL;
      rte->values_lists = exprs;
!     rte->coltypes = coltypes;
!     rte->coltypmods = coltypmods;
!     rte->colcollations = colcollations;
      rte->alias = alias;

      eref = alias ? copyObject(alias) : makeAlias(refname, NIL);
*************** addRangeTableEntryForCTE(ParseState *pst
*** 1822,1830 ****
                       parser_errposition(pstate, rv->location)));
      }

!     rte->ctecoltypes = cte->ctecoltypes;
!     rte->ctecoltypmods = cte->ctecoltypmods;
!     rte->ctecolcollations = cte->ctecolcollations;

      rte->alias = alias;
      if (alias)
--- 1826,1834 ----
                       parser_errposition(pstate, rv->location)));
      }

!     rte->coltypes = cte->ctecoltypes;
!     rte->coltypmods = cte->ctecoltypmods;
!     rte->colcollations = cte->ctecolcollations;

      rte->alias = alias;
      if (alias)
*************** expandRTE(RangeTblEntry *rte, int rtinde
*** 2153,2198 ****
                  }
              }
              break;
-         case RTE_VALUES:
-             {
-                 /* Values RTE */
-                 ListCell   *aliasp_item = list_head(rte->eref->colnames);
-                 ListCell   *lcv;
-                 ListCell   *lcc;
-
-                 varattno = 0;
-                 forboth(lcv, (List *) linitial(rte->values_lists),
-                         lcc, rte->values_collations)
-                 {
-                     Node       *col = (Node *) lfirst(lcv);
-                     Oid            colcollation = lfirst_oid(lcc);
-
-                     varattno++;
-                     if (colnames)
-                     {
-                         /* Assume there is one alias per column */
-                         char       *label = strVal(lfirst(aliasp_item));
-
-                         *colnames = lappend(*colnames,
-                                             makeString(pstrdup(label)));
-                         aliasp_item = lnext(aliasp_item);
-                     }
-
-                     if (colvars)
-                     {
-                         Var           *varnode;
-
-                         varnode = makeVar(rtindex, varattno,
-                                           exprType(col),
-                                           exprTypmod(col),
-                                           colcollation,
-                                           sublevels_up);
-                         varnode->location = location;
-                         *colvars = lappend(*colvars, varnode);
-                     }
-                 }
-             }
-             break;
          case RTE_JOIN:
              {
                  /* Join RTE */
--- 2157,2162 ----
*************** expandRTE(RangeTblEntry *rte, int rtinde
*** 2262,2278 ****
                  }
              }
              break;
          case RTE_CTE:
              {
                  ListCell   *aliasp_item = list_head(rte->eref->colnames);
                  ListCell   *lct;
                  ListCell   *lcm;
                  ListCell   *lcc;

                  varattno = 0;
!                 forthree(lct, rte->ctecoltypes,
!                          lcm, rte->ctecoltypmods,
!                          lcc, rte->ctecolcollations)
                  {
                      Oid            coltype = lfirst_oid(lct);
                      int32        coltypmod = lfirst_int(lcm);
--- 2226,2244 ----
                  }
              }
              break;
+         case RTE_VALUES:
          case RTE_CTE:
              {
+                 /* Values or CTE RTE */
                  ListCell   *aliasp_item = list_head(rte->eref->colnames);
                  ListCell   *lct;
                  ListCell   *lcm;
                  ListCell   *lcc;

                  varattno = 0;
!                 forthree(lct, rte->coltypes,
!                          lcm, rte->coltypmods,
!                          lcc, rte->colcollations)
                  {
                      Oid            coltype = lfirst_oid(lct);
                      int32        coltypmod = lfirst_int(lcm);
*************** expandRTE(RangeTblEntry *rte, int rtinde
*** 2285,2291 ****
                          /* Assume there is one alias per output column */
                          char       *label = strVal(lfirst(aliasp_item));

!                         *colnames = lappend(*colnames, makeString(pstrdup(label)));
                          aliasp_item = lnext(aliasp_item);
                      }

--- 2251,2258 ----
                          /* Assume there is one alias per output column */
                          char       *label = strVal(lfirst(aliasp_item));

!                         *colnames = lappend(*colnames,
!                                             makeString(pstrdup(label)));
                          aliasp_item = lnext(aliasp_item);
                      }

*************** expandRTE(RangeTblEntry *rte, int rtinde
*** 2296,2301 ****
--- 2263,2270 ----
                          varnode = makeVar(rtindex, varattno,
                                            coltype, coltypmod, colcoll,
                                            sublevels_up);
+                         varnode->location = location;
+
                          *colvars = lappend(*colvars, varnode);
                      }
                  }
*************** get_rte_attribute_type(RangeTblEntry *rt
*** 2654,2675 ****
                                  rte->eref->aliasname)));
              }
              break;
-         case RTE_VALUES:
-             {
-                 /* Values RTE --- get type info from first sublist */
-                 /* collation is stored separately, though */
-                 List       *collist = (List *) linitial(rte->values_lists);
-                 Node       *col;
-
-                 if (attnum < 1 || attnum > list_length(collist))
-                     elog(ERROR, "values list %s does not have attribute %d",
-                          rte->eref->aliasname, attnum);
-                 col = (Node *) list_nth(collist, attnum - 1);
-                 *vartype = exprType(col);
-                 *vartypmod = exprTypmod(col);
-                 *varcollid = list_nth_oid(rte->values_collations, attnum - 1);
-             }
-             break;
          case RTE_JOIN:
              {
                  /*
--- 2623,2628 ----
*************** get_rte_attribute_type(RangeTblEntry *rt
*** 2685,2697 ****
                  *varcollid = exprCollation(aliasvar);
              }
              break;
          case RTE_CTE:
              {
!                 /* CTE RTE --- get type info from lists in the RTE */
!                 Assert(attnum > 0 && attnum <= list_length(rte->ctecoltypes));
!                 *vartype = list_nth_oid(rte->ctecoltypes, attnum - 1);
!                 *vartypmod = list_nth_int(rte->ctecoltypmods, attnum - 1);
!                 *varcollid = list_nth_oid(rte->ctecolcollations, attnum - 1);
              }
              break;
          default:
--- 2638,2651 ----
                  *varcollid = exprCollation(aliasvar);
              }
              break;
+         case RTE_VALUES:
          case RTE_CTE:
              {
!                 /* VALUES or CTE RTE --- get type info from lists in the RTE */
!                 Assert(attnum > 0 && attnum <= list_length(rte->coltypes));
!                 *vartype = list_nth_oid(rte->coltypes, attnum - 1);
!                 *vartypmod = list_nth_int(rte->coltypmods, attnum - 1);
!                 *varcollid = list_nth_oid(rte->colcollations, attnum - 1);
              }
              break;
          default:
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 6b95c48..fc532fb 100644
*** a/src/include/nodes/parsenodes.h
--- b/src/include/nodes/parsenodes.h
*************** typedef struct RangeTblEntry
*** 927,933 ****
       * Fields valid for a values RTE (else NIL):
       */
      List       *values_lists;    /* list of expression lists */
-     List       *values_collations;        /* OID list of column collation OIDs */

      /*
       * Fields valid for a CTE RTE (else NULL/zero):
--- 927,932 ----
*************** typedef struct RangeTblEntry
*** 935,943 ****
      char       *ctename;        /* name of the WITH list item */
      Index        ctelevelsup;    /* number of query levels up */
      bool        self_reference; /* is this a recursive self-reference? */
!     List       *ctecoltypes;    /* OID list of column type OIDs */
!     List       *ctecoltypmods;    /* integer list of column typmods */
!     List       *ctecolcollations;        /* OID list of column collation OIDs */

      /*
       * Fields valid in all RTEs:
--- 934,950 ----
      char       *ctename;        /* name of the WITH list item */
      Index        ctelevelsup;    /* number of query levels up */
      bool        self_reference; /* is this a recursive self-reference? */
!
!     /*
!      * Fields valid for values and CTE RTEs (else NIL):
!      *
!      * We need these for CTE RTEs so that the types of self-referential
!      * columns are well-defined.  For VALUES RTEs, storing these explicitly
!      * saves having to re-determine the info by scanning the values_lists.
!      */
!     List       *coltypes;        /* OID list of column type OIDs */
!     List       *coltypmods;        /* integer list of column typmods */
!     List       *colcollations;    /* OID list of column collation OIDs */

      /*
       * Fields valid in all RTEs:
diff --git a/src/include/parser/parse_relation.h b/src/include/parser/parse_relation.h
index 3ef3d7b..9463f9d 100644
*** a/src/include/parser/parse_relation.h
--- b/src/include/parser/parse_relation.h
*************** extern RangeTblEntry *addRangeTableEntry
*** 85,91 ****
                                bool inFromCl);
  extern RangeTblEntry *addRangeTableEntryForValues(ParseState *pstate,
                              List *exprs,
!                             List *collations,
                              Alias *alias,
                              bool lateral,
                              bool inFromCl);
--- 85,93 ----
                                bool inFromCl);
  extern RangeTblEntry *addRangeTableEntryForValues(ParseState *pstate,
                              List *exprs,
!                             List *coltypes,
!                             List *coltypmods,
!                             List *colcollations,
                              Alias *alias,
                              bool lateral,
                              bool inFromCl);
diff --git a/src/test/regress/expected/create_view.out b/src/test/regress/expected/create_view.out
index 66ed2c8..15ceefe 100644
*** a/src/test/regress/expected/create_view.out
--- b/src/test/regress/expected/create_view.out
*************** SELECT relname, relkind, reloptions FROM
*** 288,293 ****
--- 288,317 ----
   mysecview4 | v       | {security_barrier=false}
  (4 rows)

+ -- This test checks that proper typmods are assigned in a multi-row VALUES
+ CREATE TABLE tt1 AS
+   SELECT * FROM (
+     VALUES
+        ('abc'::varchar(3), '0123456789', 42, 'abcd'::varchar(4)),
+        ('0123456789', 'abc'::varchar(3), 42.12, 'abc'::varchar(4))
+   ) vv(a,b,c,d);
+ \d tt1
+                    Table "testviewschm2.tt1"
+  Column |         Type         | Collation | Nullable | Default
+ --------+----------------------+-----------+----------+---------
+  a      | character varying    |           |          |
+  b      | character varying    |           |          |
+  c      | numeric              |           |          |
+  d      | character varying(4) |           |          |
+
+ SELECT * FROM tt1;
+      a      |     b      |   c   |  d
+ ------------+------------+-------+------
+  abc        | 0123456789 |    42 | abcd
+  0123456789 | abc        | 42.12 | abc
+ (2 rows)
+
+ DROP TABLE tt1;
  -- Test view decompilation in the face of relation renaming conflicts
  CREATE TABLE tt1 (f1 int, f2 int, f3 text);
  CREATE TABLE tx1 (x1 int, x2 int, x3 text);
diff --git a/src/test/regress/sql/create_view.sql b/src/test/regress/sql/create_view.sql
index 8bed5a5..c3391f9 100644
*** a/src/test/regress/sql/create_view.sql
--- b/src/test/regress/sql/create_view.sql
*************** SELECT relname, relkind, reloptions FROM
*** 224,229 ****
--- 224,241 ----
                       'mysecview3'::regclass, 'mysecview4'::regclass)
         ORDER BY relname;

+ -- This test checks that proper typmods are assigned in a multi-row VALUES
+
+ CREATE TABLE tt1 AS
+   SELECT * FROM (
+     VALUES
+        ('abc'::varchar(3), '0123456789', 42, 'abcd'::varchar(4)),
+        ('0123456789', 'abc'::varchar(3), 42.12, 'abc'::varchar(4))
+   ) vv(a,b,c,d);
+ \d tt1
+ SELECT * FROM tt1;
+ DROP TABLE tt1;
+
  -- Test view decompilation in the face of relation renaming conflicts

  CREATE TABLE tt1 (f1 int, f2 int, f3 text);

pgsql-hackers by date:

Previous
From: Michael Paquier
Date:
Subject: Re: Declarative partitioning - another take
Next
From: Pavel Stehule
Date:
Subject: Re: Typmod associated with multi-row VALUES constructs