diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2016-12-08 11:40:02 -0500 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2016-12-08 11:40:02 -0500 |
commit | 0b78106cd4651ad7867036a463ec743f6d3d2e86 (patch) | |
tree | 76fca8c917ad661ee42fbd56ed609c77febb04b5 /src/backend/parser/analyze.c | |
parent | 2560d244b4c9fc08f1d076b3c40913bec5f7e31f (diff) |
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
Diffstat (limited to 'src/backend/parser/analyze.c')
-rw-r--r-- | src/backend/parser/analyze.c | 54 |
1 files changed, 41 insertions, 13 deletions
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 73643461672..5e65fe75bd5 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -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); |