aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/backend/catalog/dependency.c13
-rw-r--r--src/backend/nodes/copyfuncs.c1
-rw-r--r--src/backend/nodes/equalfuncs.c1
-rw-r--r--src/backend/nodes/outfuncs.c1
-rw-r--r--src/backend/nodes/readfuncs.c1
-rw-r--r--src/backend/optimizer/plan/setrefs.c1
-rw-r--r--src/backend/parser/analyze.c125
-rw-r--r--src/backend/parser/parse_relation.c24
-rw-r--r--src/include/catalog/catversion.h2
-rw-r--r--src/include/nodes/parsenodes.h3
-rw-r--r--src/include/parser/parse_relation.h1
-rw-r--r--src/test/regress/expected/collate.out8
-rw-r--r--src/test/regress/sql/collate.sql3
13 files changed, 131 insertions, 53 deletions
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index ec9bb48c638..b3ed946530e 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -1671,10 +1671,15 @@ find_expr_references_walker(Node *node,
/*
* Add whole-relation refs for each plain relation mentioned in the
* subquery's rtable, as well as refs for any datatypes and collations
- * used in a RECORD function's output. (Note: query_tree_walker takes
- * care of recursing into RTE_FUNCTION RTEs, subqueries, etc, so no
- * need to do that here. But keep it from looking at join alias
- * lists.)
+ * used in a RECORD function's output.
+ *
+ * Note: query_tree_walker takes care of recursing into RTE_FUNCTION
+ * RTEs, subqueries, etc, so no need to do that here. But keep it
+ * from looking at join alias lists.
+ *
+ * Note: we don't need to worry about collations mentioned in
+ * RTE_VALUES or RTE_CTE RTEs, because those must just duplicate
+ * collations referenced in other parts of the Query.
*/
foreach(lc, query->rtable)
{
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index c0d2294317e..c9133ddfa19 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -1951,6 +1951,7 @@ _copyRangeTblEntry(RangeTblEntry *from)
COPY_NODE_FIELD(funccoltypmods);
COPY_NODE_FIELD(funccolcollations);
COPY_NODE_FIELD(values_lists);
+ COPY_NODE_FIELD(values_collations);
COPY_STRING_FIELD(ctename);
COPY_SCALAR_FIELD(ctelevelsup);
COPY_SCALAR_FIELD(self_reference);
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index c8110775635..3a0267c4191 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -2310,6 +2310,7 @@ _equalRangeTblEntry(RangeTblEntry *a, RangeTblEntry *b)
COMPARE_NODE_FIELD(funccoltypmods);
COMPARE_NODE_FIELD(funccolcollations);
COMPARE_NODE_FIELD(values_lists);
+ COMPARE_NODE_FIELD(values_collations);
COMPARE_STRING_FIELD(ctename);
COMPARE_SCALAR_FIELD(ctelevelsup);
COMPARE_SCALAR_FIELD(self_reference);
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 47f3523366e..681f5f85bc7 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2324,6 +2324,7 @@ _outRangeTblEntry(StringInfo str, RangeTblEntry *node)
break;
case RTE_VALUES:
WRITE_NODE_FIELD(values_lists);
+ WRITE_NODE_FIELD(values_collations);
break;
case RTE_CTE:
WRITE_STRING_FIELD(ctename);
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 5f1fd32b9f2..22885147cfb 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -1203,6 +1203,7 @@ _readRangeTblEntry(void)
break;
case RTE_VALUES:
READ_NODE_FIELD(values_lists);
+ READ_NODE_FIELD(values_collations);
break;
case RTE_CTE:
READ_STRING_FIELD(ctename);
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 432d6483be1..60a1484c992 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -216,6 +216,7 @@ set_plan_references(PlannerGlobal *glob, Plan *plan,
newrte->funccoltypmods = NIL;
newrte->funccolcollations = NIL;
newrte->values_lists = NIL;
+ newrte->values_collations = NIL;
newrte->ctecoltypes = NIL;
newrte->ctecoltypmods = NIL;
newrte->ctecolcollations = NIL;
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index e4e83a67165..4947a7d837e 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -536,7 +536,9 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
* RTE.
*/
List *exprsLists = NIL;
+ List *collations = NIL;
int sublist_length = -1;
+ int i;
foreach(lc, selectStmt->valuesLists)
{
@@ -573,7 +575,13 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
* We must assign collations now because assign_query_collations
* doesn't process rangetable entries. We just assign all the
* collations independently in each row, and don't worry about
- * whether they are consistent vertically either.
+ * whether they are consistent vertically. The outer INSERT query
+ * isn't going to care about the collations of the VALUES columns,
+ * so it's not worth the effort to identify a common collation for
+ * each one here. (But note this does have one user-visible
+ * consequence: INSERT ... VALUES won't complain about conflicting
+ * explicit COLLATEs in a column, whereas the same VALUES
+ * construct in another context would complain.)
*/
assign_list_collations(pstate, sublist);
@@ -581,6 +589,13 @@ 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.
+ */
+ for (i = 0; i < sublist_length; i++)
+ collations = lappend_oid(collations, InvalidOid);
+
+ /*
* There mustn't have been any table references in the expressions,
* else strange things would happen, like Cartesian products of those
* tables with the VALUES list ...
@@ -610,7 +625,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
/*
* Generate the VALUES RTE
*/
- rte = addRangeTableEntryForValues(pstate, exprsLists, NULL, true);
+ rte = addRangeTableEntryForValues(pstate, exprsLists, collations,
+ NULL, true);
rtr = makeNode(RangeTblRef);
/* assume new rte is at end */
rtr->rtindex = list_length(pstate->p_rtable);
@@ -989,11 +1005,10 @@ static Query *
transformValuesClause(ParseState *pstate, SelectStmt *stmt)
{
Query *qry = makeNode(Query);
- List *exprsLists = NIL;
+ List *exprsLists;
+ List *collations;
List **colexprs = NULL;
- Oid *coltypes = NULL;
int sublist_length = -1;
- List *newExprsLists;
RangeTblEntry *rte;
RangeTblRef *rtr;
ListCell *lc;
@@ -1021,9 +1036,13 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt)
}
/*
- * For each row of VALUES, transform the raw expressions and gather type
- * information. This is also a handy place to reject DEFAULT nodes, which
- * the grammar allows for simplicity.
+ * For each row of VALUES, transform the raw expressions. This is also a
+ * handy place to reject DEFAULT nodes, which the grammar allows for
+ * simplicity.
+ *
+ * Note that the intermediate representation we build is column-organized
+ * not row-organized. That simplifies the type and collation processing
+ * below.
*/
foreach(lc, stmt->valuesLists)
{
@@ -1041,9 +1060,8 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt)
{
/* Remember post-transformation length of first sublist */
sublist_length = list_length(sublist);
- /* and allocate arrays for per-column info */
+ /* and allocate array for per-column lists */
colexprs = (List **) palloc0(sublist_length * sizeof(List *));
- coltypes = (Oid *) palloc0(sublist_length * sizeof(Oid));
}
else if (sublist_length != list_length(sublist))
{
@@ -1054,8 +1072,6 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt)
exprLocation((Node *) sublist))));
}
- exprsLists = lappend(exprsLists, sublist);
-
/* Check for DEFAULT and build per-column expression lists */
i = 0;
foreach(lc2, sublist)
@@ -1070,48 +1086,77 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt)
colexprs[i] = lappend(colexprs[i], col);
i++;
}
+
+ /* Release sub-list's cells to save memory */
+ list_free(sublist);
}
/*
* Now resolve the common types of the columns, and coerce everything to
- * those types.
+ * 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
+ * RTE with column collations for use in the outer query. We don't
+ * consider conflict of implicit collations to be an error here; instead
+ * the column will just show InvalidOid as its collation, and you'll get
+ * a failure later if that results in failure to resolve a collation.
+ *
+ * Note we modify the per-column expression lists in-place.
*/
+ collations = NIL;
for (i = 0; i < sublist_length; i++)
{
- coltypes[i] = select_common_type(pstate, colexprs[i], "VALUES", NULL);
- }
+ Oid coltype;
+ Oid colcoll;
- newExprsLists = NIL;
- foreach(lc, exprsLists)
- {
- List *sublist = (List *) lfirst(lc);
- List *newsublist = NIL;
+ coltype = select_common_type(pstate, colexprs[i], "VALUES", NULL);
- i = 0;
- foreach(lc2, sublist)
+ foreach(lc, colexprs[i])
{
- Node *col = (Node *) lfirst(lc2);
+ Node *col = (Node *) lfirst(lc);
- col = coerce_to_common_type(pstate, col, coltypes[i], "VALUES");
- newsublist = lappend(newsublist, col);
- i++;
+ col = coerce_to_common_type(pstate, col, coltype, "VALUES");
+ lfirst(lc) = (void *) col;
}
- /*
- * We must assign collations now because assign_query_collations
- * doesn't process rangetable entries. We just assign all the
- * collations independently in each row, and don't worry about whether
- * they are consistent vertically either.
- */
- assign_list_collations(pstate, newsublist);
+ colcoll = select_common_collation(pstate, colexprs[i], true);
+
+ collations = lappend_oid(collations, colcoll);
+ }
- newExprsLists = lappend(newExprsLists, newsublist);
+ /*
+ * Finally, rearrange the coerced expressions into row-organized lists.
+ */
+ exprsLists = NIL;
+ foreach(lc, colexprs[0])
+ {
+ Node *col = (Node *) lfirst(lc);
+ List *sublist;
+
+ sublist = list_make1(col);
+ exprsLists = lappend(exprsLists, sublist);
+ }
+ list_free(colexprs[0]);
+ for (i = 1; i < sublist_length; i++)
+ {
+ forboth(lc, colexprs[i], lc2, exprsLists)
+ {
+ Node *col = (Node *) lfirst(lc);
+ List *sublist = lfirst(lc2);
+
+ /* sublist pointer in exprsLists won't need adjustment */
+ (void) lappend(sublist, col);
+ }
+ list_free(colexprs[i]);
}
/*
* Generate the VALUES RTE
*/
- rte = addRangeTableEntryForValues(pstate, newExprsLists, NULL, true);
+ rte = addRangeTableEntryForValues(pstate, exprsLists, collations,
+ NULL, true);
rtr = makeNode(RangeTblRef);
/* assume new rte is at end */
rtr->rtindex = list_length(pstate->p_rtable);
@@ -1164,7 +1209,7 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt)
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("VALUES must not contain table references"),
parser_errposition(pstate,
- locate_var_of_level((Node *) newExprsLists, 0))));
+ locate_var_of_level((Node *) exprsLists, 0))));
/*
* Another thing we can't currently support is NEW/OLD references in rules
@@ -1173,13 +1218,13 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt)
* This is a shame. FIXME
*/
if (list_length(pstate->p_rtable) != 1 &&
- contain_vars_of_level((Node *) newExprsLists, 0))
+ contain_vars_of_level((Node *) exprsLists, 0))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("VALUES must not contain OLD or NEW references"),
errhint("Use SELECT ... UNION ALL ... instead."),
parser_errposition(pstate,
- locate_var_of_level((Node *) newExprsLists, 0))));
+ locate_var_of_level((Node *) exprsLists, 0))));
qry->rtable = pstate->p_rtable;
qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);
@@ -1191,13 +1236,13 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt)
(errcode(ERRCODE_GROUPING_ERROR),
errmsg("cannot use aggregate function in VALUES"),
parser_errposition(pstate,
- locate_agg_of_level((Node *) newExprsLists, 0))));
+ locate_agg_of_level((Node *) exprsLists, 0))));
if (pstate->p_hasWindowFuncs)
ereport(ERROR,
(errcode(ERRCODE_WINDOWING_ERROR),
errmsg("cannot use window function in VALUES"),
parser_errposition(pstate,
- locate_windowfunc((Node *) newExprsLists))));
+ locate_windowfunc((Node *) exprsLists))));
assign_query_collations(pstate, qry);
diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c
index 0dbf5cbf38a..2a94f73a9ab 100644
--- a/src/backend/parser/parse_relation.c
+++ b/src/backend/parser/parse_relation.c
@@ -1220,6 +1220,7 @@ addRangeTableEntryForFunction(ParseState *pstate,
RangeTblEntry *
addRangeTableEntryForValues(ParseState *pstate,
List *exprs,
+ List *collations,
Alias *alias,
bool inFromCl)
{
@@ -1233,6 +1234,7 @@ addRangeTableEntryForValues(ParseState *pstate,
rte->relid = InvalidOid;
rte->subquery = NULL;
rte->values_lists = exprs;
+ rte->values_collations = collations;
rte->alias = alias;
eref = alias ? copyObject(alias) : makeAlias(refname, NIL);
@@ -1657,7 +1659,9 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up,
ListCell *l3;
int attnum = 0;
- forthree(l1, rte->funccoltypes, l2, rte->funccoltypmods, l3, rte->funccolcollations)
+ forthree(l1, rte->funccoltypes,
+ l2, rte->funccoltypmods,
+ l3, rte->funccolcollations)
{
Oid attrtype = lfirst_oid(l1);
int32 attrtypmod = lfirst_int(l2);
@@ -1687,12 +1691,15 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up,
{
/* Values RTE */
ListCell *aliasp_item = list_head(rte->eref->colnames);
- ListCell *lc;
+ ListCell *lcv;
+ ListCell *lcc;
varattno = 0;
- foreach(lc, (List *) linitial(rte->values_lists))
+ forboth(lcv, (List *) linitial(rte->values_lists),
+ lcc, rte->values_collations)
{
- Node *col = (Node *) lfirst(lc);
+ Node *col = (Node *) lfirst(lcv);
+ Oid colcollation = lfirst_oid(lcc);
varattno++;
if (colnames)
@@ -1712,7 +1719,7 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up,
varnode = makeVar(rtindex, varattno,
exprType(col),
exprTypmod(col),
- exprCollation(col),
+ colcollation,
sublevels_up);
varnode->location = location;
*colvars = lappend(*colvars, varnode);
@@ -1789,7 +1796,9 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up,
ListCell *lcc;
varattno = 0;
- forthree(lct, rte->ctecoltypes, lcm, rte->ctecoltypmods, lcc, rte->ctecolcollations)
+ forthree(lct, rte->ctecoltypes,
+ lcm, rte->ctecoltypmods,
+ lcc, rte->ctecolcollations)
{
Oid coltype = lfirst_oid(lct);
int32 coltypmod = lfirst_int(lcm);
@@ -2116,6 +2125,7 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
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;
@@ -2125,7 +2135,7 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
col = (Node *) list_nth(collist, attnum - 1);
*vartype = exprType(col);
*vartypmod = exprTypmod(col);
- *varcollid = exprCollation(col);
+ *varcollid = list_nth_oid(rte->values_collations, attnum - 1);
}
break;
case RTE_JOIN:
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index b74526b3b79..53c684aa4e3 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 201104051
+#define CATALOG_VERSION_NO 201104181
#endif
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 566cd3af764..c6337cfb879 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -725,7 +725,7 @@ typedef struct RangeTblEntry
*
* If the function returns RECORD, funccoltypes lists the column types
* declared in the RTE's column type specification, funccoltypmods lists
- * their declared typmods, funccolcollations their collations. Otherwise,
+ * their declared typmods, funccolcollations their collations. Otherwise,
* those fields are NIL.
*/
Node *funcexpr; /* expression tree for func call */
@@ -737,6 +737,7 @@ typedef struct RangeTblEntry
* 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):
diff --git a/src/include/parser/parse_relation.h b/src/include/parser/parse_relation.h
index 55066681f77..0158465c912 100644
--- a/src/include/parser/parse_relation.h
+++ b/src/include/parser/parse_relation.h
@@ -63,6 +63,7 @@ extern RangeTblEntry *addRangeTableEntryForFunction(ParseState *pstate,
bool inFromCl);
extern RangeTblEntry *addRangeTableEntryForValues(ParseState *pstate,
List *exprs,
+ List *collations,
Alias *alias,
bool inFromCl);
extern RangeTblEntry *addRangeTableEntryForJoin(ParseState *pstate,
diff --git a/src/test/regress/expected/collate.out b/src/test/regress/expected/collate.out
index 251a8a51787..627ae1f3f8f 100644
--- a/src/test/regress/expected/collate.out
+++ b/src/test/regress/expected/collate.out
@@ -460,6 +460,14 @@ ERROR: recursive query "foo" column 1 has collation "C" in non-recursive term b
LINE 2: (SELECT x FROM (VALUES('a' COLLATE "C"),('b')) t(x)
^
HINT: Use the COLLATE clause to set the collation of the non-recursive term.
+SELECT a, b, a < b as lt FROM
+ (VALUES ('a', 'B'), ('A', 'b' COLLATE "C")) v(a,b);
+ a | b | lt
+---+---+----
+ a | B | f
+ A | b | t
+(2 rows)
+
-- casting
SELECT CAST('42' AS text COLLATE "C");
ERROR: syntax error at or near "COLLATE"
diff --git a/src/test/regress/sql/collate.sql b/src/test/regress/sql/collate.sql
index 150ad2c5bc1..9585852bc54 100644
--- a/src/test/regress/sql/collate.sql
+++ b/src/test/regress/sql/collate.sql
@@ -154,6 +154,9 @@ WITH RECURSIVE foo(x) AS
SELECT (x || 'c') COLLATE "POSIX" FROM foo WHERE length(x) < 10)
SELECT * FROM foo;
+SELECT a, b, a < b as lt FROM
+ (VALUES ('a', 'B'), ('A', 'b' COLLATE "C")) v(a,b);
+
-- casting