diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 71e3058..d113c71 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -1985,6 +1985,7 @@ _copyRangeTblEntry(const RangeTblEntry *from) COPY_NODE_FIELD(funccoltypmods); COPY_NODE_FIELD(funccolcollations); COPY_SCALAR_FIELD(funcordinality); + COPY_SCALAR_FIELD(funcordinality_attno); COPY_NODE_FIELD(values_lists); COPY_NODE_FIELD(values_collations); COPY_STRING_FIELD(ctename); diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 3183ccf..8d2ce43 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -2236,6 +2236,7 @@ _equalRangeTblEntry(const RangeTblEntry *a, const RangeTblEntry *b) COMPARE_NODE_FIELD(funccoltypmods); COMPARE_NODE_FIELD(funccolcollations); COMPARE_SCALAR_FIELD(funcordinality); + COMPARE_SCALAR_FIELD(funcordinality_attno); COMPARE_NODE_FIELD(values_lists); COMPARE_NODE_FIELD(values_collations); COMPARE_STRING_FIELD(ctename); diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index bad2239..a2e12ac 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -2384,6 +2384,7 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node) WRITE_NODE_FIELD(funccoltypmods); WRITE_NODE_FIELD(funccolcollations); WRITE_BOOL_FIELD(funcordinality); + WRITE_INT_FIELD(funcordinality_attno); break; case RTE_VALUES: WRITE_NODE_FIELD(values_lists); diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index aad63e5..eb36a57 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -1224,6 +1224,7 @@ _readRangeTblEntry(void) READ_NODE_FIELD(funccoltypmods); READ_NODE_FIELD(funccolcollations); READ_BOOL_FIELD(funcordinality); + READ_INT_FIELD(funcordinality_attno); break; case RTE_VALUES: READ_NODE_FIELD(values_lists); diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index 4b8a73d..63d53bf 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -1259,6 +1259,7 @@ static void set_function_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) { Relids required_outer; + List *pathkeys; /* * We don't support pushing join clauses into the quals of a function @@ -1267,8 +1268,11 @@ set_function_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) */ required_outer = rel->lateral_relids; + /* Generate pathkeys for a function scan */ + pathkeys = build_function_pathkeys(root, rel, rte); + /* Generate appropriate path */ - add_path(rel, create_functionscan_path(root, rel, required_outer)); + add_path(rel, create_functionscan_path(root, rel, pathkeys, required_outer)); /* Select cheapest path (pretty easy in this case...) */ set_cheapest(rel); diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c index 6724996..9f3f261 100644 --- a/src/backend/optimizer/path/pathkeys.c +++ b/src/backend/optimizer/path/pathkeys.c @@ -18,6 +18,7 @@ #include "postgres.h" #include "access/skey.h" +#include "catalog/pg_type.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" #include "nodes/plannodes.h" @@ -26,6 +27,7 @@ #include "optimizer/paths.h" #include "optimizer/tlist.h" #include "utils/lsyscache.h" +#include "utils/typcache.h" static PathKey *make_canonical_pathkey(PlannerInfo *root, @@ -699,6 +701,64 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel, } /* + * build_function_pathkeys + * Generate a single-PathKey list that represents the sort order specified + * by a specific column of a function scan. + */ +List * +build_function_pathkeys(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) +{ + TypeCacheEntry *typentry; + Oid sortop; + Oid opfamily; + Oid opcintype; + int16 strategy; + Expr *expr; + PathKey *cpathkey = NULL; + + if (rte->funcordinality == false || + has_useful_pathkeys(root, rel) == false) + return NIL; + + Assert(rte->funcordinality_attno > 0); + + /* Find the sort operator using the type cache */ + typentry = lookup_type_cache(INT8OID, TYPECACHE_LT_OPR); + sortop = typentry->lt_opr; + + /* Find the operator in pg_amop --- failure shouldn't happen */ + if (!get_ordering_op_properties(sortop, + &opfamily, &opcintype, &strategy)) + elog(ERROR, "operator %u is not a valid ordering operator", + sortop); + + /* Build a representation for ordinality column */ + expr = (Expr *) makeVar(rel->relid, + rte->funcordinality_attno, + INT8OID, + -1, + InvalidOid, + 0); + + /* OK, Try to make a canonical pathkey for ordinality column */ + cpathkey = make_pathkey_from_sortinfo(root, + expr, + opfamily, + opcintype, + InvalidOid, + false, + false, + 0, + rel->relids, + false); + + if (cpathkey != NULL) + return truncate_useless_pathkeys(root, rel, list_make1(cpathkey)); + else + return NIL; +} + +/* * build_join_pathkeys * Build the path keys for a join relation constructed by mergejoin or * nestloop join. This is normally the same as the outer path's keys. diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c index 64b1705..a7169ef 100644 --- a/src/backend/optimizer/util/pathnode.c +++ b/src/backend/optimizer/util/pathnode.c @@ -1623,7 +1623,7 @@ create_subqueryscan_path(PlannerInfo *root, RelOptInfo *rel, */ Path * create_functionscan_path(PlannerInfo *root, RelOptInfo *rel, - Relids required_outer) + List *pathkeys, Relids required_outer) { Path *pathnode = makeNode(Path); @@ -1631,7 +1631,7 @@ create_functionscan_path(PlannerInfo *root, RelOptInfo *rel, pathnode->parent = rel; pathnode->param_info = get_baserel_parampathinfo(root, rel, required_outer); - pathnode->pathkeys = NIL; /* for now, assume unordered result */ + pathnode->pathkeys = pathkeys; cost_functionscan(pathnode, root, rel, pathnode->param_info); diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index 39922d3..dd53c7a 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -1228,6 +1228,7 @@ addRangeTableEntryForFunction(ParseState *pstate, Alias *alias = rangefunc->alias; List *coldeflist = rangefunc->coldeflist; Alias *eref; + AttrNumber ordinality_attno = 0; rte->rtekind = RTE_FUNCTION; rte->relid = InvalidOid; @@ -1275,11 +1276,15 @@ addRangeTableEntryForFunction(ParseState *pstate, Assert(tupdesc); /* Build the column alias list */ buildRelationAliases(tupdesc, alias, eref, rangefunc->ordinality); + + ordinality_attno = tupdesc->natts + 1; } else if (functypclass == TYPEFUNC_SCALAR) { /* Base data type, i.e. scalar */ buildScalarFunctionAlias(funcexpr, funcname, alias, eref, rangefunc->ordinality); + + ordinality_attno = 2; } else if (functypclass == TYPEFUNC_RECORD) { @@ -1334,6 +1339,7 @@ addRangeTableEntryForFunction(ParseState *pstate, */ rte->lateral = lateral; rte->funcordinality = rangefunc->ordinality; + rte->funcordinality_attno = ordinality_attno; rte->inh = false; /* never true for functions */ rte->inFromCl = inFromCl; diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 51fef68..49a7017 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -772,6 +772,7 @@ typedef struct RangeTblEntry List *funccoltypmods; /* integer list of column typmods */ List *funccolcollations; /* OID list of column collation OIDs */ bool funcordinality; /* is this called WITH ORDINALITY? */ + AttrNumber funcordinality_attno; /* attno of ordinality column */ /* * Fields valid for a values RTE (else NIL): diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h index bc68789..bc9bc34 100644 --- a/src/include/optimizer/pathnode.h +++ b/src/include/optimizer/pathnode.h @@ -70,7 +70,7 @@ extern UniquePath *create_unique_path(PlannerInfo *root, RelOptInfo *rel, extern Path *create_subqueryscan_path(PlannerInfo *root, RelOptInfo *rel, List *pathkeys, Relids required_outer); extern Path *create_functionscan_path(PlannerInfo *root, RelOptInfo *rel, - Relids required_outer); + List *pathkeys, Relids required_outer); extern Path *create_valuesscan_path(PlannerInfo *root, RelOptInfo *rel, Relids required_outer); extern Path *create_ctescan_path(PlannerInfo *root, RelOptInfo *rel, diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h index 9ef93c7..3a2ace8 100644 --- a/src/include/optimizer/paths.h +++ b/src/include/optimizer/paths.h @@ -167,6 +167,8 @@ extern List *build_index_pathkeys(PlannerInfo *root, IndexOptInfo *index, ScanDirection scandir); extern List *convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel, List *subquery_pathkeys); +extern List *build_function_pathkeys(PlannerInfo *root, RelOptInfo *rel, + RangeTblEntry *rte); extern List *build_join_pathkeys(PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype,