1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
|
/*-------------------------------------------------------------------------
*
* pathkeys.c
* Utilities for matching and building path keys
*
* See src/backend/optimizer/README for a great deal of information about
* the nature and use of path keys.
*
*
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/path/pathkeys.c,v 1.78 2006/08/17 17:02:49 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "nodes/makefuncs.h"
#include "optimizer/clauses.h"
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
#include "optimizer/planmain.h"
#include "optimizer/tlist.h"
#include "optimizer/var.h"
#include "parser/parsetree.h"
#include "parser/parse_expr.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
static PathKeyItem *makePathKeyItem(Node *key, Oid sortop, bool checkType);
static void generate_outer_join_implications(PlannerInfo *root,
List *equi_key_set,
Relids *relids);
static void sub_generate_join_implications(PlannerInfo *root,
List *equi_key_set, Relids *relids,
Node *item1, Oid sortop1,
Relids item1_relids);
static void process_implied_const_eq(PlannerInfo *root,
List *equi_key_set, Relids *relids,
Node *item1, Oid sortop1,
Relids item1_relids,
bool delete_it);
static List *make_canonical_pathkey(PlannerInfo *root, PathKeyItem *item);
static Var *find_indexkey_var(PlannerInfo *root, RelOptInfo *rel,
AttrNumber varattno);
/*
* makePathKeyItem
* create a PathKeyItem node
*/
static PathKeyItem *
makePathKeyItem(Node *key, Oid sortop, bool checkType)
{
PathKeyItem *item = makeNode(PathKeyItem);
/*
* Some callers pass expressions that are not necessarily of the same type
* as the sort operator expects as input (for example when dealing with an
* index that uses binary-compatible operators). We must relabel these
* with the correct type so that the key expressions will be seen as
* equal() to expressions that have been correctly labeled.
*/
if (checkType)
{
Oid lefttype,
righttype;
op_input_types(sortop, &lefttype, &righttype);
if (exprType(key) != lefttype)
key = (Node *) makeRelabelType((Expr *) key,
lefttype, -1,
COERCE_DONTCARE);
}
item->key = key;
item->sortop = sortop;
return item;
}
/*
* add_equijoined_keys
* The given clause has a mergejoinable operator, so its two sides
* can be considered equal after restriction clause application; in
* particular, any pathkey mentioning one side (with the correct sortop)
* can be expanded to include the other as well. Record the exprs and
* associated sortops in the query's equi_key_list for future use.
*
* The query's equi_key_list field points to a list of sublists of PathKeyItem
* nodes, where each sublist is a set of two or more exprs+sortops that have
* been identified as logically equivalent (and, therefore, we may consider
* any two in a set to be equal). As described above, we will subsequently
* use direct pointers to one of these sublists to represent any pathkey
* that involves an equijoined variable.
*/
void
add_equijoined_keys(PlannerInfo *root, RestrictInfo *restrictinfo)
{
Expr *clause = restrictinfo->clause;
PathKeyItem *item1 = makePathKeyItem(get_leftop(clause),
restrictinfo->left_sortop,
false);
PathKeyItem *item2 = makePathKeyItem(get_rightop(clause),
restrictinfo->right_sortop,
false);
List *newset;
ListCell *cursetlink;
/* We might see a clause X=X; don't make a single-element list from it */
if (equal(item1, item2))
return;
/*
* Our plan is to make a two-element set, then sweep through the existing
* equijoin sets looking for matches to item1 or item2. When we find one,
* we remove that set from equi_key_list and union it into our new set.
* When done, we add the new set to the front of equi_key_list.
*
* It may well be that the two items we're given are already known to be
* equijoin-equivalent, in which case we don't need to change our data
* structure. If we find both of them in the same equivalence set to
* start with, we can quit immediately.
*
* This is a standard UNION-FIND problem, for which there exist better
* data structures than simple lists. If this code ever proves to be a
* bottleneck then it could be sped up --- but for now, simple is
* beautiful.
*/
newset = NIL;
/* cannot use foreach here because of possible lremove */
cursetlink = list_head(root->equi_key_list);
while (cursetlink)
{
List *curset = (List *) lfirst(cursetlink);
bool item1here = list_member(curset, item1);
bool item2here = list_member(curset, item2);
/* must advance cursetlink before lremove possibly pfree's it */
cursetlink = lnext(cursetlink);
if (item1here || item2here)
{
/*
* If find both in same equivalence set, no need to do any more
*/
if (item1here && item2here)
{
/* Better not have seen only one in an earlier set... */
Assert(newset == NIL);
return;
}
/* Build the new set only when we know we must */
if (newset == NIL)
newset = list_make2(item1, item2);
/* Found a set to merge into our new set */
newset = list_concat_unique(newset, curset);
/*
* Remove old set from equi_key_list.
*/
root->equi_key_list = list_delete_ptr(root->equi_key_list, curset);
list_free(curset); /* might as well recycle old cons cells */
}
}
/* Build the new set only when we know we must */
if (newset == NIL)
newset = list_make2(item1, item2);
root->equi_key_list = lcons(newset, root->equi_key_list);
}
/*
* generate_implied_equalities
* Scan the completed equi_key_list for the query, and generate explicit
* qualifications (WHERE clauses) for all the pairwise equalities not
* already mentioned in the quals; or remove qualifications found to be
* redundant.
*
* Adding deduced equalities is useful because the additional clauses help
* the selectivity-estimation code and may allow better joins to be chosen;
* and in fact it's *necessary* to ensure that sort keys we think are
* equivalent really are (see src/backend/optimizer/README for more info).
*
* If an equi_key_list set includes any constants then we adopt a different
* strategy: we record all the "var = const" deductions we can make, and
* actively remove all the "var = var" clauses that are implied by the set
* (including the clauses that originally gave rise to the set!). The reason
* is that given input like "a = b AND b = 42", once we have deduced "a = 42"
* there is no longer any need to apply the clause "a = b"; not only is
* it a waste of time to check it, but we will misestimate selectivity if the
* clause is left in. So we must remove it. For this purpose, any pathkey
* item that mentions no Vars of the current level can be taken as a constant.
* (The only case where this would be risky is if the item contains volatile
* functions; but we will never consider such an expression to be a pathkey
* at all, because check_mergejoinable() will reject it.)
*
* Also, when we have constants in an equi_key_list we can try to propagate
* the constants into outer joins; see generate_outer_join_implications
* for discussion.
*
* This routine just walks the equi_key_list to find all pairwise equalities.
* We call process_implied_equality (in plan/initsplan.c) to adjust the
* restrictinfo datastructures for each pair.
*/
void
generate_implied_equalities(PlannerInfo *root)
{
ListCell *cursetlink;
foreach(cursetlink, root->equi_key_list)
{
List *curset = (List *) lfirst(cursetlink);
int nitems = list_length(curset);
Relids *relids;
bool have_consts;
ListCell *ptr1;
int i1;
/*
* A set containing only two items cannot imply any equalities beyond
* the one that created the set, so we can skip it --- unless outer
* joins appear in the query.
*/
if (nitems < 3 && !root->hasOuterJoins)
continue;
/*
* Collect info about relids mentioned in each item. For this routine
* we only really care whether there are any at all in each item, but
* process_implied_equality() needs the exact sets, so we may as well
* pull them here.
*/
relids = (Relids *) palloc(nitems * sizeof(Relids));
have_consts = false;
i1 = 0;
foreach(ptr1, curset)
{
PathKeyItem *item1 = (PathKeyItem *) lfirst(ptr1);
relids[i1] = pull_varnos(item1->key);
if (bms_is_empty(relids[i1]))
have_consts = true;
i1++;
}
/*
* Match each item in the set with all that appear after it (it's
* sufficient to generate A=B, need not process B=A too).
*
* A set containing only two items cannot imply any equalities beyond
* the one that created the set, so we can skip this processing in
* that case.
*/
if (nitems >= 3)
{
i1 = 0;
foreach(ptr1, curset)
{
PathKeyItem *item1 = (PathKeyItem *) lfirst(ptr1);
bool i1_is_variable = !bms_is_empty(relids[i1]);
ListCell *ptr2;
int i2 = i1 + 1;
for_each_cell(ptr2, lnext(ptr1))
{
PathKeyItem *item2 = (PathKeyItem *) lfirst(ptr2);
bool i2_is_variable = !bms_is_empty(relids[i2]);
/*
* If it's "const = const" then just ignore it altogether.
* There is no place in the restrictinfo structure to
* store it. (If the two consts are in fact unequal, then
* propagating the comparison to Vars will cause us to
* produce zero rows out, as expected.)
*/
if (i1_is_variable || i2_is_variable)
{
/*
* Tell process_implied_equality to delete the clause,
* not add it, if it's "var = var" and we have
* constants present in the list.
*/
bool delete_it = (have_consts &&
i1_is_variable &&
i2_is_variable);
process_implied_equality(root,
item1->key, item2->key,
item1->sortop, item2->sortop,
relids[i1], relids[i2],
delete_it);
}
i2++;
}
i1++;
}
}
/*
* If we have constant(s) and outer joins, try to propagate the
* constants through outer-join quals.
*/
if (have_consts && root->hasOuterJoins)
generate_outer_join_implications(root, curset, relids);
}
}
/*
* generate_outer_join_implications
* Generate clauses that can be deduced in outer-join situations.
*
* When we have mergejoinable clauses A = B that are outer-join clauses,
* we can't blindly combine them with other clauses A = C to deduce B = C,
* since in fact the "equality" A = B won't necessarily hold above the
* outer join (one of the variables might be NULL instead). Nonetheless
* there are cases where we can add qual clauses using transitivity.
*
* One case that we look for here is an outer-join clause OUTERVAR = INNERVAR
* combined with a pushed-down (valid everywhere) clause OUTERVAR = CONSTANT.
* It is safe and useful to push a clause INNERVAR = CONSTANT into the
* evaluation of the inner (nullable) relation, because any inner rows not
* meeting this condition will not contribute to the outer-join result anyway.
* (Any outer rows they could join to will be eliminated by the pushed-down
* clause.)
*
* Note that the above rule does not work for full outer joins, nor for
* pushed-down restrictions on an inner-side variable; nor is it very
* interesting to consider cases where the pushed-down clause involves
* relations entirely outside the outer join, since such clauses couldn't
* be pushed into the inner side's scan anyway. So the restriction to
* outervar = pseudoconstant is not really giving up anything.
*
* For full-join cases, we can only do something useful if it's a FULL JOIN
* USING and a merged column has a restriction MERGEDVAR = CONSTANT. By
* the time it gets here, the restriction will look like
* COALESCE(LEFTVAR, RIGHTVAR) = CONSTANT
* and we will have a join clause LEFTVAR = RIGHTVAR that we can match the
* COALESCE expression to. In this situation we can push LEFTVAR = CONSTANT
* and RIGHTVAR = CONSTANT into the input relations, since any rows not
* meeting these conditions cannot contribute to the join result.
*
* Again, there isn't any traction to be gained by trying to deal with
* clauses comparing a mergedvar to a non-pseudoconstant. So we can make
* use of the equi_key_lists to quickly find the interesting pushed-down
* clauses. The interesting outer-join clauses were accumulated for us by
* distribute_qual_to_rels.
*
* equi_key_set: a list of PathKeyItems that are known globally equivalent,
* at least one of which is a pseudoconstant.
* relids: an array of Relids sets showing the relation membership of each
* PathKeyItem in equi_key_set.
*/
static void
generate_outer_join_implications(PlannerInfo *root,
List *equi_key_set,
Relids *relids)
{
ListCell *l;
int i = 0;
/* Process each non-constant element of equi_key_set */
foreach(l, equi_key_set)
{
PathKeyItem *item1 = (PathKeyItem *) lfirst(l);
if (!bms_is_empty(relids[i]))
{
sub_generate_join_implications(root, equi_key_set, relids,
item1->key,
item1->sortop,
relids[i]);
}
i++;
}
}
/*
* sub_generate_join_implications
* Propagate a constant equality through outer join clauses.
*
* The item described by item1/sortop1/item1_relids has been determined
* to be equal to the constant(s) listed in equi_key_set. Recursively
* trace out the implications of this.
*
* equi_key_set and relids are as for generate_outer_join_implications.
*/
static void
sub_generate_join_implications(PlannerInfo *root,
List *equi_key_set, Relids *relids,
Node *item1, Oid sortop1, Relids item1_relids)
{
ListCell *l;
/*
* Examine each mergejoinable outer-join clause with OUTERVAR on left,
* looking for an OUTERVAR identical to item1
*/
foreach(l, root->left_join_clauses)
{
RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
Node *leftop = get_leftop(rinfo->clause);
if (equal(leftop, item1) && rinfo->left_sortop == sortop1)
{
/*
* Match, so find constant member(s) of set and generate implied
* INNERVAR = CONSTANT
*/
Node *rightop = get_rightop(rinfo->clause);
process_implied_const_eq(root, equi_key_set, relids,
rightop,
rinfo->right_sortop,
rinfo->right_relids,
false);
/*
* We can remove explicit tests of this outer-join qual, too,
* since we now have tests forcing each of its sides to the same
* value.
*/
process_implied_equality(root,
leftop, rightop,
rinfo->left_sortop, rinfo->right_sortop,
rinfo->left_relids, rinfo->right_relids,
true);
/*
* And recurse to see if we can deduce anything from INNERVAR =
* CONSTANT
*/
sub_generate_join_implications(root, equi_key_set, relids,
rightop,
rinfo->right_sortop,
rinfo->right_relids);
}
}
/* The same, looking at clauses with OUTERVAR on right */
foreach(l, root->right_join_clauses)
{
RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
Node *rightop = get_rightop(rinfo->clause);
if (equal(rightop, item1) && rinfo->right_sortop == sortop1)
{
/*
* Match, so find constant member(s) of set and generate implied
* INNERVAR = CONSTANT
*/
Node *leftop = get_leftop(rinfo->clause);
process_implied_const_eq(root, equi_key_set, relids,
leftop,
rinfo->left_sortop,
rinfo->left_relids,
false);
/*
* We can remove explicit tests of this outer-join qual, too,
* since we now have tests forcing each of its sides to the same
* value.
*/
process_implied_equality(root,
leftop, rightop,
rinfo->left_sortop, rinfo->right_sortop,
rinfo->left_relids, rinfo->right_relids,
true);
/*
* And recurse to see if we can deduce anything from INNERVAR =
* CONSTANT
*/
sub_generate_join_implications(root, equi_key_set, relids,
leftop,
rinfo->left_sortop,
rinfo->left_relids);
}
}
/*
* Only COALESCE(x,y) items can possibly match full joins
*/
if (IsA(item1, CoalesceExpr))
{
CoalesceExpr *cexpr = (CoalesceExpr *) item1;
Node *cfirst;
Node *csecond;
if (list_length(cexpr->args) != 2)
return;
cfirst = (Node *) linitial(cexpr->args);
csecond = (Node *) lsecond(cexpr->args);
/*
* Examine each mergejoinable full-join clause, looking for a clause
* of the form "x = y" matching the COALESCE(x,y) expression
*/
foreach(l, root->full_join_clauses)
{
RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
Node *leftop = get_leftop(rinfo->clause);
Node *rightop = get_rightop(rinfo->clause);
/*
* We can assume the COALESCE() inputs are in the same order as
* the join clause, since both were automatically generated in the
* cases we care about.
*
* XXX currently this may fail to match in cross-type cases
* because the COALESCE will contain typecast operations while the
* join clause may not (if there is a cross-type mergejoin
* operator available for the two column types). Is it OK to strip
* implicit coercions from the COALESCE arguments? What of the
* sortops in such cases?
*/
if (equal(leftop, cfirst) &&
equal(rightop, csecond) &&
rinfo->left_sortop == sortop1 &&
rinfo->right_sortop == sortop1)
{
/*
* Match, so find constant member(s) of set and generate
* implied LEFTVAR = CONSTANT
*/
process_implied_const_eq(root, equi_key_set, relids,
leftop,
rinfo->left_sortop,
rinfo->left_relids,
false);
/* ... and RIGHTVAR = CONSTANT */
process_implied_const_eq(root, equi_key_set, relids,
rightop,
rinfo->right_sortop,
rinfo->right_relids,
false);
/* ... and remove COALESCE() = CONSTANT */
process_implied_const_eq(root, equi_key_set, relids,
item1,
sortop1,
item1_relids,
true);
/*
* We can remove explicit tests of this outer-join qual, too,
* since we now have tests forcing each of its sides to the
* same value.
*/
process_implied_equality(root,
leftop, rightop,
rinfo->left_sortop,
rinfo->right_sortop,
rinfo->left_relids,
rinfo->right_relids,
true);
/*
* And recurse to see if we can deduce anything from LEFTVAR =
* CONSTANT
*/
sub_generate_join_implications(root, equi_key_set, relids,
leftop,
rinfo->left_sortop,
rinfo->left_relids);
/* ... and RIGHTVAR = CONSTANT */
sub_generate_join_implications(root, equi_key_set, relids,
rightop,
rinfo->right_sortop,
rinfo->right_relids);
}
}
}
}
/*
* process_implied_const_eq
* Apply process_implied_equality with the given item and each
* pseudoconstant member of equi_key_set.
*
* equi_key_set and relids are as for generate_outer_join_implications,
* the other parameters as for process_implied_equality.
*/
static void
process_implied_const_eq(PlannerInfo *root, List *equi_key_set, Relids *relids,
Node *item1, Oid sortop1, Relids item1_relids,
bool delete_it)
{
ListCell *l;
bool found = false;
int i = 0;
foreach(l, equi_key_set)
{
PathKeyItem *item2 = (PathKeyItem *) lfirst(l);
if (bms_is_empty(relids[i]))
{
process_implied_equality(root,
item1, item2->key,
sortop1, item2->sortop,
item1_relids, NULL,
delete_it);
found = true;
}
i++;
}
/* Caller screwed up if no constants in list */
Assert(found);
}
/*
* exprs_known_equal
* Detect whether two expressions are known equal due to equijoin clauses.
*
* Note: does not bother to check for "equal(item1, item2)"; caller must
* check that case if it's possible to pass identical items.
*/
bool
exprs_known_equal(PlannerInfo *root, Node *item1, Node *item2)
{
ListCell *cursetlink;
foreach(cursetlink, root->equi_key_list)
{
List *curset = (List *) lfirst(cursetlink);
bool item1member = false;
bool item2member = false;
ListCell *ptr;
foreach(ptr, curset)
{
PathKeyItem *pitem = (PathKeyItem *) lfirst(ptr);
if (equal(item1, pitem->key))
item1member = true;
else if (equal(item2, pitem->key))
item2member = true;
/* Exit as soon as equality is proven */
if (item1member && item2member)
return true;
}
}
return false;
}
/*
* make_canonical_pathkey
* Given a PathKeyItem, find the equi_key_list subset it is a member of,
* if any. If so, return a pointer to that sublist, which is the
* canonical representation (for this query) of that PathKeyItem's
* equivalence set. If it is not found, add a singleton "equivalence set"
* to the equi_key_list and return that --- see compare_pathkeys.
*
* Note that this function must not be used until after we have completed
* scanning the WHERE clause for equijoin operators.
*/
static List *
make_canonical_pathkey(PlannerInfo *root, PathKeyItem *item)
{
List *newset;
ListCell *cursetlink;
foreach(cursetlink, root->equi_key_list)
{
List *curset = (List *) lfirst(cursetlink);
if (list_member(curset, item))
return curset;
}
newset = list_make1(item);
root->equi_key_list = lcons(newset, root->equi_key_list);
return newset;
}
/*
* canonicalize_pathkeys
* Convert a not-necessarily-canonical pathkeys list to canonical form.
*
* Note that this function must not be used until after we have completed
* scanning the WHERE clause for equijoin operators.
*/
List *
canonicalize_pathkeys(PlannerInfo *root, List *pathkeys)
{
List *new_pathkeys = NIL;
ListCell *l;
foreach(l, pathkeys)
{
List *pathkey = (List *) lfirst(l);
PathKeyItem *item;
List *cpathkey;
/*
* It's sufficient to look at the first entry in the sublist; if there
* are more entries, they're already part of an equivalence set by
* definition.
*/
Assert(pathkey != NIL);
item = (PathKeyItem *) linitial(pathkey);
cpathkey = make_canonical_pathkey(root, item);
/*
* Eliminate redundant ordering requests --- ORDER BY A,A is the same
* as ORDER BY A. We want to check this only after we have
* canonicalized the keys, so that equivalent-key knowledge is used
* when deciding if an item is redundant.
*/
new_pathkeys = list_append_unique_ptr(new_pathkeys, cpathkey);
}
return new_pathkeys;
}
/*
* count_canonical_peers
* Given a PathKeyItem, find the equi_key_list subset it is a member of,
* if any. If so, return the number of other members of the set.
* If not, return 0 (without actually adding it to our equi_key_list).
*
* This is a hack to support the rather bogus heuristics in
* convert_subquery_pathkeys.
*/
static int
count_canonical_peers(PlannerInfo *root, PathKeyItem *item)
{
ListCell *cursetlink;
foreach(cursetlink, root->equi_key_list)
{
List *curset = (List *) lfirst(cursetlink);
if (list_member(curset, item))
return list_length(curset) - 1;
}
return 0;
}
/****************************************************************************
* PATHKEY COMPARISONS
****************************************************************************/
/*
* compare_pathkeys
* Compare two pathkeys to see if they are equivalent, and if not whether
* one is "better" than the other.
*
* This function may only be applied to canonicalized pathkey lists.
* In the canonical representation, sublists can be checked for equality
* by simple pointer comparison.
*/
PathKeysComparison
compare_pathkeys(List *keys1, List *keys2)
{
ListCell *key1,
*key2;
forboth(key1, keys1, key2, keys2)
{
List *subkey1 = (List *) lfirst(key1);
List *subkey2 = (List *) lfirst(key2);
/*
* XXX would like to check that we've been given canonicalized input,
* but PlannerInfo not accessible here...
*/
#ifdef NOT_USED
Assert(list_member_ptr(root->equi_key_list, subkey1));
Assert(list_member_ptr(root->equi_key_list, subkey2));
#endif
/*
* We will never have two subkeys where one is a subset of the other,
* because of the canonicalization process. Either they are equal or
* they ain't. Furthermore, we only need pointer comparison to detect
* equality.
*/
if (subkey1 != subkey2)
return PATHKEYS_DIFFERENT; /* no need to keep looking */
}
/*
* If we reached the end of only one list, the other is longer and
* therefore not a subset. (We assume the additional sublist(s) of the
* other list are not NIL --- no pathkey list should ever have a NIL
* sublist.)
*/
if (key1 == NULL && key2 == NULL)
return PATHKEYS_EQUAL;
if (key1 != NULL)
return PATHKEYS_BETTER1; /* key1 is longer */
return PATHKEYS_BETTER2; /* key2 is longer */
}
/*
* pathkeys_contained_in
* Common special case of compare_pathkeys: we just want to know
* if keys2 are at least as well sorted as keys1.
*/
bool
pathkeys_contained_in(List *keys1, List *keys2)
{
switch (compare_pathkeys(keys1, keys2))
{
case PATHKEYS_EQUAL:
case PATHKEYS_BETTER2:
return true;
default:
break;
}
return false;
}
/*
* get_cheapest_path_for_pathkeys
* Find the cheapest path (according to the specified criterion) that
* satisfies the given pathkeys. Return NULL if no such path.
*
* 'paths' is a list of possible paths that all generate the same relation
* 'pathkeys' represents a required ordering (already canonicalized!)
* 'cost_criterion' is STARTUP_COST or TOTAL_COST
*/
Path *
get_cheapest_path_for_pathkeys(List *paths, List *pathkeys,
CostSelector cost_criterion)
{
Path *matched_path = NULL;
ListCell *l;
foreach(l, paths)
{
Path *path = (Path *) lfirst(l);
/*
* Since cost comparison is a lot cheaper than pathkey comparison, do
* that first. (XXX is that still true?)
*/
if (matched_path != NULL &&
compare_path_costs(matched_path, path, cost_criterion) <= 0)
continue;
if (pathkeys_contained_in(pathkeys, path->pathkeys))
matched_path = path;
}
return matched_path;
}
/*
* get_cheapest_fractional_path_for_pathkeys
* Find the cheapest path (for retrieving a specified fraction of all
* the tuples) that satisfies the given pathkeys.
* Return NULL if no such path.
*
* See compare_fractional_path_costs() for the interpretation of the fraction
* parameter.
*
* 'paths' is a list of possible paths that all generate the same relation
* 'pathkeys' represents a required ordering (already canonicalized!)
* 'fraction' is the fraction of the total tuples expected to be retrieved
*/
Path *
get_cheapest_fractional_path_for_pathkeys(List *paths,
List *pathkeys,
double fraction)
{
Path *matched_path = NULL;
ListCell *l;
foreach(l, paths)
{
Path *path = (Path *) lfirst(l);
/*
* Since cost comparison is a lot cheaper than pathkey comparison, do
* that first.
*/
if (matched_path != NULL &&
compare_fractional_path_costs(matched_path, path, fraction) <= 0)
continue;
if (pathkeys_contained_in(pathkeys, path->pathkeys))
matched_path = path;
}
return matched_path;
}
/****************************************************************************
* NEW PATHKEY FORMATION
****************************************************************************/
/*
* build_index_pathkeys
* Build a pathkeys list that describes the ordering induced by an index
* scan using the given index. (Note that an unordered index doesn't
* induce any ordering; such an index will have no sortop OIDS in
* its "ordering" field, and we will return NIL.)
*
* If 'scandir' is BackwardScanDirection, attempt to build pathkeys
* representing a backwards scan of the index. Return NIL if can't do it.
*
* If 'canonical' is TRUE, we remove duplicate pathkeys (which can occur
* if two index columns are equijoined, eg WHERE x = 1 AND y = 1). This
* is required if the result is to be compared directly to a canonical query
* pathkeys list. However, some callers want a list with exactly one entry
* per index column, and they must pass FALSE.
*
* We generate the full pathkeys list whether or not all are useful for the
* current query. Caller should do truncate_useless_pathkeys().
*/
List *
build_index_pathkeys(PlannerInfo *root,
IndexOptInfo *index,
ScanDirection scandir,
bool canonical)
{
List *retval = NIL;
int *indexkeys = index->indexkeys;
Oid *ordering = index->ordering;
ListCell *indexprs_item = list_head(index->indexprs);
while (*ordering != InvalidOid)
{
PathKeyItem *item;
Oid sortop;
Node *indexkey;
List *cpathkey;
sortop = *ordering;
if (ScanDirectionIsBackward(scandir))
{
sortop = get_commutator(sortop);
if (sortop == InvalidOid)
break; /* oops, no reverse sort operator? */
}
if (*indexkeys != 0)
{
/* simple index column */
indexkey = (Node *) find_indexkey_var(root, index->rel,
*indexkeys);
}
else
{
/* expression --- assume we need not copy it */
if (indexprs_item == NULL)
elog(ERROR, "wrong number of index expressions");
indexkey = (Node *) lfirst(indexprs_item);
indexprs_item = lnext(indexprs_item);
}
/* OK, make a sublist for this sort key */
item = makePathKeyItem(indexkey, sortop, true);
cpathkey = make_canonical_pathkey(root, item);
/* Eliminate redundant ordering info if requested */
if (canonical)
retval = list_append_unique_ptr(retval, cpathkey);
else
retval = lappend(retval, cpathkey);
indexkeys++;
ordering++;
}
return retval;
}
/*
* Find or make a Var node for the specified attribute of the rel.
*
* We first look for the var in the rel's target list, because that's
* easy and fast. But the var might not be there (this should normally
* only happen for vars that are used in WHERE restriction clauses,
* but not in join clauses or in the SELECT target list). In that case,
* gin up a Var node the hard way.
*/
static Var *
find_indexkey_var(PlannerInfo *root, RelOptInfo *rel, AttrNumber varattno)
{
ListCell *temp;
Index relid;
Oid reloid,
vartypeid;
int32 type_mod;
foreach(temp, rel->reltargetlist)
{
Var *var = (Var *) lfirst(temp);
if (IsA(var, Var) &&
var->varattno == varattno)
return var;
}
relid = rel->relid;
reloid = getrelid(relid, root->parse->rtable);
get_atttypetypmod(reloid, varattno, &vartypeid, &type_mod);
return makeVar(relid, varattno, vartypeid, type_mod, 0);
}
/*
* convert_subquery_pathkeys
* Build a pathkeys list that describes the ordering of a subquery's
* result, in the terms of the outer query. This is essentially a
* task of conversion.
*
* 'rel': outer query's RelOptInfo for the subquery relation.
* 'subquery_pathkeys': the subquery's output pathkeys, in its terms.
*
* It is not necessary for caller to do truncate_useless_pathkeys(),
* because we select keys in a way that takes usefulness of the keys into
* account.
*/
List *
convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
List *subquery_pathkeys)
{
List *retval = NIL;
int retvallen = 0;
int outer_query_keys = list_length(root->query_pathkeys);
List *sub_tlist = rel->subplan->targetlist;
ListCell *i;
foreach(i, subquery_pathkeys)
{
List *sub_pathkey = (List *) lfirst(i);
ListCell *j;
PathKeyItem *best_item = NULL;
int best_score = 0;
List *cpathkey;
/*
* The sub_pathkey could contain multiple elements (representing
* knowledge that multiple items are effectively equal). Each element
* might match none, one, or more of the output columns that are
* visible to the outer query. This means we may have multiple
* possible representations of the sub_pathkey in the context of the
* outer query. Ideally we would generate them all and put them all
* into a pathkey list of the outer query, thereby propagating
* equality knowledge up to the outer query. Right now we cannot do
* so, because the outer query's canonical pathkey sets are already
* frozen when this is called. Instead we prefer the one that has the
* highest "score" (number of canonical pathkey peers, plus one if it
* matches the outer query_pathkeys). This is the most likely to be
* useful in the outer query.
*/
foreach(j, sub_pathkey)
{
PathKeyItem *sub_item = (PathKeyItem *) lfirst(j);
Node *sub_key = sub_item->key;
Expr *rtarg;
ListCell *k;
/*
* We handle two cases: the sub_pathkey key can be either an exact
* match for a targetlist entry, or a RelabelType of a targetlist
* entry. (The latter case is worth extra code because it arises
* frequently in connection with varchar fields.)
*/
if (IsA(sub_key, RelabelType))
rtarg = ((RelabelType *) sub_key)->arg;
else
rtarg = NULL;
foreach(k, sub_tlist)
{
TargetEntry *tle = (TargetEntry *) lfirst(k);
Node *outer_expr;
PathKeyItem *outer_item;
int score;
/* resjunk items aren't visible to outer query */
if (tle->resjunk)
continue;
if (equal(tle->expr, sub_key))
{
/* Exact match */
outer_expr = (Node *)
makeVar(rel->relid,
tle->resno,
exprType((Node *) tle->expr),
exprTypmod((Node *) tle->expr),
0);
}
else if (rtarg && equal(tle->expr, rtarg))
{
/* Match after discarding RelabelType */
outer_expr = (Node *)
makeVar(rel->relid,
tle->resno,
exprType((Node *) tle->expr),
exprTypmod((Node *) tle->expr),
0);
outer_expr = (Node *)
makeRelabelType((Expr *) outer_expr,
((RelabelType *) sub_key)->resulttype,
((RelabelType *) sub_key)->resulttypmod,
((RelabelType *) sub_key)->relabelformat);
}
else
continue;
/* Found a representation for this sub_key */
outer_item = makePathKeyItem(outer_expr,
sub_item->sortop,
true);
/* score = # of mergejoin peers */
score = count_canonical_peers(root, outer_item);
/* +1 if it matches the proper query_pathkeys item */
if (retvallen < outer_query_keys &&
list_member(list_nth(root->query_pathkeys, retvallen), outer_item))
score++;
if (score > best_score)
{
best_item = outer_item;
best_score = score;
}
}
}
/*
* If we couldn't find a representation of this sub_pathkey, we're
* done (we can't use the ones to its right, either).
*/
if (!best_item)
break;
/* Canonicalize the chosen item (we did not before) */
cpathkey = make_canonical_pathkey(root, best_item);
/*
* Eliminate redundant ordering info; could happen if outer query
* equijoins subquery keys...
*/
if (!list_member_ptr(retval, cpathkey))
{
retval = lappend(retval, cpathkey);
retvallen++;
}
}
return retval;
}
/*
* build_join_pathkeys
* Build the path keys for a join relation constructed by mergejoin or
* nestloop join. These keys should include all the path key vars of the
* outer path (since the join will retain the ordering of the outer path)
* plus any vars of the inner path that are equijoined to the outer vars.
*
* Per the discussion in backend/optimizer/README, equijoined inner vars
* can be considered path keys of the result, just the same as the outer
* vars they were joined with; furthermore, it doesn't matter what kind
* of join algorithm is actually used.
*
* EXCEPTION: in a FULL or RIGHT join, we cannot treat the result as
* having the outer path's path keys, because null lefthand rows may be
* inserted at random points. It must be treated as unsorted.
*
* 'joinrel' is the join relation that paths are being formed for
* 'jointype' is the join type (inner, left, full, etc)
* 'outer_pathkeys' is the list of the current outer path's path keys
*
* Returns the list of new path keys.
*/
List *
build_join_pathkeys(PlannerInfo *root,
RelOptInfo *joinrel,
JoinType jointype,
List *outer_pathkeys)
{
if (jointype == JOIN_FULL || jointype == JOIN_RIGHT)
return NIL;
/*
* This used to be quite a complex bit of code, but now that all pathkey
* sublists start out life canonicalized, we don't have to do a darn thing
* here! The inner-rel vars we used to need to add are *already* part of
* the outer pathkey!
*
* We do, however, need to truncate the pathkeys list, since it may
* contain pathkeys that were useful for forming this joinrel but are
* uninteresting to higher levels.
*/
return truncate_useless_pathkeys(root, joinrel, outer_pathkeys);
}
/****************************************************************************
* PATHKEYS AND SORT CLAUSES
****************************************************************************/
/*
* make_pathkeys_for_sortclauses
* Generate a pathkeys list that represents the sort order specified
* by a list of SortClauses (GroupClauses will work too!)
*
* NB: the result is NOT in canonical form, but must be passed through
* canonicalize_pathkeys() before it can be used for comparisons or
* labeling relation sort orders. (We do things this way because
* grouping_planner needs to be able to construct requested pathkeys
* before the pathkey equivalence sets have been created for the query.)
*
* 'sortclauses' is a list of SortClause or GroupClause nodes
* 'tlist' is the targetlist to find the referenced tlist entries in
*/
List *
make_pathkeys_for_sortclauses(List *sortclauses,
List *tlist)
{
List *pathkeys = NIL;
ListCell *l;
foreach(l, sortclauses)
{
SortClause *sortcl = (SortClause *) lfirst(l);
Node *sortkey;
PathKeyItem *pathkey;
sortkey = get_sortgroupclause_expr(sortcl, tlist);
pathkey = makePathKeyItem(sortkey, sortcl->sortop, true);
/*
* The pathkey becomes a one-element sublist, for now;
* canonicalize_pathkeys() might replace it with a longer sublist
* later.
*/
pathkeys = lappend(pathkeys, list_make1(pathkey));
}
return pathkeys;
}
/****************************************************************************
* PATHKEYS AND MERGECLAUSES
****************************************************************************/
/*
* cache_mergeclause_pathkeys
* Make the cached pathkeys valid in a mergeclause restrictinfo.
*
* RestrictInfo contains fields in which we may cache the result
* of looking up the canonical pathkeys for the left and right sides
* of the mergeclause. (Note that in normal cases they will be the
* same, but not if the mergeclause appears above an OUTER JOIN.)
* This is a worthwhile savings because these routines will be invoked
* many times when dealing with a many-relation query.
*
* We have to be careful that the cached values are palloc'd in the same
* context the RestrictInfo node itself is in. This is not currently a
* problem for normal planning, but it is an issue for GEQO planning.
*/
void
cache_mergeclause_pathkeys(PlannerInfo *root, RestrictInfo *restrictinfo)
{
Node *key;
PathKeyItem *item;
MemoryContext oldcontext;
Assert(restrictinfo->mergejoinoperator != InvalidOid);
if (restrictinfo->left_pathkey == NIL)
{
oldcontext = MemoryContextSwitchTo(GetMemoryChunkContext(restrictinfo));
key = get_leftop(restrictinfo->clause);
item = makePathKeyItem(key, restrictinfo->left_sortop, false);
restrictinfo->left_pathkey = make_canonical_pathkey(root, item);
MemoryContextSwitchTo(oldcontext);
}
if (restrictinfo->right_pathkey == NIL)
{
oldcontext = MemoryContextSwitchTo(GetMemoryChunkContext(restrictinfo));
key = get_rightop(restrictinfo->clause);
item = makePathKeyItem(key, restrictinfo->right_sortop, false);
restrictinfo->right_pathkey = make_canonical_pathkey(root, item);
MemoryContextSwitchTo(oldcontext);
}
}
/*
* find_mergeclauses_for_pathkeys
* This routine attempts to find a set of mergeclauses that can be
* used with a specified ordering for one of the input relations.
* If successful, it returns a list of mergeclauses.
*
* 'pathkeys' is a pathkeys list showing the ordering of an input path.
* It doesn't matter whether it is for the inner or outer path.
* 'restrictinfos' is a list of mergejoinable restriction clauses for the
* join relation being formed.
*
* The result is NIL if no merge can be done, else a maximal list of
* usable mergeclauses (represented as a list of their restrictinfo nodes).
*
* XXX Ideally we ought to be considering context, ie what path orderings
* are available on the other side of the join, rather than just making
* an arbitrary choice among the mergeclauses that will work for this side
* of the join.
*/
List *
find_mergeclauses_for_pathkeys(PlannerInfo *root,
List *pathkeys,
List *restrictinfos)
{
List *mergeclauses = NIL;
ListCell *i;
/* make sure we have pathkeys cached in the clauses */
foreach(i, restrictinfos)
{
RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(i);
cache_mergeclause_pathkeys(root, restrictinfo);
}
foreach(i, pathkeys)
{
List *pathkey = (List *) lfirst(i);
List *matched_restrictinfos = NIL;
ListCell *j;
/*
* We can match a pathkey against either left or right side of any
* mergejoin clause. (We examine both sides since we aren't told if
* the given pathkeys are for inner or outer input path; no confusion
* is possible.) Furthermore, if there are multiple matching clauses,
* take them all. In plain inner-join scenarios we expect only one
* match, because redundant-mergeclause elimination will have removed
* any redundant mergeclauses from the input list. However, in
* outer-join scenarios there might be multiple matches. An example is
*
* select * from a full join b on a.v1 = b.v1 and a.v2 = b.v2 and a.v1
* = b.v2;
*
* Given the pathkeys ((a.v1), (a.v2)) it is okay to return all three
* clauses (in the order a.v1=b.v1, a.v1=b.v2, a.v2=b.v2) and indeed
* we *must* do so or we will be unable to form a valid plan.
*/
foreach(j, restrictinfos)
{
RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(j);
/*
* We can compare canonical pathkey sublists by simple pointer
* equality; see compare_pathkeys.
*/
if ((pathkey == restrictinfo->left_pathkey ||
pathkey == restrictinfo->right_pathkey) &&
!list_member_ptr(mergeclauses, restrictinfo))
{
matched_restrictinfos = lappend(matched_restrictinfos,
restrictinfo);
}
}
/*
* If we didn't find a mergeclause, we're done --- any additional
* sort-key positions in the pathkeys are useless. (But we can still
* mergejoin if we found at least one mergeclause.)
*/
if (matched_restrictinfos == NIL)
break;
/*
* If we did find usable mergeclause(s) for this sort-key position,
* add them to result list.
*/
mergeclauses = list_concat(mergeclauses, matched_restrictinfos);
}
return mergeclauses;
}
/*
* make_pathkeys_for_mergeclauses
* Builds a pathkey list representing the explicit sort order that
* must be applied to a path in order to make it usable for the
* given mergeclauses.
*
* 'mergeclauses' is a list of RestrictInfos for mergejoin clauses
* that will be used in a merge join.
* 'rel' is the relation the pathkeys will apply to (ie, either the inner
* or outer side of the proposed join rel).
*
* Returns a pathkeys list that can be applied to the indicated relation.
*
* Note that it is not this routine's job to decide whether sorting is
* actually needed for a particular input path. Assume a sort is necessary;
* just make the keys, eh?
*/
List *
make_pathkeys_for_mergeclauses(PlannerInfo *root,
List *mergeclauses,
RelOptInfo *rel)
{
List *pathkeys = NIL;
ListCell *l;
foreach(l, mergeclauses)
{
RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(l);
List *pathkey;
cache_mergeclause_pathkeys(root, restrictinfo);
if (bms_is_subset(restrictinfo->left_relids, rel->relids))
{
/* Rel is left side of mergeclause */
pathkey = restrictinfo->left_pathkey;
}
else if (bms_is_subset(restrictinfo->right_relids, rel->relids))
{
/* Rel is right side of mergeclause */
pathkey = restrictinfo->right_pathkey;
}
else
{
elog(ERROR, "could not identify which side of mergeclause to use");
pathkey = NIL; /* keep compiler quiet */
}
/*
* When we are given multiple merge clauses, it's possible that some
* clauses refer to the same vars as earlier clauses. There's no
* reason for us to specify sort keys like (A,B,A) when (A,B) will do
* --- and adding redundant sort keys makes add_path think that this
* sort order is different from ones that are really the same, so
* don't do it. Since we now have a canonicalized pathkey, a simple
* ptrMember test is sufficient to detect redundant keys.
*/
pathkeys = list_append_unique_ptr(pathkeys, pathkey);
}
return pathkeys;
}
/****************************************************************************
* PATHKEY USEFULNESS CHECKS
*
* We only want to remember as many of the pathkeys of a path as have some
* potential use, either for subsequent mergejoins or for meeting the query's
* requested output ordering. This ensures that add_path() won't consider
* a path to have a usefully different ordering unless it really is useful.
* These routines check for usefulness of given pathkeys.
****************************************************************************/
/*
* pathkeys_useful_for_merging
* Count the number of pathkeys that may be useful for mergejoins
* above the given relation (by looking at its joininfo list).
*
* We consider a pathkey potentially useful if it corresponds to the merge
* ordering of either side of any joinclause for the rel. This might be
* overoptimistic, since joinclauses that require different other relations
* might never be usable at the same time, but trying to be exact is likely
* to be more trouble than it's worth.
*/
int
pathkeys_useful_for_merging(PlannerInfo *root, RelOptInfo *rel, List *pathkeys)
{
int useful = 0;
ListCell *i;
foreach(i, pathkeys)
{
List *pathkey = (List *) lfirst(i);
bool matched = false;
ListCell *j;
foreach(j, rel->joininfo)
{
RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(j);
if (restrictinfo->mergejoinoperator == InvalidOid)
continue;
cache_mergeclause_pathkeys(root, restrictinfo);
/*
* We can compare canonical pathkey sublists by simple pointer
* equality; see compare_pathkeys.
*/
if (pathkey == restrictinfo->left_pathkey ||
pathkey == restrictinfo->right_pathkey)
{
matched = true;
break;
}
}
/*
* If we didn't find a mergeclause, we're done --- any additional
* sort-key positions in the pathkeys are useless. (But we can still
* mergejoin if we found at least one mergeclause.)
*/
if (matched)
useful++;
else
break;
}
return useful;
}
/*
* pathkeys_useful_for_ordering
* Count the number of pathkeys that are useful for meeting the
* query's requested output ordering.
*
* Unlike merge pathkeys, this is an all-or-nothing affair: it does us
* no good to order by just the first key(s) of the requested ordering.
* So the result is always either 0 or list_length(root->query_pathkeys).
*/
int
pathkeys_useful_for_ordering(PlannerInfo *root, List *pathkeys)
{
if (root->query_pathkeys == NIL)
return 0; /* no special ordering requested */
if (pathkeys == NIL)
return 0; /* unordered path */
if (pathkeys_contained_in(root->query_pathkeys, pathkeys))
{
/* It's useful ... or at least the first N keys are */
return list_length(root->query_pathkeys);
}
return 0; /* path ordering not useful */
}
/*
* truncate_useless_pathkeys
* Shorten the given pathkey list to just the useful pathkeys.
*/
List *
truncate_useless_pathkeys(PlannerInfo *root,
RelOptInfo *rel,
List *pathkeys)
{
int nuseful;
int nuseful2;
nuseful = pathkeys_useful_for_merging(root, rel, pathkeys);
nuseful2 = pathkeys_useful_for_ordering(root, pathkeys);
if (nuseful2 > nuseful)
nuseful = nuseful2;
/*
* Note: not safe to modify input list destructively, but we can avoid
* copying the list if we're not actually going to change it
*/
if (nuseful == list_length(pathkeys))
return pathkeys;
else
return list_truncate(list_copy(pathkeys), nuseful);
}
|