From 49e93bda7ddc25d23b3b2d66bb65c9b3db256ee0 Mon Sep 17 00:00:00 2001 From: Jacob Champion Date: Wed, 7 Jun 2023 16:42:17 -0700 Subject: [PATCH 2/3] WIP: create ScanKeys from derived null tests ...for pushdown to the TableAM layer. During the scan fixup, we have to also adjust the new splan->keyexprs list. Otherwise our Vars won't reference the correct RTEs. TODO: - quals made redundant with the ScanKeys need to be removed - costing --- src/backend/commands/explain.c | 12 +++++- src/backend/executor/nodeSeqscan.c | 53 +++++++++++++++++++++++- src/backend/optimizer/plan/createplan.c | 55 ++++++++++++++++++++++++- src/backend/optimizer/plan/setrefs.c | 3 ++ src/include/nodes/execnodes.h | 2 + src/include/nodes/plannodes.h | 1 + 6 files changed, 122 insertions(+), 4 deletions(-) diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index 8570b14f62..622d0615bc 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -1821,12 +1821,20 @@ ExplainNode(PlanState *planstate, List *ancestors, if (es->analyze) show_tidbitmap_info((BitmapHeapScanState *) planstate, es); break; + case T_SeqScan: + if (((SeqScan *) plan)->keyexprs) + show_scan_qual(((SeqScan *) plan)->keyexprs, "Scan Cond", + planstate, ancestors, es); + show_scan_qual(plan->qual, "Filter", planstate, ancestors, es); + if (plan->qual) + show_instrumentation_count("Rows Removed by Filter", 1, + planstate, es); + break; case T_SampleScan: show_tablesample(((SampleScan *) plan)->tablesample, planstate, ancestors, es); - /* fall through to print additional fields the same as SeqScan */ + /* fall through to print additional fields the same as basic scans */ /* FALLTHROUGH */ - case T_SeqScan: case T_ValuesScan: case T_CteScan: case T_NamedTuplestoreScan: diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c index 4da0f28f7b..67ff9d1482 100644 --- a/src/backend/executor/nodeSeqscan.c +++ b/src/backend/executor/nodeSeqscan.c @@ -28,6 +28,7 @@ #include "postgres.h" #include "access/relscan.h" +#include "access/skey.h" #include "access/tableam.h" #include "executor/execdebug.h" #include "executor/nodeSeqscan.h" @@ -70,7 +71,7 @@ SeqNext(SeqScanState *node) */ scandesc = table_beginscan(node->ss.ss_currentRelation, estate->es_snapshot, - 0, NULL); + node->nkeys, node->scankeys); node->ss.ss_currentScanDesc = scandesc; } @@ -91,6 +92,8 @@ SeqRecheck(SeqScanState *node, TupleTableSlot *slot) /* * Note that unlike IndexScan, SeqScan never use keys in heap_beginscan * (and this is very bad) - so, here we do not check are keys ok or not. + * + * TODO: so I guess this isn't true anymore. */ return true; } @@ -114,6 +117,48 @@ ExecSeqScan(PlanState *pstate) (ExecScanRecheckMtd) SeqRecheck); } +static void +create_scankeys(SeqScanState *scanstate, List *keyexprs) +{ + struct ScanKeyData *keys; + int nkeys; + ListCell *l; + + nkeys = list_length(keyexprs); + keys = palloc(sizeof(struct ScanKeyData) * nkeys); + + foreach(l, keyexprs) + { + NullTest *n; + Var *var; + int typeflag; + int i = foreach_current_index(l); + + /* + * create_seqscan_plan() only puts NullTests of Vars into the SeqScan's + * key clauses, so that's all we handle here. + */ + n = lfirst_node(NullTest, l); + var = (Var *) n->arg; + Assert(IsA(var, Var)); + + typeflag = (n->nulltesttype == IS_NULL) ? SK_SEARCHNULL + : SK_SEARCHNOTNULL; + + Assert(i < nkeys); + ScanKeyEntryInitialize(&keys[i], + SK_ISNULL | typeflag, + var->varattno, + InvalidStrategy, + InvalidOid, + InvalidOid, + InvalidOid, + (Datum) 0); + } + + scanstate->nkeys = nkeys; + scanstate->scankeys = keys; +} /* ---------------------------------------------------------------- * ExecInitSeqScan @@ -171,6 +216,12 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags) scanstate->ss.ps.qual = ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate); + /* + * Populate scankeys, if necessary. + */ + if (node->keyexprs) + create_scankeys(scanstate, node->keyexprs); + return scanstate; } diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index af48109058..c7cfe59ea9 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -177,7 +177,8 @@ static void copy_generic_path_info(Plan *dest, Path *src); static void copy_plan_costsize(Plan *dest, Plan *src); static void label_sort_with_costsize(PlannerInfo *root, Sort *plan, double limit_tuples); -static SeqScan *make_seqscan(List *qptlist, List *qpqual, Index scanrelid); +static SeqScan *make_seqscan(List *qptlist, List *qpqual, List *scankeys, + Index scanrelid); static SampleScan *make_samplescan(List *qptlist, List *qpqual, Index scanrelid, TableSampleClause *tsc); static IndexScan *make_indexscan(List *qptlist, List *qpqual, Index scanrelid, @@ -2885,6 +2886,47 @@ create_limit_plan(PlannerInfo *root, LimitPath *best_path, int flags) *****************************************************************************/ +/* + * reconstruct_null_tests + * Creates a list of NullTests, one for each Var in varset, which is a + * multibitmapset of varno/varattno. Whole-row Vars are omitted. + */ +static List * +reconstruct_null_tests(List *tests, NullTestType type, List *varset) +{ + ListCell *lc; + + foreach(lc, varset) + { + Bitmapset *varattnos = lfirst_node(Bitmapset, lc); + int i = -1; + + while ((i = bms_next_member(varattnos, i)) >= 0) + { + AttrNumber varattno = i + FirstLowInvalidHeapAttributeNumber; + Var *var; + NullTest *n; + + if (varattno == 0) + continue; /* skip whole-row vars */ + + var = makeNode(Var); + var->varno = foreach_current_index(lc); + var->varattno = varattno; + + n = makeNode(NullTest); + n->arg = (Expr *) var; + n->nulltesttype = type; + n->location = -1; + + tests = lappend(tests, n); + } + } + + return tests; +} + + /* * create_seqscan_plan * Returns a seqscan plan for the base relation scanned by 'best_path' @@ -2896,6 +2938,7 @@ create_seqscan_plan(PlannerInfo *root, Path *best_path, { SeqScan *scan_plan; Index scan_relid = best_path->parent->relid; + List *keyexprs = NIL; /* it should be a base rel... */ Assert(scan_relid > 0); @@ -2914,8 +2957,16 @@ create_seqscan_plan(PlannerInfo *root, Path *best_path, replace_nestloop_params(root, (Node *) scan_clauses); } + keyexprs = + reconstruct_null_tests(keyexprs, IS_NULL, + find_forced_null_vars((Node *) scan_clauses)); + keyexprs = + reconstruct_null_tests(keyexprs, IS_NOT_NULL, + find_nonnullable_vars((Node *) scan_clauses)); + scan_plan = make_seqscan(tlist, scan_clauses, + keyexprs, scan_relid); copy_generic_path_info(&scan_plan->scan.plan, best_path); @@ -5460,6 +5511,7 @@ bitmap_subplan_mark_shared(Plan *plan) static SeqScan * make_seqscan(List *qptlist, List *qpqual, + List *keyexprs, Index scanrelid) { SeqScan *node = makeNode(SeqScan); @@ -5470,6 +5522,7 @@ make_seqscan(List *qptlist, plan->lefttree = NULL; plan->righttree = NULL; node->scan.scanrelid = scanrelid; + node->keyexprs = keyexprs; return node; } diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index 97fa561e4e..2b58b6c2b3 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -632,6 +632,9 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset) splan->scan.plan.qual = fix_scan_list(root, splan->scan.plan.qual, rtoffset, NUM_EXEC_QUAL(plan)); + splan->keyexprs = + fix_scan_list(root, splan->keyexprs, + rtoffset, NUM_EXEC_QUAL(plan)); } break; case T_SampleScan: diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index cb714f4a19..d015a3a127 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1485,6 +1485,8 @@ typedef struct SeqScanState { ScanState ss; /* its first field is NodeTag */ Size pscan_len; /* size of parallel heap scan descriptor */ + struct ScanKeyData *scankeys; + int nkeys; } SeqScanState; /* ---------------- diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index 1b787fe031..b6dbf64d1e 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -394,6 +394,7 @@ typedef struct Scan typedef struct SeqScan { Scan scan; + List *keyexprs; /* expressions to push down as ScanKeys */ } SeqScan; /* ---------------- -- 2.25.1