01_tid_scan_ordering.patch
application/octet-stream
Filename: 01_tid_scan_ordering.patch
Type: application/octet-stream
Part: 0
Message:
Tid scan improvements
Patch
Format: unified
| File | + | − |
|---|---|---|
| src/backend/commands/explain.c | 26 | 10 |
| src/backend/executor/nodeTidscan.c | 21 | 8 |
| src/backend/nodes/copyfuncs.c | 1 | 0 |
| src/backend/nodes/outfuncs.c | 1 | 0 |
| src/backend/nodes/readfuncs.c | 1 | 0 |
| src/backend/optimizer/path/pathkeys.c | 19 | 0 |
| src/backend/optimizer/path/tidpath.c | 27 | 1 |
| src/backend/optimizer/plan/createplan.c | 7 | 3 |
| src/backend/optimizer/util/pathnode.c | 3 | 1 |
| src/include/catalog/pg_operator.dat | 1 | 1 |
| src/include/nodes/plannodes.h | 1 | 0 |
| src/include/nodes/relation.h | 1 | 0 |
| src/include/optimizer/pathnode.h | 2 | 1 |
| src/include/optimizer/paths.h | 3 | 0 |
| src/test/regress/expected/tidscan.out | 33 | 0 |
| src/test/regress/sql/tidscan.sql | 9 | 0 |
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 16a80a0..d9deb72 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -111,6 +111,7 @@ static void show_foreignscan_info(ForeignScanState *fsstate, ExplainState *es);
static void show_eval_params(Bitmapset *bms_params, ExplainState *es);
static const char *explain_get_index_name(Oid indexId);
static void show_buffer_usage(ExplainState *es, const BufferUsage *usage);
+static void show_scan_direction(ExplainState *es, ScanDirection direction);
static void ExplainIndexScanDetails(Oid indexid, ScanDirection indexorderdir,
ExplainState *es);
static void ExplainScanTarget(Scan *plan, ExplainState *es);
@@ -1194,7 +1195,6 @@ ExplainNode(PlanState *planstate, List *ancestors,
case T_SeqScan:
case T_SampleScan:
case T_BitmapHeapScan:
- case T_TidScan:
case T_SubqueryScan:
case T_FunctionScan:
case T_TableFuncScan:
@@ -1203,6 +1203,10 @@ ExplainNode(PlanState *planstate, List *ancestors,
case T_WorkTableScan:
ExplainScanTarget((Scan *) plan, es);
break;
+ case T_TidScan:
+ show_scan_direction(es, ((TidScan *) plan)->direction);
+ ExplainScanTarget((Scan *) plan, es);
+ break;
case T_ForeignScan:
case T_CustomScan:
if (((Scan *) plan)->scanrelid > 0)
@@ -2797,25 +2801,21 @@ show_buffer_usage(ExplainState *es, const BufferUsage *usage)
}
/*
- * Add some additional details about an IndexScan or IndexOnlyScan
+ * Show the direction of a scan.
*/
static void
-ExplainIndexScanDetails(Oid indexid, ScanDirection indexorderdir,
- ExplainState *es)
+show_scan_direction(ExplainState *es, ScanDirection direction)
{
- const char *indexname = explain_get_index_name(indexid);
-
if (es->format == EXPLAIN_FORMAT_TEXT)
{
- if (ScanDirectionIsBackward(indexorderdir))
+ if (ScanDirectionIsBackward(direction))
appendStringInfoString(es->str, " Backward");
- appendStringInfo(es->str, " using %s", indexname);
}
else
{
const char *scandir;
- switch (indexorderdir)
+ switch (direction)
{
case BackwardScanDirection:
scandir = "Backward";
@@ -2831,11 +2831,27 @@ ExplainIndexScanDetails(Oid indexid, ScanDirection indexorderdir,
break;
}
ExplainPropertyText("Scan Direction", scandir, es);
- ExplainPropertyText("Index Name", indexname, es);
}
}
/*
+ * Add some additional details about an IndexScan or IndexOnlyScan
+ */
+static void
+ExplainIndexScanDetails(Oid indexid, ScanDirection indexorderdir,
+ ExplainState *es)
+{
+ const char *indexname = explain_get_index_name(indexid);
+
+ show_scan_direction(es, indexorderdir);
+
+ if (es->format == EXPLAIN_FORMAT_TEXT)
+ appendStringInfo(es->str, " using %s", indexname);
+ else
+ ExplainPropertyText("Index Name", indexname, es);
+}
+
+/*
* Show the target of a Scan node
*/
static void
diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c
index e207b1f..df11d92 100644
--- a/src/backend/executor/nodeTidscan.c
+++ b/src/backend/executor/nodeTidscan.c
@@ -50,6 +50,7 @@ typedef struct TidExpr
static void TidExprListCreate(TidScanState *tidstate);
static void TidListEval(TidScanState *tidstate);
static int itemptr_comparator(const void *a, const void *b);
+static int itemptr_comparator_reverse(const void *a, const void *b);
static TupleTableSlot *TidNext(TidScanState *node);
@@ -225,13 +226,12 @@ TidListEval(TidScanState *tidstate)
RelationGetRelid(tidstate->ss.ss_currentRelation),
&cursor_tid))
{
- if (numTids >= numAllocTids)
- {
- numAllocTids *= 2;
- tidList = (ItemPointerData *)
- repalloc(tidList,
- numAllocTids * sizeof(ItemPointerData));
- }
+ /*
+ * A current-of TidExpr only exists by itself, and we should
+ * already have allocated a tidList entry for it. We don't
+ * need to check whether the tidList array needs to be resized.
+ */
+ Assert(numTids < numAllocTids);
tidList[numTids++] = cursor_tid;
}
}
@@ -247,12 +247,16 @@ TidListEval(TidScanState *tidstate)
{
int lastTid;
int i;
+ int (* cmp) (const void *, const void *);
+
+ /* Choose the sort order based on the scan direction. */
+ cmp = ScanDirectionIsBackward(((TidScan *) tidstate->ss.ps.plan)->direction) ? itemptr_comparator_reverse : itemptr_comparator;
/* CurrentOfExpr could never appear OR'd with something else */
Assert(!tidstate->tss_isCurrentOf);
qsort((void *) tidList, numTids, sizeof(ItemPointerData),
- itemptr_comparator);
+ cmp);
lastTid = 0;
for (i = 1; i < numTids; i++)
{
@@ -291,6 +295,15 @@ itemptr_comparator(const void *a, const void *b)
return 0;
}
+/*
+ * qsort comparator for ItemPointerData items, in reverse order
+ */
+static int
+itemptr_comparator_reverse(const void *a, const void *b)
+{
+ return itemptr_comparator(b,a);
+}
+
/* ----------------------------------------------------------------
* TidNext
*
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 7c8220c..5f84984 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -583,6 +583,7 @@ _copyTidScan(const TidScan *from)
* copy remainder of node
*/
COPY_NODE_FIELD(tidquals);
+ COPY_SCALAR_FIELD(direction);
return newnode;
}
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 6269f47..870dd2e 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -615,6 +615,7 @@ _outTidScan(StringInfo str, const TidScan *node)
_outScanInfo(str, (const Scan *) node);
WRITE_NODE_FIELD(tidquals);
+ WRITE_ENUM_FIELD(direction, ScanDirection);
}
static void
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 3254524..2317f58 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -1828,6 +1828,7 @@ _readTidScan(void)
ReadCommonScan(&local_node->scan);
READ_NODE_FIELD(tidquals);
+ READ_ENUM_FIELD(direction, ScanDirection);
READ_DONE();
}
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index ec66cb9..729b207 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -18,6 +18,9 @@
#include "postgres.h"
#include "access/stratnum.h"
+#include "access/sysattr.h"
+#include "catalog/pg_operator.h"
+#include "catalog/pg_type.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "nodes/plannodes.h"
@@ -848,6 +851,22 @@ build_join_pathkeys(PlannerInfo *root,
return truncate_useless_pathkeys(root, joinrel, outer_pathkeys);
}
+/*
+ * build_tidscan_pathkeys
+ * Build the path keys corresponding to ORDER BY ctid ASC|DESC.
+ */
+List *
+build_tidscan_pathkeys(PlannerInfo *root,
+ RelOptInfo *rel,
+ ScanDirection direction)
+{
+ int opno = (direction == ForwardScanDirection) ? TIDLessOperator : TIDGreaterOperator;
+ Var *varexpr = makeVar(rel->relid, SelfItemPointerAttributeNumber, TIDOID, -1, InvalidOid, 0);
+ List *pathkeys = build_expression_pathkey(root, (Expr *) varexpr, NULL, opno, rel->relids, true);
+
+ return pathkeys;
+}
+
/****************************************************************************
* PATHKEYS AND SORT CLAUSES
****************************************************************************/
diff --git a/src/backend/optimizer/path/tidpath.c b/src/backend/optimizer/path/tidpath.c
index 3bb5b8d..7a40700 100644
--- a/src/backend/optimizer/path/tidpath.c
+++ b/src/backend/optimizer/path/tidpath.c
@@ -247,6 +247,8 @@ TidQualFromBaseRestrictinfo(RelOptInfo *rel)
* create_tidscan_paths
* Create paths corresponding to direct TID scans of the given rel.
*
+ * Path keys and direction will be set on the scans if it looks useful.
+ *
* Candidate paths are added to the rel's pathlist (using add_path).
*/
void
@@ -265,6 +267,30 @@ create_tidscan_paths(PlannerInfo *root, RelOptInfo *rel)
tidquals = TidQualFromBaseRestrictinfo(rel);
if (tidquals)
- add_path(rel, (Path *) create_tidscan_path(root, rel, tidquals,
+ {
+ List *pathkeys = NULL;
+ ScanDirection direction = ForwardScanDirection;
+
+ if (has_useful_pathkeys(root, rel)) {
+ /*
+ * Build path keys corresponding to ORDER BY ctid ASC, and check
+ * whether they will be useful for this scan. If not, build
+ * path keys for DESC, and try that; set the direction to
+ * BackwardScanDirection if so. If neither of them will be
+ * useful, no path keys will be set.
+ */
+ pathkeys = build_tidscan_pathkeys(root, rel, ForwardScanDirection);
+ if (!pathkeys_contained_in(pathkeys, root->query_pathkeys))
+ {
+ pathkeys = build_tidscan_pathkeys(root, rel, BackwardScanDirection);
+ if (pathkeys_contained_in(pathkeys, root->query_pathkeys))
+ direction = BackwardScanDirection;
+ else
+ pathkeys = NULL;
+ }
+ }
+
+ add_path(rel, (Path *) create_tidscan_path(root, rel, tidquals, pathkeys, direction,
required_outer));
+ }
}
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index ae41c9e..4e1faa6 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -185,7 +185,7 @@ static BitmapHeapScan *make_bitmap_heapscan(List *qptlist,
List *bitmapqualorig,
Index scanrelid);
static TidScan *make_tidscan(List *qptlist, List *qpqual, Index scanrelid,
- List *tidquals);
+ List *tidquals, ScanDirection direction);
static SubqueryScan *make_subqueryscan(List *qptlist,
List *qpqual,
Index scanrelid,
@@ -3097,7 +3097,9 @@ create_tidscan_plan(PlannerInfo *root, TidPath *best_path,
scan_plan = make_tidscan(tlist,
scan_clauses,
scan_relid,
- tidquals);
+ tidquals,
+ best_path->direction
+ );
copy_generic_path_info(&scan_plan->scan.plan, &best_path->path);
@@ -5179,7 +5181,8 @@ static TidScan *
make_tidscan(List *qptlist,
List *qpqual,
Index scanrelid,
- List *tidquals)
+ List *tidquals,
+ ScanDirection direction)
{
TidScan *node = makeNode(TidScan);
Plan *plan = &node->scan.plan;
@@ -5190,6 +5193,7 @@ make_tidscan(List *qptlist,
plan->righttree = NULL;
node->scan.scanrelid = scanrelid;
node->tidquals = tidquals;
+ node->direction = direction;
return node;
}
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index c5aaaf5..e2d51a9 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -1186,6 +1186,7 @@ create_bitmap_or_path(PlannerInfo *root,
*/
TidPath *
create_tidscan_path(PlannerInfo *root, RelOptInfo *rel, List *tidquals,
+ List *pathkeys, ScanDirection direction,
Relids required_outer)
{
TidPath *pathnode = makeNode(TidPath);
@@ -1198,9 +1199,10 @@ create_tidscan_path(PlannerInfo *root, RelOptInfo *rel, List *tidquals,
pathnode->path.parallel_aware = false;
pathnode->path.parallel_safe = rel->consider_parallel;
pathnode->path.parallel_workers = 0;
- pathnode->path.pathkeys = NIL; /* always unordered */
+ pathnode->path.pathkeys = pathkeys;
pathnode->tidquals = tidquals;
+ pathnode->direction = direction;
cost_tidscan(&pathnode->path, root, rel, tidquals,
pathnode->path.param_info);
diff --git a/src/include/catalog/pg_operator.dat b/src/include/catalog/pg_operator.dat
index d9b6bad..31e7d61 100644
--- a/src/include/catalog/pg_operator.dat
+++ b/src/include/catalog/pg_operator.dat
@@ -156,7 +156,7 @@
oprname => '<', oprleft => 'tid', oprright => 'tid', oprresult => 'bool',
oprcom => '>(tid,tid)', oprnegate => '>=(tid,tid)', oprcode => 'tidlt',
oprrest => 'scalarltsel', oprjoin => 'scalarltjoinsel' },
-{ oid => '2800', descr => 'greater than',
+{ oid => '2800', oid_symbol => 'TIDGreaterOperator', descr => 'greater than',
oprname => '>', oprleft => 'tid', oprright => 'tid', oprresult => 'bool',
oprcom => '<(tid,tid)', oprnegate => '<=(tid,tid)', oprcode => 'tidgt',
oprrest => 'scalargtsel', oprjoin => 'scalargtjoinsel' },
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 7c2abbd..96d30aa 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -492,6 +492,7 @@ typedef struct TidScan
{
Scan scan;
List *tidquals; /* qual(s) involving CTID = something */
+ ScanDirection direction;
} TidScan;
/* ----------------
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index 41caf87..cf4839d 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -1233,6 +1233,7 @@ typedef struct TidPath
{
Path path;
List *tidquals; /* qual(s) involving CTID = something */
+ ScanDirection direction;
} TidPath;
/*
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index 7c5ff22..a0a88a5 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -63,7 +63,8 @@ extern BitmapOrPath *create_bitmap_or_path(PlannerInfo *root,
RelOptInfo *rel,
List *bitmapquals);
extern TidPath *create_tidscan_path(PlannerInfo *root, RelOptInfo *rel,
- List *tidquals, Relids required_outer);
+ List *tidquals, List *pathkeys, ScanDirection direction,
+ Relids required_outer);
extern AppendPath *create_append_path(PlannerInfo *root, RelOptInfo *rel,
List *subpaths, List *partial_subpaths,
Relids required_outer,
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index cafde30..3b915b5 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -211,6 +211,9 @@ extern List *build_join_pathkeys(PlannerInfo *root,
RelOptInfo *joinrel,
JoinType jointype,
List *outer_pathkeys);
+extern List *build_tidscan_pathkeys(PlannerInfo *root,
+ RelOptInfo *rel,
+ ScanDirection direction);
extern List *make_pathkeys_for_sortclauses(PlannerInfo *root,
List *sortclauses,
List *tlist);
diff --git a/src/test/regress/expected/tidscan.out b/src/test/regress/expected/tidscan.out
index 521ed1b..7eebe77 100644
--- a/src/test/regress/expected/tidscan.out
+++ b/src/test/regress/expected/tidscan.out
@@ -116,6 +116,39 @@ FETCH FIRST FROM c;
(1 row)
ROLLBACK;
+-- check that ordering on a tidscan doesn't require a sort
+EXPLAIN (COSTS OFF)
+SELECT ctid, * FROM tidscan WHERE ctid = ANY(ARRAY['(0,2)', '(0,1)', '(0,3)']::tid[]) ORDER BY ctid;
+ QUERY PLAN
+---------------------------------------------------------------
+ Tid Scan on tidscan
+ TID Cond: (ctid = ANY ('{"(0,2)","(0,1)","(0,3)"}'::tid[]))
+(2 rows)
+
+SELECT ctid, * FROM tidscan WHERE ctid = ANY(ARRAY['(0,2)', '(0,1)', '(0,3)']::tid[]) ORDER BY ctid;
+ ctid | id
+-------+----
+ (0,1) | 1
+ (0,2) | 2
+ (0,3) | 3
+(3 rows)
+
+EXPLAIN (COSTS OFF)
+SELECT ctid, * FROM tidscan WHERE ctid = ANY(ARRAY['(0,2)', '(0,1)', '(0,3)']::tid[]) ORDER BY ctid DESC;
+ QUERY PLAN
+---------------------------------------------------------------
+ Tid Scan Backward on tidscan
+ TID Cond: (ctid = ANY ('{"(0,2)","(0,1)","(0,3)"}'::tid[]))
+(2 rows)
+
+SELECT ctid, * FROM tidscan WHERE ctid = ANY(ARRAY['(0,2)', '(0,1)', '(0,3)']::tid[]) ORDER BY ctid DESC;
+ ctid | id
+-------+----
+ (0,3) | 3
+ (0,2) | 2
+ (0,1) | 1
+(3 rows)
+
-- tidscan via CURRENT OF
BEGIN;
DECLARE c CURSOR FOR SELECT ctid, * FROM tidscan;
diff --git a/src/test/regress/sql/tidscan.sql b/src/test/regress/sql/tidscan.sql
index a8472e0..5237f06 100644
--- a/src/test/regress/sql/tidscan.sql
+++ b/src/test/regress/sql/tidscan.sql
@@ -43,6 +43,15 @@ FETCH BACKWARD 1 FROM c;
FETCH FIRST FROM c;
ROLLBACK;
+-- check that ordering on a tidscan doesn't require a sort
+EXPLAIN (COSTS OFF)
+SELECT ctid, * FROM tidscan WHERE ctid = ANY(ARRAY['(0,2)', '(0,1)', '(0,3)']::tid[]) ORDER BY ctid;
+SELECT ctid, * FROM tidscan WHERE ctid = ANY(ARRAY['(0,2)', '(0,1)', '(0,3)']::tid[]) ORDER BY ctid;
+
+EXPLAIN (COSTS OFF)
+SELECT ctid, * FROM tidscan WHERE ctid = ANY(ARRAY['(0,2)', '(0,1)', '(0,3)']::tid[]) ORDER BY ctid DESC;
+SELECT ctid, * FROM tidscan WHERE ctid = ANY(ARRAY['(0,2)', '(0,1)', '(0,3)']::tid[]) ORDER BY ctid DESC;
+
-- tidscan via CURRENT OF
BEGIN;
DECLARE c CURSOR FOR SELECT ctid, * FROM tidscan;