From: jesperpedersen Date: Mon, 11 Nov 2019 08:49:43 -0500 Subject: [PATCH 1/2] Unique key Design by David Rowley. Author: Jesper Pedersen --- src/backend/nodes/outfuncs.c | 14 +++ src/backend/nodes/print.c | 39 +++++++ src/backend/optimizer/path/Makefile | 3 +- src/backend/optimizer/path/allpaths.c | 8 ++ src/backend/optimizer/path/costsize.c | 5 + src/backend/optimizer/path/indxpath.c | 41 +++++++ src/backend/optimizer/path/pathkeys.c | 71 ++++++++++-- src/backend/optimizer/path/uniquekey.c | 147 +++++++++++++++++++++++++ src/backend/optimizer/plan/planagg.c | 1 + src/backend/optimizer/plan/planmain.c | 1 + src/backend/optimizer/plan/planner.c | 17 ++- src/backend/optimizer/util/pathnode.c | 12 ++ src/include/nodes/nodes.h | 1 + src/include/nodes/pathnodes.h | 18 +++ src/include/nodes/print.h | 1 + src/include/optimizer/pathnode.h | 1 + src/include/optimizer/paths.h | 11 ++ 17 files changed, 378 insertions(+), 13 deletions(-) create mode 100644 src/backend/optimizer/path/uniquekey.c diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index b0dcd02ff6..1ccd68d3aa 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -1720,6 +1720,7 @@ _outPathInfo(StringInfo str, const Path *node) =09WRITE_FLOAT_FIELD(startup_cost, "%.2f"); =09WRITE_FLOAT_FIELD(total_cost, "%.2f"); =09WRITE_NODE_FIELD(pathkeys); +=09WRITE_NODE_FIELD(uniquekeys); } =20 /* @@ -2201,6 +2202,7 @@ _outPlannerInfo(StringInfo str, const PlannerInfo *no= de) =09WRITE_NODE_FIELD(eq_classes); =09WRITE_BOOL_FIELD(ec_merging_done); =09WRITE_NODE_FIELD(canon_pathkeys); +=09WRITE_NODE_FIELD(canon_uniquekeys); =09WRITE_NODE_FIELD(left_join_clauses); =09WRITE_NODE_FIELD(right_join_clauses); =09WRITE_NODE_FIELD(full_join_clauses); @@ -2210,6 +2212,7 @@ _outPlannerInfo(StringInfo str, const PlannerInfo *no= de) =09WRITE_NODE_FIELD(placeholder_list); =09WRITE_NODE_FIELD(fkey_list); =09WRITE_NODE_FIELD(query_pathkeys); +=09WRITE_NODE_FIELD(query_uniquekeys); =09WRITE_NODE_FIELD(group_pathkeys); =09WRITE_NODE_FIELD(window_pathkeys); =09WRITE_NODE_FIELD(distinct_pathkeys); @@ -2397,6 +2400,14 @@ _outPathKey(StringInfo str, const PathKey *node) =09WRITE_BOOL_FIELD(pk_nulls_first); } =20 +static void +_outUniqueKey(StringInfo str, const UniqueKey *node) +{ +=09WRITE_NODE_TYPE("UNIQUEKEY"); + +=09WRITE_NODE_FIELD(eq_clause); +} + static void _outPathTarget(StringInfo str, const PathTarget *node) { @@ -4083,6 +4094,9 @@ outNode(StringInfo str, const void *obj) =09=09=09case T_PathKey: =09=09=09=09_outPathKey(str, obj); =09=09=09=09break; +=09=09=09case T_UniqueKey: +=09=09=09=09_outUniqueKey(str, obj); +=09=09=09=09break; =09=09=09case T_PathTarget: =09=09=09=09_outPathTarget(str, obj); =09=09=09=09break; diff --git a/src/backend/nodes/print.c b/src/backend/nodes/print.c index 4ecde6b421..62c9d4ef7a 100644 --- a/src/backend/nodes/print.c +++ b/src/backend/nodes/print.c @@ -459,6 +459,45 @@ print_pathkeys(const List *pathkeys, const List *rtabl= e) =09printf(")\n"); } =20 +/* + * print_uniquekeys - + *=09 unique_key an UniqueKey + */ +void +print_uniquekeys(const List *uniquekeys, const List *rtable) +{ +=09ListCell *l; + +=09printf("("); +=09foreach(l, uniquekeys) +=09{ +=09=09UniqueKey *unique_key =3D (UniqueKey *) lfirst(l); +=09=09EquivalenceClass *eclass =3D (EquivalenceClass *) unique_key->eq_cla= use; +=09=09ListCell *k; +=09=09bool=09=09first =3D true; + +=09=09/* chase up */ +=09=09while (eclass->ec_merged) +=09=09=09eclass =3D eclass->ec_merged; + +=09=09printf("("); +=09=09foreach(k, eclass->ec_members) +=09=09{ +=09=09=09EquivalenceMember *mem =3D (EquivalenceMember *) lfirst(k); + +=09=09=09if (first) +=09=09=09=09first =3D false; +=09=09=09else +=09=09=09=09printf(", "); +=09=09=09print_expr((Node *) mem->em_expr, rtable); +=09=09} +=09=09printf(")"); +=09=09if (lnext(uniquekeys, l)) +=09=09=09printf(", "); +=09} +=09printf(")\n"); +} + /* * print_tl *=09 print targetlist in a more legible way. diff --git a/src/backend/optimizer/path/Makefile b/src/backend/optimizer/pa= th/Makefile index 1e199ff66f..63cc1505d9 100644 --- a/src/backend/optimizer/path/Makefile +++ b/src/backend/optimizer/path/Makefile @@ -21,6 +21,7 @@ OBJS =3D \ =09joinpath.o \ =09joinrels.o \ =09pathkeys.o \ -=09tidpath.o +=09tidpath.o \ +=09uniquekey.o =20 include $(top_srcdir)/src/backend/common.mk diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/= path/allpaths.c index db3a68a51d..5fc9b81746 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -3954,6 +3954,14 @@ print_path(PlannerInfo *root, Path *path, int indent= ) =09=09print_pathkeys(path->pathkeys, root->parse->rtable); =09} =20 +=09if (path->uniquekeys) +=09{ +=09=09for (i =3D 0; i < indent; i++) +=09=09=09printf("\t"); +=09=09printf(" uniquekeys: "); +=09=09print_uniquekeys(path->uniquekeys, root->parse->rtable); +=09} + =09if (join) =09{ =09=09JoinPath *jp =3D (JoinPath *) path; diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/= path/costsize.c index c5f6593485..0ec9a6db76 100644 --- a/src/backend/optimizer/path/costsize.c +++ b/src/backend/optimizer/path/costsize.c @@ -705,6 +705,11 @@ cost_index(IndexPath *path, PlannerInfo *root, double = loop_count, =09=09path->path.parallel_aware =3D true; =09} =20 +=09/* Consider cost based on unique key */ +=09if (path->path.uniquekeys) +=09{ +=09} + =09/* =09 * Now interpolate based on estimated index order correlation to get to= tal =09 * disk I/O cost for main table accesses. diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/= path/indxpath.c index 37b257cd0e..aa0da49119 100644 --- a/src/backend/optimizer/path/indxpath.c +++ b/src/backend/optimizer/path/indxpath.c @@ -189,6 +189,7 @@ static Expr *match_clause_to_ordering_op(IndexOptInfo *= index, static bool ec_member_matches_indexcol(PlannerInfo *root, RelOptInfo *rel, =09=09=09=09=09=09=09=09=09 EquivalenceClass *ec, EquivalenceMember *em, =09=09=09=09=09=09=09=09=09 void *arg); +static List *get_uniquekeys_for_index(PlannerInfo *root, List *pathkeys); =20 =20 /* @@ -874,6 +875,7 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel, =09List=09 *orderbyclausecols; =09List=09 *index_pathkeys; =09List=09 *useful_pathkeys; +=09List=09 *useful_uniquekeys; =09bool=09=09found_lower_saop_clause; =09bool=09=09pathkeys_possibly_useful; =09bool=09=09index_is_ordered; @@ -1036,11 +1038,15 @@ build_index_paths(PlannerInfo *root, RelOptInfo *re= l, =09if (index_clauses !=3D NIL || useful_pathkeys !=3D NIL || useful_predic= ate || =09=09index_only_scan) =09{ +=09=09if (has_useful_uniquekeys(root)) +=09=09=09useful_uniquekeys =3D get_uniquekeys_for_index(root, useful_pathk= eys); + =09=09ipath =3D create_index_path(root, index, =09=09=09=09=09=09=09=09 index_clauses, =09=09=09=09=09=09=09=09 orderbyclauses, =09=09=09=09=09=09=09=09 orderbyclausecols, =09=09=09=09=09=09=09=09 useful_pathkeys, +=09=09=09=09=09=09=09=09 useful_uniquekeys, =09=09=09=09=09=09=09=09 index_is_ordered ? =09=09=09=09=09=09=09=09 ForwardScanDirection : =09=09=09=09=09=09=09=09 NoMovementScanDirection, @@ -1063,6 +1069,7 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel, =09=09=09=09=09=09=09=09=09 orderbyclauses, =09=09=09=09=09=09=09=09=09 orderbyclausecols, =09=09=09=09=09=09=09=09=09 useful_pathkeys, +=09=09=09=09=09=09=09=09=09 useful_uniquekeys, =09=09=09=09=09=09=09=09=09 index_is_ordered ? =09=09=09=09=09=09=09=09=09 ForwardScanDirection : =09=09=09=09=09=09=09=09=09 NoMovementScanDirection, @@ -1093,11 +1100,15 @@ build_index_paths(PlannerInfo *root, RelOptInfo *re= l, =09=09=09=09=09=09=09=09=09=09=09=09=09index_pathkeys); =09=09if (useful_pathkeys !=3D NIL) =09=09{ +=09=09=09if (has_useful_uniquekeys(root)) +=09=09=09=09useful_uniquekeys =3D get_uniquekeys_for_index(root, useful_pa= thkeys); + =09=09=09ipath =3D create_index_path(root, index, =09=09=09=09=09=09=09=09=09 index_clauses, =09=09=09=09=09=09=09=09=09 NIL, =09=09=09=09=09=09=09=09=09 NIL, =09=09=09=09=09=09=09=09=09 useful_pathkeys, +=09=09=09=09=09=09=09=09=09 useful_uniquekeys, =09=09=09=09=09=09=09=09=09 BackwardScanDirection, =09=09=09=09=09=09=09=09=09 index_only_scan, =09=09=09=09=09=09=09=09=09 outer_relids, @@ -1115,6 +1126,7 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel, =09=09=09=09=09=09=09=09=09=09 NIL, =09=09=09=09=09=09=09=09=09=09 NIL, =09=09=09=09=09=09=09=09=09=09 useful_pathkeys, +=09=09=09=09=09=09=09=09=09=09 useful_uniquekeys, =09=09=09=09=09=09=09=09=09=09 BackwardScanDirection, =09=09=09=09=09=09=09=09=09=09 index_only_scan, =09=09=09=09=09=09=09=09=09=09 outer_relids, @@ -3365,6 +3377,35 @@ match_clause_to_ordering_op(IndexOptInfo *index, =09return clause; } =20 +/* + * get_uniquekeys_for_index + */ +static List * +get_uniquekeys_for_index(PlannerInfo *root, List *pathkeys) +{ +=09ListCell *lc; + +=09if (pathkeys) +=09{ +=09=09List *uniquekeys =3D NIL; +=09=09foreach(lc, pathkeys) +=09=09{ +=09=09=09UniqueKey *unique_key; +=09=09=09PathKey *pk =3D (PathKey *) lfirst(lc); +=09=09=09EquivalenceClass *ec =3D (EquivalenceClass *) pk->pk_eclass; + +=09=09=09unique_key =3D makeNode(UniqueKey); +=09=09=09unique_key->eq_clause =3D ec; +=09=09=09 +=09=09=09lappend(uniquekeys, unique_key); +=09=09} + +=09=09if (uniquekeys_contained_in(root->canon_uniquekeys, uniquekeys)) +=09=09=09return uniquekeys; +=09} + +=09return NIL; +} =20 /*************************************************************************= *** *=09=09=09=09---- ROUTINES TO DO PARTIAL INDEX PREDICATE TESTS=09---- diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/= path/pathkeys.c index 2f4fea241a..b0dc1bc22a 100644 --- a/src/backend/optimizer/path/pathkeys.c +++ b/src/backend/optimizer/path/pathkeys.c @@ -29,6 +29,7 @@ #include "utils/lsyscache.h" =20 =20 +static bool pathkey_is_unique(PathKey *new_pathkey, List *pathkeys); static bool pathkey_is_redundant(PathKey *new_pathkey, List *pathkeys); static bool matches_boolean_partition_clause(RestrictInfo *rinfo, =09=09=09=09=09=09=09=09=09=09=09 RelOptInfo *partrel, @@ -96,6 +97,29 @@ make_canonical_pathkey(PlannerInfo *root, =09return pk; } =20 +/* + * pathkey_is_unique + *=09 Checks if the new pathkey's equivalence class is the same as that = of + * any existing member of the pathkey list. + */ +static bool +pathkey_is_unique(PathKey *new_pathkey, List *pathkeys) +{ +=09EquivalenceClass *new_ec =3D new_pathkey->pk_eclass; +=09ListCell *lc; + +=09/* If same EC already is already in the list, then not unique */ +=09foreach(lc, pathkeys) +=09{ +=09=09PathKey *old_pathkey =3D (PathKey *) lfirst(lc); + +=09=09if (new_ec =3D=3D old_pathkey->pk_eclass) +=09=09=09return false; +=09} + +=09return true; +} + /* * pathkey_is_redundant *=09 Is a pathkey redundant with one already in the given list? @@ -135,22 +159,12 @@ static bool pathkey_is_redundant(PathKey *new_pathkey, List *pathkeys) { =09EquivalenceClass *new_ec =3D new_pathkey->pk_eclass; -=09ListCell *lc; =20 =09/* Check for EC containing a constant --- unconditionally redundant */ =09if (EC_MUST_BE_REDUNDANT(new_ec)) =09=09return true; =20 -=09/* If same EC already used in list, then redundant */ -=09foreach(lc, pathkeys) -=09{ -=09=09PathKey *old_pathkey =3D (PathKey *) lfirst(lc); - -=09=09if (new_ec =3D=3D old_pathkey->pk_eclass) -=09=09=09return true; -=09} - -=09return false; +=09return !pathkey_is_unique(new_pathkey, pathkeys); } =20 /* @@ -1098,6 +1112,41 @@ make_pathkeys_for_sortclauses(PlannerInfo *root, =09return pathkeys; } =20 +/* + * make_pathkeys_for_uniquekeyclauses + *=09=09Generate a pathkeys list to be used for uniquekey clauses + */ +List * +make_pathkeys_for_uniquekeys(PlannerInfo *root, +=09=09=09=09=09=09=09 List *sortclauses, +=09=09=09=09=09=09=09 List *tlist) +{ +=09List=09 *pathkeys =3D NIL; +=09ListCell *l; + +=09foreach(l, sortclauses) +=09{ +=09=09SortGroupClause *sortcl =3D (SortGroupClause *) lfirst(l); +=09=09Expr=09 *sortkey; +=09=09PathKey *pathkey; + +=09=09sortkey =3D (Expr *) get_sortgroupclause_expr(sortcl, tlist); +=09=09Assert(OidIsValid(sortcl->sortop)); +=09=09pathkey =3D make_pathkey_from_sortop(root, +=09=09=09=09=09=09=09=09=09=09 sortkey, +=09=09=09=09=09=09=09=09=09=09 root->nullable_baserels, +=09=09=09=09=09=09=09=09=09=09 sortcl->sortop, +=09=09=09=09=09=09=09=09=09=09 sortcl->nulls_first, +=09=09=09=09=09=09=09=09=09=09 sortcl->tleSortGroupRef, +=09=09=09=09=09=09=09=09=09=09 true); + +=09=09if (pathkey_is_unique(pathkey, pathkeys)) +=09=09=09pathkeys =3D lappend(pathkeys, pathkey); +=09} + +=09return pathkeys; +} + /*************************************************************************= *** *=09=09PATHKEYS AND MERGECLAUSES *************************************************************************= ***/ diff --git a/src/backend/optimizer/path/uniquekey.c b/src/backend/optimizer= /path/uniquekey.c new file mode 100644 index 0000000000..13d4ebb98c --- /dev/null +++ b/src/backend/optimizer/path/uniquekey.c @@ -0,0 +1,147 @@ +/*------------------------------------------------------------------------= - + * + * uniquekey.c + *=09 Utilities for matching and building unique keys + * + * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + *=09 src/backend/optimizer/path/uniquekey.c + * + *------------------------------------------------------------------------= - + */ +#include "postgres.h" + +#include "optimizer/pathnode.h" +#include "optimizer/paths.h" +#include "nodes/pg_list.h" + +static UniqueKey *make_canonical_uniquekey(PlannerInfo *root, EquivalenceC= lass *eclass); + +/* + * Build a list of unique keys + */ +List* +build_uniquekeys(PlannerInfo *root, List *sortclauses) +{ +=09List *result =3D NIL; +=09List *sortkeys; +=09ListCell *l; + +=09sortkeys =3D make_pathkeys_for_uniquekeys(root, +=09=09=09=09=09=09=09=09=09=09=09sortclauses, +=09=09=09=09=09=09=09=09=09=09=09root->processed_tlist); + +=09/* Create a uniquekey and add it to the list */ +=09foreach(l, sortkeys) +=09{ +=09=09PathKey *pathkey =3D (PathKey *) lfirst(l); +=09=09EquivalenceClass *ec =3D pathkey->pk_eclass; +=09=09UniqueKey *unique_key =3D make_canonical_uniquekey(root, ec); + +=09=09result =3D lappend(result, unique_key); +=09} + +=09return result; +} + +/* + * uniquekeys_contained_in + *=09 Are the keys2 included in the keys1 superset + */ +bool +uniquekeys_contained_in(List *keys1, List *keys2) +{ +=09ListCell *key1, +=09=09=09 *key2; + +=09/* +=09 * Fall out quickly if we are passed two identical lists. This mostly +=09 * catches the case where both are NIL, but that's common enough to +=09 * warrant the test. +=09 */ +=09if (keys1 =3D=3D keys2) +=09=09return true; + +=09foreach(key2, keys2) +=09{ +=09=09bool found =3D false; +=09=09UniqueKey *uniquekey2 =3D (UniqueKey *) lfirst(key2); + +=09=09foreach(key1, keys1) +=09=09{ +=09=09=09UniqueKey *uniquekey1 =3D (UniqueKey *) lfirst(key1); + +=09=09=09if (uniquekey1->eq_clause =3D=3D uniquekey2->eq_clause) +=09=09=09{ +=09=09=09=09found =3D true; +=09=09=09=09break; +=09=09=09} +=09=09} + +=09=09if (!found) +=09=09=09return false; +=09} + +=09return true; +} + +/* + * has_useful_uniquekeys + *=09=09Detect whether the planner could have any uniquekeys that are + *=09=09useful. + */ +bool +has_useful_uniquekeys(PlannerInfo *root) +{ +=09if (root->query_uniquekeys !=3D NIL) +=09=09return true;=09/* there are some */ +=09return false;=09=09/* definitely useless */ +} + +/* + * make_canonical_uniquekey + *=09 Given the parameters for a UniqueKey, find any pre-existing matchin= g + *=09 uniquekey in the query's list of "canonical" uniquekeys. Make a ne= w + *=09 entry if there's not one already. + * + * Note that this function must not be used until after we have completed + * merging EquivalenceClasses. (We don't try to enforce that here; instea= d, + * equivclass.c will complain if a merge occurs after root->canon_uniqueke= ys + * has become nonempty.) + */ +static UniqueKey * +make_canonical_uniquekey(PlannerInfo *root, +=09=09=09=09=09=09 EquivalenceClass *eclass) +{ +=09UniqueKey *uk; +=09ListCell *lc; +=09MemoryContext oldcontext; + +=09/* The passed eclass might be non-canonical, so chase up to the top */ +=09while (eclass->ec_merged) +=09=09eclass =3D eclass->ec_merged; + +=09foreach(lc, root->canon_uniquekeys) +=09{ +=09=09uk =3D (UniqueKey *) lfirst(lc); +=09=09if (eclass =3D=3D uk->eq_clause) +=09=09=09return uk; +=09} + +=09/* +=09 * Be sure canonical uniquekeys are allocated in the main planning cont= ext. +=09 * Not an issue in normal planning, but it is for GEQO. +=09 */ +=09oldcontext =3D MemoryContextSwitchTo(root->planner_cxt); + +=09uk =3D makeNode(UniqueKey); +=09uk->eq_clause =3D eclass; + +=09root->canon_uniquekeys =3D lappend(root->canon_uniquekeys, uk); + +=09MemoryContextSwitchTo(oldcontext); + +=09return uk; +} diff --git a/src/backend/optimizer/plan/planagg.c b/src/backend/optimizer/p= lan/planagg.c index 9381939c82..3d32f6dfd6 100644 --- a/src/backend/optimizer/plan/planagg.c +++ b/src/backend/optimizer/plan/planagg.c @@ -512,6 +512,7 @@ minmax_qp_callback(PlannerInfo *root, void *extra) =09=09=09=09=09=09=09=09=09 root->parse->targetList); =20 =09root->query_pathkeys =3D root->sort_pathkeys; +=09root->query_uniquekeys =3D NIL; } =20 /* diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/= plan/planmain.c index f0c1b52a2e..3ccde03ab7 100644 --- a/src/backend/optimizer/plan/planmain.c +++ b/src/backend/optimizer/plan/planmain.c @@ -70,6 +70,7 @@ query_planner(PlannerInfo *root, =09root->join_rel_level =3D NULL; =09root->join_cur_level =3D 0; =09root->canon_pathkeys =3D NIL; +=09root->canon_uniquekeys =3D NIL; =09root->left_join_clauses =3D NIL; =09root->right_join_clauses =3D NIL; =09root->full_join_clauses =3D NIL; diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/p= lan/planner.c index 17c5f086fb..2507ec7d2a 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -3652,15 +3652,30 @@ standard_qp_callback(PlannerInfo *root, void *extra= ) =09 * much easier, since we know that the parser ensured that one is a =09 * superset of the other. =09 */ +=09root->query_uniquekeys =3D NIL; + =09if (root->group_pathkeys) +=09{ =09=09root->query_pathkeys =3D root->group_pathkeys; + +=09=09if (!root->parse->hasAggs) +=09=09=09root->query_uniquekeys =3D build_uniquekeys(root, qp_extra->group= Clause); +=09} =09else if (root->window_pathkeys) =09=09root->query_pathkeys =3D root->window_pathkeys; =09else if (list_length(root->distinct_pathkeys) > =09=09=09 list_length(root->sort_pathkeys)) +=09{ =09=09root->query_pathkeys =3D root->distinct_pathkeys; +=09=09root->query_uniquekeys =3D build_uniquekeys(root, parse->distinctCla= use); +=09} =09else if (root->sort_pathkeys) +=09{ =09=09root->query_pathkeys =3D root->sort_pathkeys; + +=09=09if (root->distinct_pathkeys) +=09=09=09root->query_uniquekeys =3D build_uniquekeys(root, parse->distinct= Clause); +=09} =09else =09=09root->query_pathkeys =3D NIL; } @@ -6217,7 +6232,7 @@ plan_cluster_use_sort(Oid tableOid, Oid indexOid) =20 =09/* Estimate the cost of index scan */ =09indexScanPath =3D create_index_path(root, indexInfo, -=09=09=09=09=09=09=09=09=09 NIL, NIL, NIL, NIL, +=09=09=09=09=09=09=09=09=09 NIL, NIL, NIL, NIL, NIL, =09=09=09=09=09=09=09=09=09 ForwardScanDirection, false, =09=09=09=09=09=09=09=09=09 NULL, 1.0, false); =20 diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/= util/pathnode.c index 34acb732ee..f268112386 100644 --- a/src/backend/optimizer/util/pathnode.c +++ b/src/backend/optimizer/util/pathnode.c @@ -941,6 +941,7 @@ create_seqscan_path(PlannerInfo *root, RelOptInfo *rel, =09pathnode->parallel_safe =3D rel->consider_parallel; =09pathnode->parallel_workers =3D parallel_workers; =09pathnode->pathkeys =3D NIL;=09/* seqscan has unordered result */ +=09pathnode->uniquekeys =3D NIL; =20 =09cost_seqscan(pathnode, root, rel, pathnode->param_info); =20 @@ -965,6 +966,7 @@ create_samplescan_path(PlannerInfo *root, RelOptInfo *r= el, Relids required_outer =09pathnode->parallel_safe =3D rel->consider_parallel; =09pathnode->parallel_workers =3D 0; =09pathnode->pathkeys =3D NIL;=09/* samplescan has unordered result */ +=09pathnode->uniquekeys =3D NIL; =20 =09cost_samplescan(pathnode, root, rel, pathnode->param_info); =20 @@ -1001,6 +1003,7 @@ create_index_path(PlannerInfo *root, =09=09=09=09 List *indexorderbys, =09=09=09=09 List *indexorderbycols, =09=09=09=09 List *pathkeys, +=09=09=09=09 List *uniquekeys, =09=09=09=09 ScanDirection indexscandir, =09=09=09=09 bool indexonly, =09=09=09=09 Relids required_outer, @@ -1019,6 +1022,7 @@ create_index_path(PlannerInfo *root, =09pathnode->path.parallel_safe =3D rel->consider_parallel; =09pathnode->path.parallel_workers =3D 0; =09pathnode->path.pathkeys =3D pathkeys; +=09pathnode->path.uniquekeys =3D uniquekeys; =20 =09pathnode->indexinfo =3D index; =09pathnode->indexclauses =3D indexclauses; @@ -1062,6 +1066,7 @@ create_bitmap_heap_path(PlannerInfo *root, =09pathnode->path.parallel_safe =3D rel->consider_parallel; =09pathnode->path.parallel_workers =3D parallel_degree; =09pathnode->path.pathkeys =3D NIL;=09/* always unordered */ +=09pathnode->path.uniquekeys =3D NIL; =20 =09pathnode->bitmapqual =3D bitmapqual; =20 @@ -1923,6 +1928,7 @@ create_functionscan_path(PlannerInfo *root, RelOptInf= o *rel, =09pathnode->parallel_safe =3D rel->consider_parallel; =09pathnode->parallel_workers =3D 0; =09pathnode->pathkeys =3D pathkeys; +=09pathnode->uniquekeys =3D NIL; =20 =09cost_functionscan(pathnode, root, rel, pathnode->param_info); =20 @@ -1949,6 +1955,7 @@ create_tablefuncscan_path(PlannerInfo *root, RelOptIn= fo *rel, =09pathnode->parallel_safe =3D rel->consider_parallel; =09pathnode->parallel_workers =3D 0; =09pathnode->pathkeys =3D NIL;=09/* result is always unordered */ +=09pathnode->uniquekeys =3D NIL; =20 =09cost_tablefuncscan(pathnode, root, rel, pathnode->param_info); =20 @@ -1975,6 +1982,7 @@ create_valuesscan_path(PlannerInfo *root, RelOptInfo = *rel, =09pathnode->parallel_safe =3D rel->consider_parallel; =09pathnode->parallel_workers =3D 0; =09pathnode->pathkeys =3D NIL;=09/* result is always unordered */ +=09pathnode->uniquekeys =3D NIL; =20 =09cost_valuesscan(pathnode, root, rel, pathnode->param_info); =20 @@ -2000,6 +2008,7 @@ create_ctescan_path(PlannerInfo *root, RelOptInfo *re= l, Relids required_outer) =09pathnode->parallel_safe =3D rel->consider_parallel; =09pathnode->parallel_workers =3D 0; =09pathnode->pathkeys =3D NIL;=09/* XXX for now, result is always unordere= d */ +=09pathnode->uniquekeys =3D NIL; =20 =09cost_ctescan(pathnode, root, rel, pathnode->param_info); =20 @@ -2026,6 +2035,7 @@ create_namedtuplestorescan_path(PlannerInfo *root, Re= lOptInfo *rel, =09pathnode->parallel_safe =3D rel->consider_parallel; =09pathnode->parallel_workers =3D 0; =09pathnode->pathkeys =3D NIL;=09/* result is always unordered */ +=09pathnode->uniquekeys =3D NIL; =20 =09cost_namedtuplestorescan(pathnode, root, rel, pathnode->param_info); =20 @@ -2052,6 +2062,7 @@ create_resultscan_path(PlannerInfo *root, RelOptInfo = *rel, =09pathnode->parallel_safe =3D rel->consider_parallel; =09pathnode->parallel_workers =3D 0; =09pathnode->pathkeys =3D NIL;=09/* result is always unordered */ +=09pathnode->uniquekeys =3D NIL; =20 =09cost_resultscan(pathnode, root, rel, pathnode->param_info); =20 @@ -2078,6 +2089,7 @@ create_worktablescan_path(PlannerInfo *root, RelOptIn= fo *rel, =09pathnode->parallel_safe =3D rel->consider_parallel; =09pathnode->parallel_workers =3D 0; =09pathnode->pathkeys =3D NIL;=09/* result is always unordered */ +=09pathnode->uniquekeys =3D NIL; =20 =09/* Cost is the same as for a regular CTE scan */ =09cost_ctescan(pathnode, root, rel, pathnode->param_info); diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index bce2d59b0d..cbb6ba2586 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -261,6 +261,7 @@ typedef enum NodeTag =09T_EquivalenceMember, =09T_PathKey, =09T_PathTarget, +=09T_UniqueKey, =09T_RestrictInfo, =09T_IndexClause, =09T_PlaceHolderVar, diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h index 23a06d718e..6198c31cd4 100644 --- a/src/include/nodes/pathnodes.h +++ b/src/include/nodes/pathnodes.h @@ -267,6 +267,8 @@ struct PlannerInfo =20 =09List=09 *canon_pathkeys; /* list of "canonical" PathKeys */ =20 +=09List=09 *canon_uniquekeys; /* list of "canonical" UniqueKeys */ + =09List=09 *left_join_clauses;=09/* list of RestrictInfos for mergejoina= ble =09=09=09=09=09=09=09=09=09 * outer join clauses w/nonnullable var on =09=09=09=09=09=09=09=09=09 * left */ @@ -295,6 +297,8 @@ struct PlannerInfo =20 =09List=09 *query_pathkeys; /* desired pathkeys for query_planner() */ =20 +=09List=09 *query_uniquekeys; /* */ + =09List=09 *group_pathkeys; /* groupClause pathkeys, if any */ =09List=09 *window_pathkeys;=09/* pathkeys of bottom window, if any */ =09List=09 *distinct_pathkeys;=09/* distinctClause pathkeys, if any */ @@ -1075,6 +1079,15 @@ typedef struct ParamPathInfo =09List=09 *ppi_clauses;=09/* join clauses available from outer rels */ } ParamPathInfo; =20 +/* + * UniqueKey + */ +typedef struct UniqueKey +{ +=09NodeTag=09=09type; + +=09EquivalenceClass *eq_clause;=09/* equivalence class */ +} UniqueKey; =20 /* * Type "Path" is used as-is for sequential-scan paths, as well as some ot= her @@ -1104,6 +1117,9 @@ typedef struct ParamPathInfo * * "pathkeys" is a List of PathKey nodes (see above), describing the sort * ordering of the path's output rows. + * + * "uniquekeys", if not NIL, is a list of UniqueKey nodes (see above), + * describing the XXX. */ typedef struct Path { @@ -1127,6 +1143,8 @@ typedef struct Path =20 =09List=09 *pathkeys;=09=09/* sort ordering of path's output */ =09/* pathkeys is a List of PathKey nodes; see above */ + +=09List=09 *uniquekeys;=09/* the unique keys, or NIL if none */ } Path; =20 /* Macro for extracting a path's parameterization relids; beware double ev= al */ diff --git a/src/include/nodes/print.h b/src/include/nodes/print.h index cbff56a724..f1a7112877 100644 --- a/src/include/nodes/print.h +++ b/src/include/nodes/print.h @@ -28,6 +28,7 @@ extern char *pretty_format_node_dump(const char *dump); extern void print_rt(const List *rtable); extern void print_expr(const Node *expr, const List *rtable); extern void print_pathkeys(const List *pathkeys, const List *rtable); +extern void print_uniquekeys(const List *uniquekeys, const List *rtable); extern void print_tl(const List *tlist, const List *rtable); extern void print_slot(TupleTableSlot *slot); =20 diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathn= ode.h index a12af54971..37a946f857 100644 --- a/src/include/optimizer/pathnode.h +++ b/src/include/optimizer/pathnode.h @@ -44,6 +44,7 @@ extern IndexPath *create_index_path(PlannerInfo *root, =09=09=09=09=09=09=09=09=09List *indexorderbys, =09=09=09=09=09=09=09=09=09List *indexorderbycols, =09=09=09=09=09=09=09=09=09List *pathkeys, +=09=09=09=09=09=09=09=09=09List *uniquekeys, =09=09=09=09=09=09=09=09=09ScanDirection indexscandir, =09=09=09=09=09=09=09=09=09bool indexonly, =09=09=09=09=09=09=09=09=09Relids required_outer, diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h index c6c34630c2..c79e47eeaf 100644 --- a/src/include/optimizer/paths.h +++ b/src/include/optimizer/paths.h @@ -214,6 +214,9 @@ extern List *build_join_pathkeys(PlannerInfo *root, extern List *make_pathkeys_for_sortclauses(PlannerInfo *root, =09=09=09=09=09=09=09=09=09=09 List *sortclauses, =09=09=09=09=09=09=09=09=09=09 List *tlist); +extern List *make_pathkeys_for_uniquekeys(PlannerInfo *root, +=09=09=09=09=09=09=09=09=09=09 List *sortclauses, +=09=09=09=09=09=09=09=09=09=09 List *tlist); extern void initialize_mergeclause_eclasses(PlannerInfo *root, =09=09=09=09=09=09=09=09=09=09=09RestrictInfo *restrictinfo); extern void update_mergeclause_eclasses(PlannerInfo *root, @@ -240,4 +243,12 @@ extern PathKey *make_canonical_pathkey(PlannerInfo *ro= ot, extern void add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel, =09=09=09=09=09=09=09=09=09List *live_childrels); =20 +/* + * uniquekey.c + *=09 Utilities for matching and building unique keys + */ +extern List *build_uniquekeys(PlannerInfo *root, List *sortclauses); +extern bool uniquekeys_contained_in(List *keys1, List *keys2); +extern bool has_useful_uniquekeys(PlannerInfo *root); + #endif=09=09=09=09=09=09=09/* PATHS_H */ --=20 2.21.0 --------------25BA7482CCCC3CA4CEC0D330 Content-Type: text/x-patch; charset=UTF-8; name="v29_0002-Index-skip-scan.patch" Content-Transfer-Encoding: quoted-printable Content-Disposition: attachment; filename="v29_0002-Index-skip-scan.patch"