54
54
55
55
static Plan * recurse_set_operations (Node * setOp , PlannerInfo * root ,
56
56
double tuple_fraction ,
57
- List * colTypes , bool junkOK ,
57
+ List * colTypes , List * colCollations ,
58
+ bool junkOK ,
58
59
int flag , List * refnames_tlist ,
59
60
List * * sortClauses , double * pNumGroups );
60
61
static Plan * generate_recursion_plan (SetOperationStmt * setOp ,
@@ -81,12 +82,14 @@ static bool choose_hashed_setop(PlannerInfo *root, List *groupClauses,
81
82
double dNumGroups , double dNumOutputRows ,
82
83
double tuple_fraction ,
83
84
const char * construct );
84
- static List * generate_setop_tlist (List * colTypes , int flag ,
85
+ static List * generate_setop_tlist (List * colTypes , List * colCollations ,
86
+ int flag ,
85
87
Index varno ,
86
88
bool hack_constants ,
87
89
List * input_tlist ,
88
90
List * refnames_tlist );
89
- static List * generate_append_tlist (List * colTypes , List * colCollations , bool flag ,
91
+ static List * generate_append_tlist (List * colTypes , List * colCollations ,
92
+ bool flag ,
90
93
List * input_plans ,
91
94
List * refnames_tlist );
92
95
static List * generate_setop_grouplist (SetOperationStmt * op , List * targetlist );
@@ -169,7 +172,8 @@ plan_set_operations(PlannerInfo *root, double tuple_fraction,
169
172
* on upper-level nodes to deal with that).
170
173
*/
171
174
return recurse_set_operations ((Node * ) topop , root , tuple_fraction ,
172
- topop -> colTypes , true, -1 ,
175
+ topop -> colTypes , topop -> colCollations ,
176
+ true, -1 ,
173
177
leftmostQuery -> targetList ,
174
178
sortClauses , NULL );
175
179
}
@@ -179,7 +183,8 @@ plan_set_operations(PlannerInfo *root, double tuple_fraction,
179
183
* Recursively handle one step in a tree of set operations
180
184
*
181
185
* tuple_fraction: fraction of tuples we expect to retrieve from node
182
- * colTypes: list of type OIDs of expected output columns
186
+ * colTypes: OID list of set-op's result column datatypes
187
+ * colCollations: OID list of set-op's result column collations
183
188
* junkOK: if true, child resjunk columns may be left in the result
184
189
* flag: if >= 0, add a resjunk output column indicating value of flag
185
190
* refnames_tlist: targetlist to take column names from
@@ -196,7 +201,8 @@ plan_set_operations(PlannerInfo *root, double tuple_fraction,
196
201
static Plan *
197
202
recurse_set_operations (Node * setOp , PlannerInfo * root ,
198
203
double tuple_fraction ,
199
- List * colTypes , bool junkOK ,
204
+ List * colTypes , List * colCollations ,
205
+ bool junkOK ,
200
206
int flag , List * refnames_tlist ,
201
207
List * * sortClauses , double * pNumGroups )
202
208
{
@@ -239,7 +245,8 @@ recurse_set_operations(Node *setOp, PlannerInfo *root,
239
245
* Add a SubqueryScan with the caller-requested targetlist
240
246
*/
241
247
plan = (Plan * )
242
- make_subqueryscan (generate_setop_tlist (colTypes , flag ,
248
+ make_subqueryscan (generate_setop_tlist (colTypes , colCollations ,
249
+ flag ,
243
250
rtr -> rtindex ,
244
251
true,
245
252
subplan -> targetlist ,
@@ -287,11 +294,13 @@ recurse_set_operations(Node *setOp, PlannerInfo *root,
287
294
* generate_setop_tlist() to use varno 0.
288
295
*/
289
296
if (flag >= 0 ||
290
- !tlist_same_datatypes (plan -> targetlist , colTypes , junkOK ))
297
+ !tlist_same_datatypes (plan -> targetlist , colTypes , junkOK ) ||
298
+ !tlist_same_collations (plan -> targetlist , colCollations , junkOK ))
291
299
{
292
300
plan = (Plan * )
293
301
make_result (root ,
294
- generate_setop_tlist (colTypes , flag ,
302
+ generate_setop_tlist (colTypes , colCollations ,
303
+ flag ,
295
304
0 ,
296
305
false,
297
306
plan -> targetlist ,
@@ -336,12 +345,14 @@ generate_recursion_plan(SetOperationStmt *setOp, PlannerInfo *root,
336
345
* separately without any intention of combining them into one Append.
337
346
*/
338
347
lplan = recurse_set_operations (setOp -> larg , root , tuple_fraction ,
339
- setOp -> colTypes , false, -1 ,
348
+ setOp -> colTypes , setOp -> colCollations ,
349
+ false, -1 ,
340
350
refnames_tlist , sortClauses , NULL );
341
351
/* The right plan will want to look at the left one ... */
342
352
root -> non_recursive_plan = lplan ;
343
353
rplan = recurse_set_operations (setOp -> rarg , root , tuple_fraction ,
344
- setOp -> colTypes , false, -1 ,
354
+ setOp -> colTypes , setOp -> colCollations ,
355
+ false, -1 ,
345
356
refnames_tlist , sortClauses , NULL );
346
357
root -> non_recursive_plan = NULL ;
347
358
@@ -499,12 +510,14 @@ generate_nonunion_plan(SetOperationStmt *op, PlannerInfo *root,
499
510
/* Recurse on children, ensuring their outputs are marked */
500
511
lplan = recurse_set_operations (op -> larg , root ,
501
512
0.0 /* all tuples needed */ ,
502
- op -> colTypes , false, 0 ,
513
+ op -> colTypes , op -> colCollations ,
514
+ false, 0 ,
503
515
refnames_tlist ,
504
516
& child_sortclauses , & dLeftGroups );
505
517
rplan = recurse_set_operations (op -> rarg , root ,
506
518
0.0 /* all tuples needed */ ,
507
- op -> colTypes , false, 1 ,
519
+ op -> colTypes , op -> colCollations ,
520
+ false, 1 ,
508
521
refnames_tlist ,
509
522
& child_sortclauses , & dRightGroups );
510
523
@@ -620,6 +633,13 @@ generate_nonunion_plan(SetOperationStmt *op, PlannerInfo *root,
620
633
*
621
634
* NOTE: we can also pull a UNION ALL up into a UNION, since the distinct
622
635
* output rows will be lost anyway.
636
+ *
637
+ * NOTE: currently, we ignore collations while determining if a child has
638
+ * the same properties. This is semantically sound only so long as all
639
+ * collations have the same notion of equality. It is valid from an
640
+ * implementation standpoint because we don't care about the ordering of
641
+ * a UNION child's result: UNION ALL results are always unordered, and
642
+ * generate_union_plan will force a fresh sort if the top level is a UNION.
623
643
*/
624
644
static List *
625
645
recurse_union_children (Node * setOp , PlannerInfo * root ,
@@ -660,8 +680,10 @@ recurse_union_children(Node *setOp, PlannerInfo *root,
660
680
*/
661
681
return list_make1 (recurse_set_operations (setOp , root ,
662
682
tuple_fraction ,
663
- top_union -> colTypes , false,
664
- -1 , refnames_tlist ,
683
+ top_union -> colTypes ,
684
+ top_union -> colCollations ,
685
+ false, -1 ,
686
+ refnames_tlist ,
665
687
& child_sortclauses , NULL ));
666
688
}
667
689
@@ -830,35 +852,41 @@ choose_hashed_setop(PlannerInfo *root, List *groupClauses,
830
852
/*
831
853
* Generate targetlist for a set-operation plan node
832
854
*
833
- * colTypes: column datatypes for non-junk columns
855
+ * colTypes: OID list of set-op's result column datatypes
856
+ * colCollations: OID list of set-op's result column collations
834
857
* flag: -1 if no flag column needed, 0 or 1 to create a const flag column
835
858
* varno: varno to use in generated Vars
836
859
* hack_constants: true to copy up constants (see comments in code)
837
860
* input_tlist: targetlist of this node's input node
838
861
* refnames_tlist: targetlist to take column names from
839
862
*/
840
863
static List *
841
- generate_setop_tlist (List * colTypes , int flag ,
864
+ generate_setop_tlist (List * colTypes , List * colCollations ,
865
+ int flag ,
842
866
Index varno ,
843
867
bool hack_constants ,
844
868
List * input_tlist ,
845
869
List * refnames_tlist )
846
870
{
847
871
List * tlist = NIL ;
848
872
int resno = 1 ;
849
- ListCell * i ,
850
- * j ,
851
- * k ;
873
+ ListCell * ctlc ,
874
+ * cclc ,
875
+ * itlc ,
876
+ * rtlc ;
852
877
TargetEntry * tle ;
853
878
Node * expr ;
854
879
855
- j = list_head ( input_tlist );
856
- k = list_head (refnames_tlist );
857
- foreach ( i , colTypes )
880
+ /* there's no forfour() so we must chase one list manually */
881
+ rtlc = list_head (refnames_tlist );
882
+ forthree ( ctlc , colTypes , cclc , colCollations , itlc , input_tlist )
858
883
{
859
- Oid colType = lfirst_oid (i );
860
- TargetEntry * inputtle = (TargetEntry * ) lfirst (j );
861
- TargetEntry * reftle = (TargetEntry * ) lfirst (k );
884
+ Oid colType = lfirst_oid (ctlc );
885
+ Oid colColl = lfirst_oid (cclc );
886
+ TargetEntry * inputtle = (TargetEntry * ) lfirst (itlc );
887
+ TargetEntry * reftle = (TargetEntry * ) lfirst (rtlc );
888
+
889
+ rtlc = lnext (rtlc );
862
890
863
891
Assert (inputtle -> resno == resno );
864
892
Assert (reftle -> resno == resno );
@@ -887,21 +915,48 @@ generate_setop_tlist(List *colTypes, int flag,
887
915
exprTypmod ((Node * ) inputtle -> expr ),
888
916
exprCollation ((Node * ) inputtle -> expr ),
889
917
0 );
918
+
890
919
if (exprType (expr ) != colType )
891
920
{
921
+ /*
922
+ * Note: it's not really cool to be applying coerce_to_common_type
923
+ * here; one notable point is that assign_expr_collations never
924
+ * gets run on any generated nodes. For the moment that's not a
925
+ * problem because we force the correct exposed collation below.
926
+ * It would likely be best to make the parser generate the correct
927
+ * output tlist for every set-op to begin with, though.
928
+ */
892
929
expr = coerce_to_common_type (NULL , /* no UNKNOWNs here */
893
930
expr ,
894
931
colType ,
895
932
"UNION/INTERSECT/EXCEPT" );
896
933
}
934
+
935
+ /*
936
+ * Ensure the tlist entry's exposed collation matches the set-op.
937
+ * This is necessary because plan_set_operations() reports the result
938
+ * ordering as a list of SortGroupClauses, which don't carry collation
939
+ * themselves but just refer to tlist entries. If we don't show the
940
+ * right collation then planner.c might do the wrong thing in
941
+ * higher-level queries.
942
+ *
943
+ * Note we use RelabelType, not CollateExpr, since this expression
944
+ * will reach the executor without any further processing.
945
+ */
946
+ if (exprCollation (expr ) != colColl )
947
+ {
948
+ expr = (Node * ) makeRelabelType ((Expr * ) expr ,
949
+ exprType (expr ),
950
+ exprTypmod (expr ),
951
+ colColl ,
952
+ COERCE_DONTCARE );
953
+ }
954
+
897
955
tle = makeTargetEntry ((Expr * ) expr ,
898
956
(AttrNumber ) resno ++ ,
899
957
pstrdup (reftle -> resname ),
900
958
false);
901
959
tlist = lappend (tlist , tle );
902
-
903
- j = lnext (j );
904
- k = lnext (k );
905
960
}
906
961
907
962
if (flag >= 0 )
@@ -928,17 +983,19 @@ generate_setop_tlist(List *colTypes, int flag,
928
983
/*
929
984
* Generate targetlist for a set-operation Append node
930
985
*
931
- * colTypes: column datatypes for non-junk columns
986
+ * colTypes: OID list of set-op's result column datatypes
987
+ * colCollations: OID list of set-op's result column collations
932
988
* flag: true to create a flag column copied up from subplans
933
989
* input_plans: list of sub-plans of the Append
934
990
* refnames_tlist: targetlist to take column names from
935
991
*
936
992
* The entries in the Append's targetlist should always be simple Vars;
937
- * we just have to make sure they have the right datatypes and typmods.
993
+ * we just have to make sure they have the right datatypes/ typmods/collations .
938
994
* The Vars are always generated with varno 0.
939
995
*/
940
996
static List *
941
- generate_append_tlist (List * colTypes , List * colCollations , bool flag ,
997
+ generate_append_tlist (List * colTypes , List * colCollations ,
998
+ bool flag ,
942
999
List * input_plans ,
943
1000
List * refnames_tlist )
944
1001
{
@@ -1000,7 +1057,8 @@ generate_append_tlist(List *colTypes, List *colCollations, bool flag,
1000
1057
* Now we can build the tlist for the Append.
1001
1058
*/
1002
1059
colindex = 0 ;
1003
- forthree (curColType , colTypes , curColCollation , colCollations , ref_tl_item , refnames_tlist )
1060
+ forthree (curColType , colTypes , curColCollation , colCollations ,
1061
+ ref_tl_item , refnames_tlist )
1004
1062
{
1005
1063
Oid colType = lfirst_oid (curColType );
1006
1064
int32 colTypmod = colTypmods [colindex ++ ];
@@ -1331,7 +1389,7 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
1331
1389
* Build the list of translations from parent Vars to child Vars for
1332
1390
* an inheritance child.
1333
1391
*
1334
- * For paranoia's sake, we match type as well as attribute name.
1392
+ * For paranoia's sake, we match type/collation as well as attribute name.
1335
1393
*/
1336
1394
static void
1337
1395
make_inh_translation_list (Relation oldrelation , Relation newrelation ,
@@ -1410,10 +1468,13 @@ make_inh_translation_list(Relation oldrelation, Relation newrelation,
1410
1468
attname , RelationGetRelationName (newrelation ));
1411
1469
}
1412
1470
1413
- /* Found it, check type */
1471
+ /* Found it, check type and collation match */
1414
1472
if (atttypid != att -> atttypid || atttypmod != att -> atttypmod )
1415
1473
elog (ERROR , "attribute \"%s\" of relation \"%s\" does not match parent's type" ,
1416
1474
attname , RelationGetRelationName (newrelation ));
1475
+ if (attcollation != att -> attcollation )
1476
+ elog (ERROR , "attribute \"%s\" of relation \"%s\" does not match parent's collation" ,
1477
+ attname , RelationGetRelationName (newrelation ));
1417
1478
1418
1479
vars = lappend (vars , makeVar (newvarno ,
1419
1480
(AttrNumber ) (new_attno + 1 ),
0 commit comments