mirror of
https://github.com/postgres/postgres.git
synced 2025-09-02 04:21:28 +03:00
Fix reporting of column typmods for multi-row VALUES constructs.
expandRTE() and get_rte_attribute_type() reported the exprType() and exprTypmod() values of the expressions in the first row of the VALUES as being the column type/typmod returned by the VALUES RTE. That's fine for the data type, since we coerce all expressions in a column to have the same common type. But we don't coerce them to have a common typmod, so it was possible for rows after the first one to return values that violate the claimed column typmod. This leads to the incorrect result seen in bug #14448 from Hassan Mahmood, as well as some other corner-case misbehaviors. The desired behavior is the same as we use in other type-unification cases: report the common typmod if there is one, but otherwise return -1 indicating no particular constraint. It's cheap for transformValuesClause to determine the common typmod while transforming a multi-row VALUES, but it'd be less cheap for expandRTE() and get_rte_attribute_type() to re-determine that info every time they're asked --- possibly a lot less cheap, if the VALUES has many rows. Therefore, the best fix is to record the common typmods explicitly in a list in the VALUES RTE, as we were already doing for column collations. This looks quite a bit like what we're doing for CTE RTEs, so we can save a little bit of space and code by unifying the representation for those two RTE types. They both now share coltypes/coltypmods/colcollations fields. (At some point it might seem desirable to populate those fields for all RTE types; but right now it looks like constructing them for other RTE types would add more code and cycles than it would save.) The RTE change requires a catversion bump, so this fix is only usable in HEAD. If we fix this at all in the back branches, the patch will need to look quite different. Report: https://postgr.es/m/20161205143037.4377.60754@wrigleys.postgresql.org Discussion: https://postgr.es/m/27429.1480968538@sss.pgh.pa.us
This commit is contained in:
@@ -633,10 +633,11 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
|
||||
* RTE.
|
||||
*/
|
||||
List *exprsLists = NIL;
|
||||
List *collations = NIL;
|
||||
List *coltypes = NIL;
|
||||
List *coltypmods = NIL;
|
||||
List *colcollations = NIL;
|
||||
int sublist_length = -1;
|
||||
bool lateral = false;
|
||||
int i;
|
||||
|
||||
Assert(selectStmt->intoClause == NULL);
|
||||
|
||||
@@ -703,11 +704,20 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
|
||||
}
|
||||
|
||||
/*
|
||||
* Although we don't really need collation info, let's just make sure
|
||||
* we provide a correctly-sized list in the VALUES RTE.
|
||||
* 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.
|
||||
*/
|
||||
for (i = 0; i < sublist_length; i++)
|
||||
collations = lappend_oid(collations, InvalidOid);
|
||||
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
|
||||
@@ -722,7 +732,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
|
||||
/*
|
||||
* Generate the VALUES RTE
|
||||
*/
|
||||
rte = addRangeTableEntryForValues(pstate, exprsLists, collations,
|
||||
rte = addRangeTableEntryForValues(pstate, exprsLists,
|
||||
coltypes, coltypmods, colcollations,
|
||||
NULL, lateral, true);
|
||||
rtr = makeNode(RangeTblRef);
|
||||
/* assume new rte is at end */
|
||||
@@ -1274,7 +1285,9 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt)
|
||||
{
|
||||
Query *qry = makeNode(Query);
|
||||
List *exprsLists;
|
||||
List *collations;
|
||||
List *coltypes = NIL;
|
||||
List *coltypmods = NIL;
|
||||
List *colcollations = NIL;
|
||||
List **colexprs = NULL;
|
||||
int sublist_length = -1;
|
||||
bool lateral = false;
|
||||
@@ -1360,8 +1373,8 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt)
|
||||
|
||||
/*
|
||||
* Now resolve the common types of the columns, and coerce everything to
|
||||
* those types. Then identify the common collation, if any, of each
|
||||
* column.
|
||||
* 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
|
||||
@@ -1372,11 +1385,12 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt)
|
||||
*
|
||||
* Note we modify the per-column expression lists in-place.
|
||||
*/
|
||||
collations = NIL;
|
||||
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);
|
||||
|
||||
@@ -1386,11 +1400,24 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
colcoll = select_common_collation(pstate, colexprs[i], true);
|
||||
|
||||
collations = lappend_oid(collations, colcoll);
|
||||
coltypes = lappend_oid(coltypes, coltype);
|
||||
coltypmods = lappend_int(coltypmods, coltypmod);
|
||||
colcollations = lappend_oid(colcollations, colcoll);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1432,7 +1459,8 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt)
|
||||
/*
|
||||
* Generate the VALUES RTE
|
||||
*/
|
||||
rte = addRangeTableEntryForValues(pstate, exprsLists, collations,
|
||||
rte = addRangeTableEntryForValues(pstate, exprsLists,
|
||||
coltypes, coltypmods, colcollations,
|
||||
NULL, lateral, true);
|
||||
addRTEtoQuery(pstate, rte, true, true, true);
|
||||
|
||||
|
Reference in New Issue
Block a user