From 6f9ff92cc0ff6a07d2fe38abe044286ee98d44a0 Mon Sep 17 00:00:00 2001 From: Bruce Momjian Date: Tue, 23 Nov 1999 20:07:06 +0000 Subject: [PATCH] Tid access method feature from Hiroshi Inoue, Inoue@tpf.co.jp --- src/backend/access/heap/heapam.c | 10 +- src/backend/commands/explain.c | 19 +- src/backend/executor/Makefile | 5 +- src/backend/executor/execAmi.c | 17 +- src/backend/executor/execProcnode.c | 18 +- src/backend/executor/execTuples.c | 9 +- src/backend/executor/nodeTidscan.c | 550 ++++++++++++++++++++++++ src/backend/nodes/copyfuncs.c | 58 ++- src/backend/nodes/equalfuncs.c | 42 +- src/backend/nodes/freefuncs.c | 54 ++- src/backend/nodes/outfuncs.c | 44 +- src/backend/nodes/print.c | 5 +- src/backend/nodes/readfuncs.c | 68 ++- src/backend/optimizer/path/Makefile | 5 +- src/backend/optimizer/path/allpaths.c | 9 +- src/backend/optimizer/path/costsize.c | 26 +- src/backend/optimizer/path/tidpath.c | 296 +++++++++++++ src/backend/optimizer/plan/createplan.c | 75 +++- src/backend/optimizer/plan/setrefs.c | 5 +- src/backend/optimizer/plan/subselect.c | 7 +- src/backend/optimizer/util/pathnode.c | 28 +- src/include/nodes/execnodes.h | 33 +- src/include/nodes/nodes.h | 5 +- src/include/nodes/plannodes.h | 16 +- src/include/nodes/relation.h | 9 +- src/include/optimizer/cost.h | 4 +- src/include/optimizer/pathnode.h | 3 +- src/include/optimizer/paths.h | 8 +- 28 files changed, 1396 insertions(+), 32 deletions(-) create mode 100644 src/backend/executor/nodeTidscan.c create mode 100644 src/backend/optimizer/path/tidpath.c diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c index a26216c026..3636993387 100644 --- a/src/backend/access/heap/heapam.c +++ b/src/backend/access/heap/heapam.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/access/heap/heapam.c,v 1.58 1999/11/07 23:07:52 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/access/heap/heapam.c,v 1.59 1999/11/23 20:06:47 momjian Exp $ * * * INTERFACE ROUTINES @@ -1062,7 +1062,13 @@ heap_fetch(Relation relation, * ---------------- */ - Assert(ItemIdIsUsed(lp)); + if (!ItemIdIsUsed(lp)) + { + ReleaseBuffer(buffer); + *userbuf = InvalidBuffer; + tuple->t_data = NULL; + return; + } tuple->t_data = (HeapTupleHeader) PageGetItem((Page) dp, lp); tuple->t_len = ItemIdGetLength(lp); diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index 99e4e45dd5..95856194ff 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -4,7 +4,7 @@ * * Copyright (c) 1994-5, Regents of the University of California * - * $Id: explain.c,v 1.49 1999/11/07 23:08:02 momjian Exp $ + * $Id: explain.c,v 1.50 1999/11/23 20:06:48 momjian Exp $ * */ @@ -196,6 +196,9 @@ explain_outNode(StringInfo str, Plan *plan, int indent, ExplainState *es) case T_Hash: pname = "Hash"; break; + case T_TidScan: + pname = "Tid Scan"; + break; default: pname = "???"; break; @@ -234,6 +237,20 @@ explain_outNode(StringInfo str, Plan *plan, int indent, ExplainState *es) appendStringInfo(str, stringStringInfo(rte->refname)); } break; + case T_TidScan: + if (((TidScan *) plan)->scan.scanrelid > 0) + { + RangeTblEntry *rte = nth(((TidScan *) plan)->scan.scanrelid - 1, es->rtable); + + appendStringInfo(str, " on "); + if (strcmp(rte->refname, rte->relname) != 0) + { + appendStringInfo(str, "%s ", + stringStringInfo(rte->relname)); + } + appendStringInfo(str, stringStringInfo(rte->refname)); + } + break; default: break; } diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile index 4f8c1341c8..ea0b20e9b4 100644 --- a/src/backend/executor/Makefile +++ b/src/backend/executor/Makefile @@ -4,7 +4,7 @@ # Makefile for executor # # IDENTIFICATION -# $Header: /cvsroot/pgsql/src/backend/executor/Makefile,v 1.8 1999/03/23 16:50:46 momjian Exp $ +# $Header: /cvsroot/pgsql/src/backend/executor/Makefile,v 1.9 1999/11/23 20:06:49 momjian Exp $ # #------------------------------------------------------------------------- @@ -18,7 +18,8 @@ OBJS = execAmi.o execFlatten.o execJunk.o execMain.o \ execUtils.o functions.o nodeAppend.o nodeAgg.o nodeHash.o \ nodeHashjoin.o nodeIndexscan.o nodeMaterial.o nodeMergejoin.o \ nodeNestloop.o nodeResult.o nodeSeqscan.o nodeSort.o \ - nodeUnique.o nodeGroup.o spi.o nodeSubplan.o + nodeUnique.o nodeGroup.o spi.o nodeSubplan.o \ + nodeTidscan.o all: SUBSYS.o diff --git a/src/backend/executor/execAmi.c b/src/backend/executor/execAmi.c index dc69953e21..0d7801bcd8 100644 --- a/src/backend/executor/execAmi.c +++ b/src/backend/executor/execAmi.c @@ -5,7 +5,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: execAmi.c,v 1.43 1999/11/04 08:00:57 inoue Exp $ + * $Id: execAmi.c,v 1.44 1999/11/23 20:06:50 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -40,6 +40,7 @@ #include "executor/nodeHash.h" #include "executor/nodeHashjoin.h" #include "executor/nodeIndexscan.h" +#include "executor/nodeTidscan.h" #include "executor/nodeMaterial.h" #include "executor/nodeMergejoin.h" #include "executor/nodeNestloop.h" @@ -217,6 +218,10 @@ ExecCloseR(Plan *node) state = &(((Agg *) node)->aggstate->csstate); break; + case T_TidScan: + state = ((TidScan *) node)->scan.scanstate; + break; + default: elog(DEBUG, "ExecCloseR: not a scan, material, or sort node!"); return; @@ -367,6 +372,10 @@ ExecReScan(Plan *node, ExprContext *exprCtxt, Plan *parent) ExecReScanAppend((Append *) node, exprCtxt, parent); break; + case T_TidScan: + ExecTidReScan((TidScan *) node, exprCtxt, parent); + break; + default: elog(ERROR, "ExecReScan: node type %u not supported", nodeTag(node)); return; @@ -413,7 +422,7 @@ ExecMarkPos(Plan *node) { switch (nodeTag(node)) { - case T_SeqScan: + case T_SeqScan: ExecSeqMarkPos((SeqScan *) node); break; @@ -425,6 +434,10 @@ ExecMarkPos(Plan *node) ExecSortMarkPos((Sort *) node); break; + case T_TidScan: + ExecTidMarkPos((TidScan *) node); + break; + default: elog(DEBUG, "ExecMarkPos: node type %u not supported", nodeTag(node)); break; diff --git a/src/backend/executor/execProcnode.c b/src/backend/executor/execProcnode.c index 1b6d2bd842..d4527be23b 100644 --- a/src/backend/executor/execProcnode.c +++ b/src/backend/executor/execProcnode.c @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/execProcnode.c,v 1.15 1999/07/16 04:58:46 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/execProcnode.c,v 1.16 1999/11/23 20:06:51 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -81,6 +81,7 @@ #include "executor/nodeHash.h" #include "executor/nodeHashjoin.h" #include "executor/nodeIndexscan.h" +#include "executor/nodeTidscan.h" #include "executor/nodeMaterial.h" #include "executor/nodeMergejoin.h" #include "executor/nodeNestloop.h" @@ -195,6 +196,10 @@ ExecInitNode(Plan *node, EState *estate, Plan *parent) result = ExecInitHashJoin((HashJoin *) node, estate, parent); break; + case T_TidScan: + result = ExecInitTidScan((TidScan *) node, estate, parent); + break; + default: elog(ERROR, "ExecInitNode: node %d unsupported", nodeTag(node)); result = FALSE; @@ -310,6 +315,10 @@ ExecProcNode(Plan *node, Plan *parent) result = ExecHashJoin((HashJoin *) node); break; + case T_TidScan: + result = ExecTidScan((TidScan *) node); + break; + default: elog(ERROR, "ExecProcNode: node %d unsupported", nodeTag(node)); result = NULL; @@ -381,6 +390,9 @@ ExecCountSlotsNode(Plan *node) case T_HashJoin: return ExecCountSlotsHashJoin((HashJoin *) node); + case T_TidScan: + return ExecCountSlotsTidScan((TidScan *) node); + default: elog(ERROR, "ExecCountSlotsNode: node not yet supported: %d", nodeTag(node)); @@ -497,6 +509,10 @@ ExecEndNode(Plan *node, Plan *parent) ExecEndHashJoin((HashJoin *) node); break; + case T_TidScan: + ExecEndTidScan((TidScan *) node); + break; + default: elog(ERROR, "ExecEndNode: node %d unsupported", nodeTag(node)); break; diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 53edd555ad..f1f6d15f1b 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -14,7 +14,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.31 1999/11/07 23:08:06 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.32 1999/11/23 20:06:51 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -771,6 +771,13 @@ NodeGetResultTupleSlot(Plan *node) } break; + case T_TidScan: + { + CommonScanState *scanstate = ((IndexScan *) node)->scan.scanstate; + slot = scanstate->cstate.cs_ResultTupleSlot; + } + break; + default: /* ---------------- * should never get here diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c new file mode 100644 index 0000000000..8d6481bf8a --- /dev/null +++ b/src/backend/executor/nodeTidscan.c @@ -0,0 +1,550 @@ +/*------------------------------------------------------------------------- + * + * nodeTidscan.c + * Routines to support direct tid scans of relations + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/executor/nodeTidscan.c,v 1.1 1999/11/23 20:06:51 momjian Exp $ + * + *------------------------------------------------------------------------- + */ +/* + * INTERFACE ROUTINES + * + * ExecTidScan scans a relation using tids + * ExecInitTidScan creates and initializes state info. + * ExecTidReScan rescans the tid relation. + * ExecEndTidScan releases all storage. + * ExecTidMarkPos marks scan position. + * ExecTidRestrPos restores scan position. + * + */ +#include "postgres.h" + +#include "executor/executor.h" +#include "executor/execdebug.h" +#include "executor/nodeTidscan.h" +#include "optimizer/clauses.h" /* for get_op, get_leftop, get_rightop */ +#include "access/heapam.h" +#include "parser/parsetree.h" + +static int TidListCreate(List *, ExprContext *, ItemPointer *); +static TupleTableSlot *TidNext(TidScan *node); + +static int +TidListCreate(List *evalList, ExprContext *econtext, ItemPointer *tidList) +{ + List *lst; + ItemPointer itemptr; + bool isNull; + int numTids = 0; + + foreach (lst, evalList) + { + itemptr = (ItemPointer)ExecEvalExpr(lfirst(lst), econtext, + &isNull, (bool *)0); + if (itemptr && ItemPointerIsValid(itemptr)) + { + tidList[numTids] = itemptr; + numTids++; + } + } + return numTids; +} + +/* ---------------------------------------------------------------- + * TidNext + * + * Retrieve a tuple from the TidScan node's currentRelation + * using the tids in the TidScanState information. + * + * ---------------------------------------------------------------- + */ +static TupleTableSlot * +TidNext(TidScan *node) +{ + EState *estate; + CommonScanState *scanstate; + TidScanState *tidstate; + ScanDirection direction; + Snapshot snapshot; + Relation heapRelation; + HeapTuple tuple; + TupleTableSlot *slot; + Buffer buffer = InvalidBuffer; + int numTids; + + bool bBackward; + int tidNumber; + ItemPointer *tidList, itemptr; + + /* ---------------- + * extract necessary information from tid scan node + * ---------------- + */ + estate = node->scan.plan.state; + direction = estate->es_direction; + snapshot = estate->es_snapshot; + scanstate = node->scan.scanstate; + tidstate = node->tidstate; + heapRelation = scanstate->css_currentRelation; + numTids = tidstate->tss_NumTids; + tidList = tidstate->tss_TidList; + slot = scanstate->css_ScanTupleSlot; + + /* + * Check if we are evaluating PlanQual for tuple of this relation. + * Additional checking is not good, but no other way for now. We could + * introduce new nodes for this case and handle TidScan --> NewNode + * switching in Init/ReScan plan... + */ + if (estate->es_evTuple != NULL && + estate->es_evTuple[node->scan.scanrelid - 1] != NULL) + { + int iptr, numQuals; + + ExecClearTuple(slot); + if (estate->es_evTupleNull[node->scan.scanrelid - 1]) + return slot; /* return empty slot */ + + slot->val = estate->es_evTuple[node->scan.scanrelid - 1]; + slot->ttc_shouldFree = false; + /* Flag for the next call that no more tuples */ + estate->es_evTupleNull[node->scan.scanrelid - 1] = true; + return (slot); + } + + tuple = &(tidstate->tss_htup); + + /* ---------------- + * ok, now that we have what we need, fetch an tid tuple. + * if scanning this tid succeeded then return the + * appropriate heap tuple.. else return NULL. + * ---------------- + */ + bBackward = ScanDirectionIsBackward(direction); + if (bBackward) + { + tidNumber = numTids - tidstate->tss_TidPtr - 1; + if (tidNumber < 0) + { + tidNumber = 0; + tidstate->tss_TidPtr = numTids - 1; + } + } + else + { + if ((tidNumber = tidstate->tss_TidPtr) < 0) + { + tidNumber = 0; + tidstate->tss_TidPtr = 0; + } + } + while (tidNumber < numTids) + { + bool slot_is_valid = false; + + itemptr = tidList[tidstate->tss_TidPtr]; + tuple->t_data = NULL; + if (itemptr) + { + tuple->t_self = *(itemptr); + heap_fetch(heapRelation, snapshot, tuple, &buffer); + } + if (tuple->t_data != NULL) + { + bool prev_matches = false; + int prev_tid; + + /* ---------------- + * store the scanned tuple in the scan tuple slot of + * the scan state. Eventually we will only do this and not + * return a tuple. Note: we pass 'false' because tuples + * returned by amgetnext are pointers onto disk pages and + * were not created with palloc() and so should not be pfree()'d. + * ---------------- + */ + ExecStoreTuple(tuple, /* tuple to store */ + slot, /* slot to store in */ + buffer, /* buffer associated with tuple */ + false); /* don't pfree */ + + /* + * At this point we have an extra pin on the buffer, + * because ExecStoreTuple incremented the pin count. + * Drop our local pin. + */ + ReleaseBuffer(buffer); + /* + * We must check to see if the current tuple would have + * been matched by an earlier tid, so we don't double + * report it. We do this by passing the tuple through + * ExecQual and look for failure with all previous + * qualifications. + */ + for (prev_tid = 0; prev_tid < tidstate->tss_TidPtr; + prev_tid++) + { + if (ItemPointerEquals(tidList[prev_tid], &tuple->t_self)) + { + prev_matches = true; + break; + } + } + if (!prev_matches) + slot_is_valid = true; + else + ExecClearTuple(slot); + } + else if (BufferIsValid(buffer)) + ReleaseBuffer(buffer); + tidNumber++; + if (bBackward) + tidstate->tss_TidPtr--; + else + tidstate->tss_TidPtr++; + if (slot_is_valid) + return slot; + } + /* ---------------- + * if we get here it means the tid scan failed so we + * are at the end of the scan.. + * ---------------- + */ + return ExecClearTuple(slot); +} + +/* ---------------------------------------------------------------- + * ExecTidScan(node) + * + * Scans the relation using tids and returns + * the next qualifying tuple in the direction specified. + * It calls ExecScan() and passes it the access methods which returns + * the next tuple using the tids. + * + * Conditions: + * -- the "cursor" maintained by the AMI is positioned at the tuple + * returned previously. + * + * Initial States: + * -- the relation indicated is opened for scanning so that the + * "cursor" is positioned before the first qualifying tuple. + * -- tidPtr points to the first tid. + * -- state variable ruleFlag = nil. + * ---------------------------------------------------------------- + */ +TupleTableSlot * +ExecTidScan(TidScan *node) +{ + /* ---------------- + * use TidNext as access method + * ---------------- + */ + return ExecScan(&node->scan, TidNext); +} + +/* ---------------------------------------------------------------- + * ExecTidReScan(node) + * ---------------------------------------------------------------- + */ +void +ExecTidReScan(TidScan *node, ExprContext *exprCtxt, Plan *parent) +{ + EState *estate; + TidScanState *tidstate; + Plan *outerPlan; + ItemPointer *tidList; + + tidstate = node->tidstate; + estate = node->scan.plan.state; + tidstate->tss_TidPtr = -1; + tidList = tidstate->tss_TidList; + + if ((outerPlan = outerPlan((Plan *) node)) != NULL) + { + /* we are scanning a subplan */ + outerPlan = outerPlan((Plan *) node); + ExecReScan(outerPlan, exprCtxt, parent); + } + else + /* otherwise, we are scanning a relation */ + { + /* If this is re-scanning of PlanQual ... */ + if (estate->es_evTuple != NULL && + estate->es_evTuple[node->scan.scanrelid - 1] != NULL) + { + estate->es_evTupleNull[node->scan.scanrelid - 1] = false; + return; + } + + /* it's possible in subselects */ + if (exprCtxt == NULL) + exprCtxt = node->scan.scanstate->cstate.cs_ExprContext; + + node->scan.scanstate->cstate.cs_ExprContext->ecxt_outertuple = exprCtxt->ecxt_outertuple; + tidstate->tss_NumTids = TidListCreate(node->tideval, exprCtxt, tidList); + } + + /* ---------------- + * perhaps return something meaningful + * ---------------- + */ + return; +} + +/* ---------------------------------------------------------------- + * ExecEndTidScan + * + * Releases any storage allocated through C routines. + * Returns nothing. + * ---------------------------------------------------------------- + */ +void +ExecEndTidScan(TidScan *node) +{ + CommonScanState *scanstate; + TidScanState *tidstate; + + scanstate = node->scan.scanstate; + tidstate = node->tidstate; + if (tidstate && tidstate->tss_TidList) + pfree(tidstate->tss_TidList); + + /* ---------------- + * extract information from the node + * ---------------- + */ + + /* ---------------- + * Free the projection info and the scan attribute info + * + * Note: we don't ExecFreeResultType(scanstate) + * because the rule manager depends on the tupType + * returned by ExecMain(). So for now, this + * is freed at end-transaction time. -cim 6/2/91 + * ---------------- + */ + ExecFreeProjectionInfo(&scanstate->cstate); + + /* ---------------- + * close the heap and tid relations + * ---------------- + */ + ExecCloseR((Plan *) node); + + /* ---------------- + * clear out tuple table slots + * ---------------- + */ + ExecClearTuple(scanstate->cstate.cs_ResultTupleSlot); + ExecClearTuple(scanstate->css_ScanTupleSlot); +/* ExecClearTuple(scanstate->css_RawTupleSlot); */ +} + +/* ---------------------------------------------------------------- + * ExecTidMarkPos + * + * Marks scan position by marking the current tid. + * Returns nothing. + * ---------------------------------------------------------------- + */ +void +ExecTidMarkPos(TidScan *node) +{ + TidScanState *tidstate; + + tidstate = node->tidstate; + tidstate->tss_MarkTidPtr = tidstate->tss_TidPtr; +} + +/* ---------------------------------------------------------------- + * ExecTidRestrPos + * + * Restores scan position by restoring the current tid. + * Returns nothing. + * + * XXX Assumes previously marked scan position belongs to current tid + * ---------------------------------------------------------------- + */ +void +ExecTidRestrPos(TidScan *node) +{ + TidScanState *tidstate; + + tidstate = node->tidstate; + tidstate->tss_TidPtr = tidstate->tss_MarkTidPtr; +} + +/* ---------------------------------------------------------------- + * ExecInitTidScan + * + * Initializes the tid scan's state information, creates + * scan keys, and opens the base and tid relations. + * + * Parameters: + * node: TidNode node produced by the planner. + * estate: the execution state initialized in InitPlan. + * ---------------------------------------------------------------- + */ +bool +ExecInitTidScan(TidScan *node, EState *estate, Plan *parent) +{ + TidScanState *tidstate; + CommonScanState *scanstate; + ItemPointer *tidList; + int numTids; + int tidPtr; + List *rangeTable; + RangeTblEntry *rtentry; + Oid relid; + Oid reloid; + + Relation currentRelation; + int baseid; + + List *execParam = NULL; + + /* ---------------- + * assign execution state to node + * ---------------- + */ + node->scan.plan.state = estate; + + /* -------------------------------- + * Part 1) initialize scan state + * + * create new CommonScanState for node + * -------------------------------- + */ + scanstate = makeNode(CommonScanState); +/* + scanstate->ss_ProcOuterFlag = false; + scanstate->ss_OldRelId = 0; +*/ + + node->scan.scanstate = scanstate; + + /* ---------------- + * assign node's base_id .. we don't use AssignNodeBaseid() because + * the increment is done later on after we assign the tid scan's + * scanstate. see below. + * ---------------- + */ + baseid = estate->es_BaseId; +/* scanstate->csstate.cstate.bnode.base_id = baseid; */ + scanstate->cstate.cs_base_id = baseid; + + /* ---------------- + * create expression context for node + * ---------------- + */ + ExecAssignExprContext(estate, &scanstate->cstate); + +#define TIDSCAN_NSLOTS 3 + /* ---------------- + * tuple table initialization + * ---------------- + */ + ExecInitResultTupleSlot(estate, &scanstate->cstate); + ExecInitScanTupleSlot(estate, scanstate); +/* ExecInitRawTupleSlot(estate, scanstate); */ + + /* ---------------- + * initialize projection info. result type comes from scan desc + * below.. + * ---------------- + */ + ExecAssignProjectionInfo((Plan *) node, &scanstate->cstate); + + /* -------------------------------- + * Part 2) initialize tid scan state + * + * create new TidScanState for node + * -------------------------------- + */ + tidstate = makeNode(TidScanState); + node->tidstate = tidstate; + + /* ---------------- + * assign base id to tid scan state also + * ---------------- + */ + tidstate->cstate.cs_base_id = baseid; + baseid++; + estate->es_BaseId = baseid; + + /* ---------------- + * get the tid node information + * ---------------- + */ + tidList = (ItemPointer *)palloc(length(node->tideval) * sizeof(ItemPointer)); + numTids = 0; + if (!node->needRescan) + numTids = TidListCreate(node->tideval, scanstate->cstate.cs_ExprContext, tidList); + tidPtr = -1; + + CXT1_printf("ExecInitTidScan: context is %d\n", CurrentMemoryContext); + + tidstate->tss_NumTids = numTids; + tidstate->tss_TidPtr = tidPtr; + tidstate->tss_TidList = tidList; + + /* ---------------- + * get the range table and direction information + * from the execution state (these are needed to + * open the relations). + * ---------------- + */ + rangeTable = estate->es_range_table; + + /* ---------------- + * open the base relation + * ---------------- + */ + relid = node->scan.scanrelid; + rtentry = rt_fetch(relid, rangeTable); + reloid = rtentry->relid; + + currentRelation = heap_open(reloid, AccessShareLock); + if (currentRelation == NULL) + elog(ERROR, "ExecInitTidScan heap_open failed."); + scanstate->css_currentRelation = currentRelation; + scanstate->css_currentScanDesc = 0; + + /* ---------------- + * get the scan type from the relation descriptor. + * ---------------- + */ + ExecAssignScanType(scanstate, RelationGetDescr(currentRelation)); + ExecAssignResultTypeFromTL((Plan *) node, &scanstate->cstate); + + /* ---------------- + * tid scans don't have subtrees.. + * ---------------- + */ +/* scanstate->ss_ProcOuterFlag = false; */ + + tidstate->cstate.cs_TupFromTlist = false; + + /* + * if there are some PARAM_EXEC in skankeys then force tid rescan on + * first scan. + */ + ((Plan *) node)->chgParam = execParam; + + /* ---------------- + * all done. + * ---------------- + */ + return TRUE; +} + +int +ExecCountSlotsTidScan(TidScan *node) +{ + return ExecCountSlotsNode(outerPlan((Plan *) node)) + + ExecCountSlotsNode(innerPlan((Plan *) node)) + TIDSCAN_NSLOTS; +} diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 5f23a954b1..1b2726f822 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.96 1999/11/15 03:28:06 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.97 1999/11/23 20:06:52 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -253,6 +253,32 @@ _copyIndexScan(IndexScan *from) return newnode; } +/* ---------------- + * _copyTidScan + * ---------------- + */ +static TidScan * +_copyTidScan(TidScan *from) +{ + TidScan *newnode = makeNode(TidScan); + + /* ---------------- + * copy node superclass fields + * ---------------- + */ + CopyPlanFields((Plan *) from, (Plan *) newnode); + CopyScanFields((Scan *) from, (Scan *) newnode); + /* ---------------- + * copy remainder of node + * ---------------- + */ + newnode->needRescan = from->needRescan; + Node_Copy(from, newnode, tideval); + + return newnode; +} + + /* ---------------- * CopyJoinFields * @@ -1058,6 +1084,30 @@ _copyIndexPath(IndexPath *from) return newnode; } +/* ---------------- + * _copyTidPath + * ---------------- + */ +static TidPath * +_copyTidPath(TidPath *from) +{ + TidPath *newnode = makeNode(TidPath); + + /* ---------------- + * copy the node superclass fields + * ---------------- + */ + CopyPathFields((Path *) from, (Path *) newnode); + + /* ---------------- + * copy remainder of node + * ---------------- + */ + Node_Copy(from, newnode, tideval); + newnode->unjoined_relids = listCopy(from->unjoined_relids); + + return newnode; +} /* ---------------- * CopyJoinPathFields * @@ -1437,6 +1487,9 @@ copyObject(void *from) case T_IndexScan: retval = _copyIndexScan(from); break; + case T_TidScan: + retval = _copyTidScan(from); + break; case T_Join: retval = _copyJoin(from); break; @@ -1535,6 +1588,9 @@ copyObject(void *from) case T_IndexPath: retval = _copyIndexPath(from); break; + case T_TidPath: + retval = _copyTidPath(from); + break; case T_NestPath: retval = _copyNestPath(from); break; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index fccb9d3160..b35b271275 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.51 1999/11/15 03:28:06 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.52 1999/11/23 20:06:52 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -330,6 +330,18 @@ _equalIndexPath(IndexPath *a, IndexPath *b) return true; } +static bool +_equalTidPath(TidPath *a, TidPath *b) +{ + if (!_equalPath((Path *) a, (Path *) b)) + return false; + if (!equal(a->tideval, b->tideval)) + return false; + if (!equali(a->unjoined_relids, b->unjoined_relids)) + return false; + return true; +} + static bool _equalJoinPath(JoinPath *a, JoinPath *b) { @@ -403,6 +415,28 @@ _equalIndexScan(IndexScan *a, IndexScan *b) return true; } +static bool +_equalTidScan(TidScan *a, TidScan *b) +{ + Assert(IsA(a, TidScan)); + Assert(IsA(b, TidScan)); + + /* + * if(a->scan.plan.cost != b->scan.plan.cost) return(false); + */ + + if (a->needRescan != b->needRescan) + return false; + + if (!equal(a->tideval, b->tideval)) + return false; + + if (a->scan.scanrelid != b->scan.scanrelid) + return false; + + return true; +} + static bool _equalSubPlan(SubPlan *a, SubPlan *b) { @@ -756,6 +790,9 @@ equal(void *a, void *b) case T_IndexPath: retval = _equalIndexPath(a, b); break; + case T_TidPath: + retval = _equalTidPath(a, b); + break; case T_NestPath: retval = _equalNestPath(a, b); break; @@ -768,6 +805,9 @@ equal(void *a, void *b) case T_IndexScan: retval = _equalIndexScan(a, b); break; + case T_TidScan: + retval = _equalTidScan(a, b); + break; case T_SubPlan: retval = _equalSubPlan(a, b); break; diff --git a/src/backend/nodes/freefuncs.c b/src/backend/nodes/freefuncs.c index db09b6700b..66368afd68 100644 --- a/src/backend/nodes/freefuncs.c +++ b/src/backend/nodes/freefuncs.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/Attic/freefuncs.c,v 1.27 1999/11/15 03:28:07 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/Attic/freefuncs.c,v 1.28 1999/11/23 20:06:53 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -183,6 +183,29 @@ _freeIndexScan(IndexScan *node) pfree(node); } +/* ---------------- + * _freeTidScan + * ---------------- + */ +static void +_freeTidScan(TidScan *node) +{ + /* ---------------- + * free node superclass fields + * ---------------- + */ + FreePlanFields((Plan *) node); + FreeScanFields((Scan *) node); + + /* ---------------- + * free remainder of node + * ---------------- + */ + freeObject(node->tideval); + + pfree(node); +} + /* ---------------- * FreeJoinFields * @@ -781,6 +804,29 @@ _freeIndexPath(IndexPath *node) pfree(node); } +/* ---------------- + * _freeTidPath + * ---------------- + */ +static void +_freeTidPath(TidPath *node) +{ + /* ---------------- + * free the node superclass fields + * ---------------- + */ + FreePathFields((Path *) node); + + /* ---------------- + * free remainder of node + * ---------------- + */ + freeObject(node->tideval); + freeList(node->unjoined_relids); + + pfree(node); +} + /* ---------------- * FreeJoinPathFields * @@ -1079,6 +1125,9 @@ freeObject(void *node) case T_IndexScan: _freeIndexScan(node); break; + case T_TidScan: + _freeTidScan(node); + break; case T_Join: _freeJoin(node); break; @@ -1177,6 +1226,9 @@ freeObject(void *node) case T_IndexPath: _freeIndexPath(node); break; + case T_TidPath: + _freeTidPath(node); + break; case T_NestPath: _freeNestPath(node); break; diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 06c2520d27..789faad772 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -5,7 +5,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: outfuncs.c,v 1.97 1999/10/07 04:23:04 tgl Exp $ + * $Id: outfuncs.c,v 1.98 1999/11/23 20:06:53 momjian Exp $ * * NOTES * Every (plan) node in POSTGRES has an associated "out" routine which @@ -451,6 +451,23 @@ _outIndexScan(StringInfo str, IndexScan *node) appendStringInfo(str, " :indxorderdir %d ", node->indxorderdir); } +/* + * TidScan is a subclass of Scan + */ +static void +_outTidScan(StringInfo str, TidScan *node) +{ + appendStringInfo(str, " TIDSCAN "); + _outPlanInfo(str, (Plan *) node); + + appendStringInfo(str, " :scanrelid %u ", node->scan.scanrelid); + appendStringInfo(str, " :needrescan %d ", node->needRescan); + + appendStringInfo(str, " :tideval "); + _outNode(str, node->tideval); + +} + /* * Noname is a subclass of Plan */ @@ -914,6 +931,25 @@ _outIndexPath(StringInfo str, IndexPath *node) _outIntList(str, node->joinrelids); } +/* + * TidPath is a subclass of Path. + */ +static void +_outTidPath(StringInfo str, TidPath *node) +{ + appendStringInfo(str, + " TIDPATH :pathtype %d :cost %f :pathkeys ", + node->path.pathtype, + node->path.path_cost); + _outNode(str, node->path.pathkeys); + + appendStringInfo(str, " :tideval "); + _outNode(str, node->tideval); + + appendStringInfo(str, " :un joined_relids "); + _outIntList(str, node->unjoined_relids); +} + /* * NestPath is a subclass of Path */ @@ -1357,6 +1393,9 @@ _outNode(StringInfo str, void *obj) case T_IndexScan: _outIndexScan(str, obj); break; + case T_TidScan: + _outTidScan(str, obj); + break; case T_Noname: _outNoname(str, obj); break; @@ -1435,6 +1474,9 @@ _outNode(StringInfo str, void *obj) case T_IndexPath: _outIndexPath(str, obj); break; + case T_TidPath: + _outTidPath(str, obj); + break; case T_NestPath: _outNestPath(str, obj); break; diff --git a/src/backend/nodes/print.c b/src/backend/nodes/print.c index 9cb04181cd..3241816cd3 100644 --- a/src/backend/nodes/print.c +++ b/src/backend/nodes/print.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/print.c,v 1.32 1999/08/16 02:17:43 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/print.c,v 1.33 1999/11/23 20:06:53 momjian Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -338,6 +338,9 @@ plannode_type(Plan *p) case T_Group: return "GROUP"; break; + case T_TidScan: + return "TIDSCAN"; + break; default: return "UNKNOWN"; break; diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index ef62e5a285..99be5199fa 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.74 1999/10/07 04:23:04 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.75 1999/11/23 20:06:53 momjian Exp $ * * NOTES * Most of the read functions for plan nodes are tested. (In fact, they @@ -541,6 +541,33 @@ _readIndexScan() return local_node; } +/* ---------------- + * _readTidScan + * + * TidScan is a subclass of Scan + * ---------------- + */ +static TidScan * +_readTidScan() +{ + TidScan *local_node; + char *token; + int length; + + local_node = makeNode(TidScan); + + _getScan((Scan *) local_node); + + token = lsptok(NULL, &length); /* eat :needrescan */ + token = lsptok(NULL, &length); /* get needrescan */ + local_node->needRescan = atoi(token); + + token = lsptok(NULL, &length); /* eat :tideval */ + local_node->tideval = nodeRead(true); /* now read it */ + + return local_node; +} + /* ---------------- * _readNoname * @@ -1476,6 +1503,41 @@ _readIndexPath() return local_node; } +/* ---------------- + * _readTidPath + * + * TidPath is a subclass of Path. + * ---------------- + */ +static TidPath * +_readTidPath() +{ + TidPath *local_node; + char *token; + int length; + + local_node = makeNode(TidPath); + + token = lsptok(NULL, &length); /* get :pathtype */ + token = lsptok(NULL, &length); /* now read it */ + local_node->path.pathtype = atol(token); + + token = lsptok(NULL, &length); /* get :cost */ + token = lsptok(NULL, &length); /* now read it */ + local_node->path.path_cost = (Cost) atof(token); + + token = lsptok(NULL, &length); /* get :pathkeys */ + local_node->path.pathkeys = nodeRead(true); /* now read it */ + + token = lsptok(NULL, &length); /* get :tideval */ + local_node->tideval = nodeRead(true); /* now read it */ + + token = lsptok(NULL, &length); /* get :unjoined_relids */ + local_node->unjoined_relids = toIntList(nodeRead(true)); + + return local_node; +} + /* ---------------- * _readNestPath * @@ -1801,6 +1863,8 @@ parsePlanString(void) return_value = _readSeqScan(); else if (!strncmp(token, "INDEXSCAN", length)) return_value = _readIndexScan(); + else if (!strncmp(token, "TIDSCAN", length)) + return_value = _readTidScan(); else if (!strncmp(token, "NONAME", length)) return_value = _readNoname(); else if (!strncmp(token, "SORT", length)) @@ -1845,6 +1909,8 @@ parsePlanString(void) return_value = _readPath(); else if (!strncmp(token, "INDEXPATH", length)) return_value = _readIndexPath(); + else if (!strncmp(token, "TIDPATH", length)) + return_value = _readTidPath(); else if (!strncmp(token, "NESTPATH", length)) return_value = _readNestPath(); else if (!strncmp(token, "MERGEPATH", length)) diff --git a/src/backend/optimizer/path/Makefile b/src/backend/optimizer/path/Makefile index cdc401b831..5e903ce666 100644 --- a/src/backend/optimizer/path/Makefile +++ b/src/backend/optimizer/path/Makefile @@ -4,7 +4,7 @@ # Makefile for optimizer/path # # IDENTIFICATION -# $Header: /cvsroot/pgsql/src/backend/optimizer/path/Makefile,v 1.9 1999/08/16 02:17:50 tgl Exp $ +# $Header: /cvsroot/pgsql/src/backend/optimizer/path/Makefile,v 1.10 1999/11/23 20:06:54 momjian Exp $ # #------------------------------------------------------------------------- @@ -14,7 +14,8 @@ include ../../../Makefile.global CFLAGS += -I../.. OBJS = allpaths.o clausesel.o costsize.o indxpath.o \ - joinpath.o joinrels.o orindxpath.o pathkeys.o prune.o + joinpath.o joinrels.o orindxpath.o pathkeys.o prune.o \ + tidpath.o all: SUBSYS.o diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index 23c759bd6e..3cc8466f77 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.53 1999/08/16 02:17:50 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.54 1999/11/23 20:06:54 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -108,9 +108,14 @@ set_base_rel_pathlist(Query *root, List *rels) List *sequential_scan_list; List *rel_index_scan_list; List *or_index_scan_list; + List *tidscan_pathlist; sequential_scan_list = lcons(create_seqscan_path(rel), NIL); - + /* Tid Scan Pathlist add */ + tidscan_pathlist = create_tidscan_paths(root, rel); + if (tidscan_pathlist) + sequential_scan_list = nconc(sequential_scan_list, + tidscan_pathlist); rel_index_scan_list = create_index_paths(root, rel, indices, diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c index fcf462b83e..99ee42cf8c 100644 --- a/src/backend/optimizer/path/costsize.c +++ b/src/backend/optimizer/path/costsize.c @@ -18,7 +18,7 @@ * Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.45 1999/08/22 20:14:41 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.46 1999/11/23 20:06:54 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -59,6 +59,7 @@ bool _enable_sort_ = true; bool _enable_nestloop_ = true; bool _enable_mergejoin_ = true; bool _enable_hashjoin_ = true; +bool _enable_tidscan_ = true; Cost _cpu_page_weight_ = _CPU_PAGE_WEIGHT_; Cost _cpu_index_page_weight_ = _CPU_INDEX_PAGE_WEIGHT_; @@ -174,6 +175,29 @@ cost_index(Oid indexid, return temp; } +/* + * cost_tidscan + * Determines and returns the cost of scanning a relation using tid-s. + * + * disk = number of tids + * cpu = *CPU-PAGE-WEIGHT* * number_of_tids + * + * Returns a flonum. + * + */ +Cost +cost_tidscan(List *tideval) +{ + Cost temp = 0; + + if (!_enable_tidscan_) + temp += _disable_cost_; + + temp += (1.0 + _cpu_page_weight_) * length(tideval); + + return temp; +} + /* * cost_sort * Determines and returns the cost of sorting a relation by considering diff --git a/src/backend/optimizer/path/tidpath.c b/src/backend/optimizer/path/tidpath.c new file mode 100644 index 0000000000..9e618f7a87 --- /dev/null +++ b/src/backend/optimizer/path/tidpath.c @@ -0,0 +1,296 @@ +/*------------------------------------------------------------------------- + * + * tidpath.c + * Routines to determine which tids are usable for scanning a + * given relation, and create TidPaths accordingly. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/tidpath.c,v 1.1 1999/11/23 20:06:55 momjian Exp $ + * + *------------------------------------------------------------------------- + */ +#include + +#include "postgres.h" + +#include "access/heapam.h" +#include "catalog/catname.h" +#include "catalog/pg_amop.h" +#include "catalog/pg_operator.h" +#include "executor/executor.h" +#include "nodes/makefuncs.h" +#include "nodes/nodeFuncs.h" +#include "optimizer/clauses.h" +#include "optimizer/cost.h" +#include "optimizer/pathnode.h" +#include "optimizer/paths.h" +#include "optimizer/plancat.h" +#include "optimizer/restrictinfo.h" +#include "parser/parse_coerce.h" +#include "parser/parse_expr.h" +#include "parser/parse_oper.h" +#include "parser/parsetree.h" +#include "utils/lsyscache.h" + +static List *create_tidscan_joinpaths(RelOptInfo *); +static List *TidqualFromRestrictinfo(List *relids, List * restrictinfo); +static bool isEvaluable(int varno, Node *node); +static Node *TidequalClause(int varno, Expr *node); +static List *TidqualFromExpr(int varno, Expr *expr); + +static +bool isEvaluable(int varno, Node *node) +{ + List *lst; + Expr *expr; + + if (IsA(node, Const)) return true; + if (IsA(node, Param)) return true; + if (IsA(node, Var)) + { + Var *var = (Var *)node; + + if (var->varno == varno) + return false; + return true; + } + if (!is_funcclause(node)) return false; + expr = (Expr *)node; + foreach (lst, expr->args) + { + if (!isEvaluable(varno, lfirst(lst))) + return false; + } + + return true; +} + +/* + * The 2nd parameter should be an opclause + * Extract the right node if the opclause is CTID= .... + * or the left node if the opclause is ....=CTID + */ +static +Node *TidequalClause(int varno, Expr *node) +{ + Node *rnode = 0, *arg1, *arg2, *arg; + Oper *oper; + Var *var; + Const *aconst; + Param *param; + Expr *expr; + + if (!node->oper) return rnode; + if (!node->args) return rnode; + if (length(node->args) != 2) return rnode; + oper = (Oper *) node->oper; + if (oper->opno != TIDEqualOperator) + return rnode; + arg1 = lfirst(node->args); + arg2 = lsecond(node->args); + + arg = (Node *)0; + if (IsA(arg1, Var)) + { + var = (Var *) arg1; + if (var->varno == varno && + var->varattno == SelfItemPointerAttributeNumber && + var->vartype == TIDOID) + arg = arg2; + else if (var->varnoold == varno && + var->varoattno == SelfItemPointerAttributeNumber && + var->vartype == TIDOID) + arg = arg2; + } + if ((!arg) && IsA(arg2, Var)) + { + var = (Var *) arg2; + if (var->varno == varno && + var->varattno == SelfItemPointerAttributeNumber && + var->vartype == TIDOID) + arg = arg1; + } + if (!arg) + return rnode; + switch (nodeTag(arg)) + { + case T_Const: + aconst = (Const *) arg; + if (aconst->consttype != TIDOID) + return rnode; + if (aconst->constbyval) + return rnode; + rnode = arg; + break; + case T_Param: + param = (Param *) arg; + if (param->paramtype != TIDOID) + return rnode; + rnode = arg; + break; + case T_Var: + var = (Var *) arg; + if (var->varno == varno || + var->vartype != TIDOID) + return rnode; + rnode = arg; + break; + case T_Expr: + expr = (Expr *) arg; + if (expr->typeOid != TIDOID) return rnode; + if (expr->opType != FUNC_EXPR) return rnode; + if (isEvaluable(varno, (Node *)expr)) + rnode = arg; + break; + default: + break; + } + return rnode; +} + +/* + * Extract the list of CTID values from a specified expr node. + * When the expr node is an or_clause,we try to extract CTID + * values from all member nodes. However we would discard them + * all if we couldn't extract CTID values from a member node. + * When the expr node is an and_clause,we return the list of + * CTID values if we could extract the CTID values from a member + * node. + */ +static +List *TidqualFromExpr(int varno, Expr *expr) +{ + List *rlst = NIL, *lst, *frtn; + Node *node = (Node *) expr, *rnode; + + if (is_opclause(node)) + { + rnode = TidequalClause(varno, expr); + if (rnode) + { + rlst = lcons(rnode, rlst); + } + } + else if (and_clause(node)) + { + foreach (lst, expr->args) + { + node = lfirst(lst); + if (!IsA(node, Expr)) + continue; + rlst = TidqualFromExpr(varno, (Expr *)node); + if (rlst) + break; + } + } + else if (or_clause(node)) + { + foreach (lst, expr->args) + { + node = lfirst(lst); + if (IsA(node, Expr) && + (frtn = TidqualFromExpr(varno, (Expr *)node)) ) + { + rlst = nconc(rlst, frtn); + } + else + { + if (rlst) + freeList(rlst); + rlst = NIL; + break; + } + } + } + return rlst; +} + +static +List *TidqualFromRestrictinfo(List *relids, List * restrictinfo) +{ + List *lst, *rlst = NIL; + int varno; + Node *node; + Expr *expr; + + if (length(relids)>1) return NIL; + varno = (int)lfirst(relids); + foreach (lst, restrictinfo) + { + node = lfirst(lst); + if (!IsA(node, RestrictInfo)) continue; + expr = ((RestrictInfo *)node)->clause; + rlst = TidqualFromExpr(varno, expr); + if (rlst) + { + break; + } + } + return rlst; +} + +/* + * create_tidscan_joinpaths + * Creates a path corresponding to a tid_direct scan, returning the + * pathnode. + * + */ +List * +create_tidscan_joinpaths(RelOptInfo *rel) +{ + List *rlst = NIL, *lst; + TidPath *pathnode = (TidPath *)0; + List *restinfo, *tideval; + + foreach (lst, rel->joininfo) + { + JoinInfo *joininfo = (JoinInfo *)lfirst(lst); + restinfo = joininfo->jinfo_restrictinfo; + tideval = TidqualFromRestrictinfo(rel->relids, restinfo); + if (tideval && length(tideval) == 1) + { + pathnode = makeNode(TidPath); + + pathnode->path.pathtype = T_TidScan; + pathnode->path.parent = rel; + pathnode->path.path_cost = 0.0; + pathnode->path.pathkeys = NIL; + + pathnode->path.path_cost = cost_tidscan(tideval); + pathnode->tideval = tideval; + /* + pathnode->tideval = copyObject(tideval); + freeList(tideval); + */ + pathnode->unjoined_relids = joininfo->unjoined_relids; + rlst = lappend(rlst, pathnode); + } + } + rel->innerjoin = nconc(rel->innerjoin, rlst); + return rlst; +} + +/* + * create_tidscan_paths + * Creates a path corresponding to a tid direct scan, returning the + * pathnode List. + * + */ +List * +create_tidscan_paths(Query *root, RelOptInfo *rel) +{ + List *rlst = NIL; + TidPath *pathnode = (TidPath *)0; + List *tideval = TidqualFromRestrictinfo(rel->relids, rel->restrictinfo); + + if (tideval) + pathnode = create_tidscan_path(rel, tideval); + if (pathnode) + rlst = lcons(pathnode, rlst); + create_tidscan_joinpaths(rel); + + return rlst; +} diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index b9742d6fef..6fda28aa74 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.76 1999/08/22 23:56:44 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.77 1999/11/23 20:06:57 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -37,6 +37,8 @@ static SeqScan *create_seqscan_node(Path *best_path, List *tlist, List *scan_clauses); static IndexScan *create_indexscan_node(IndexPath *best_path, List *tlist, List *scan_clauses); +static TidScan *create_tidscan_node(TidPath *best_path, List *tlist, + List *scan_clauses); static NestLoop *create_nestloop_node(NestPath *best_path, List *tlist, List *clauses, Plan *outer_node, List *outer_tlist, Plan *inner_node, List *inner_tlist); @@ -53,6 +55,8 @@ static Node *fix_indxqual_operand(Node *node, IndexPath *index_path, Form_pg_index index); static IndexScan *make_indexscan(List *qptlist, List *qpqual, Index scanrelid, List *indxid, List *indxqual, List *indxqualorig); +static TidScan *make_tidscan(List *qptlist, List *qpqual, Index scanrelid, + List *tideval); static NestLoop *make_nestloop(List *qptlist, List *qpqual, Plan *lefttree, Plan *righttree); static HashJoin *make_hashjoin(List *tlist, List *qpqual, @@ -101,6 +105,7 @@ create_plan(Path *best_path) { case T_IndexScan: case T_SeqScan: + case T_TidScan: plan_node = (Plan *) create_scan_node(best_path, tlist); break; case T_HashJoin: @@ -168,6 +173,12 @@ create_scan_node(Path *best_path, List *tlist) scan_clauses); break; + case T_TidScan: + node = (Scan *) create_tidscan_node((TidPath *) best_path, + tlist, + scan_clauses); + break; + default: elog(ERROR, "create_scan_node: unknown node type", best_path->pathtype); @@ -399,6 +410,62 @@ create_indexscan_node(IndexPath *best_path, return scan_node; } +static TidScan * +make_tidscan(List *qptlist, + List *qpqual, + Index scanrelid, + List *tideval) +{ + TidScan *node = makeNode(TidScan); + Plan *plan = &node->scan.plan; + + plan->cost = 0; + plan->plan_size = 0; + plan->plan_width = 0; + plan->state = (EState *) NULL; + plan->targetlist = qptlist; + plan->qual = qpqual; + plan->lefttree = NULL; + plan->righttree = NULL; + node->scan.scanrelid = scanrelid; + node->tideval = copyObject(tideval); + node->needRescan = false; + node->scan.scanstate = (CommonScanState *) NULL; + + return node; +} + +/* + * create_tidscan_node + * Returns a tidscan node for the base relation scanned by 'best_path' + * with restriction clauses 'scan_clauses' and targetlist 'tlist'. + */ +static TidScan * +create_tidscan_node(TidPath *best_path, List *tlist, List *scan_clauses) +{ + TidScan *scan_node = (TidScan *) NULL; + Index scan_relid = -1; + List *temp; + + temp = best_path->path.parent->relids; + if (temp == NULL) + elog(ERROR, "scanrelid is empty"); + else if (length(temp) != 1) + return scan_node; + else + scan_relid = (Index) lfirsti(temp); + scan_node = make_tidscan(tlist, + scan_clauses, + scan_relid, + best_path->tideval); + + if (best_path->unjoined_relids) + scan_node->needRescan = true; + scan_node->scan.plan.cost = best_path->path.path_cost; + + return scan_node; +} + /***************************************************************************** * * JOIN METHODS @@ -487,6 +554,12 @@ create_nestloop_node(NestPath *best_path, innerrel); } } + else if (IsA(inner_node, TidScan)) + { + List *inner_tideval = ((TidScan *) inner_node)->tideval; + TidScan *innerscan = (TidScan *) inner_node; + ((TidScan *) inner_node)->tideval = join_references(inner_tideval, outer_tlist, inner_tlist, innerscan->scan.scanrelid); + } else if (IsA_Join(inner_node)) { /* diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index 051c2ea3e3..ec8c67b7a7 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.58 1999/10/30 23:07:55 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.59 1999/11/23 20:06:57 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -125,6 +125,9 @@ set_plan_references(Plan *plan) set_plan_references((Plan *) lfirst(pl)); } break; + case T_TidScan: + /* nothing special */ + break; default: elog(ERROR, "set_plan_references: unknown plan type %d", nodeTag(plan)); diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index 5290c96d5d..d04839afc2 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -6,7 +6,7 @@ * Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.25 1999/11/15 02:00:08 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.26 1999/11/23 20:06:57 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -534,6 +534,11 @@ SS_finalize_plan(Plan *plan) &results); break; + case T_TidScan: + finalize_primnode((Node *) ((TidScan *) plan)->tideval, + &results); + break; + case T_Agg: case T_SeqScan: case T_NestLoop: diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c index f3b99f8892..0f9bf1b8bb 100644 --- a/src/backend/optimizer/util/pathnode.c +++ b/src/backend/optimizer/util/pathnode.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.54 1999/08/16 02:17:58 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.55 1999/11/23 20:07:00 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -318,6 +318,32 @@ create_index_path(Query *root, return pathnode; } +/* + * create_tidscan_path + * Creates a path corresponding to a tid_direct scan, returning the + * pathnode. + * + */ +TidPath * +create_tidscan_path(RelOptInfo *rel, List *tideval) +{ + TidPath *pathnode = makeNode(TidPath); + + pathnode->path.pathtype = T_TidScan; + pathnode->path.parent = rel; + pathnode->path.path_cost = 0.0; + pathnode->path.pathkeys = NIL; + + pathnode->path.path_cost = cost_tidscan(tideval); + /* divide selectivity for each clause to get an equal selectivity + * as IndexScan does OK ? + */ + pathnode->tideval = copyObject(tideval); + pathnode->unjoined_relids = NIL; + + return pathnode; +} + /* * create_nestloop_path * Creates a pathnode corresponding to a nestloop join between two diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 44aa8b8ace..47e6e40b7c 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -6,7 +6,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: execnodes.h,v 1.37 1999/10/17 22:15:07 tgl Exp $ + * $Id: execnodes.h,v 1.38 1999/11/23 20:07:02 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -414,6 +414,37 @@ typedef struct IndexScanState HeapTupleData iss_htup; } IndexScanState; +/* ---------------- + * TidScanState information + * + *| tid scans don't use CommonScanState because + *| the underlying AM abstractions for heap scans and + *| tid scans are too different.. It would be nice + *| if the current abstraction was more useful but ... -cim 10/15/89 + * + * TidPtr current tid in use + * NumTids number of tids in this scan + * tidList evaluated item pointers + * + * CommonState information + * + * OuterTupleSlot pointer to slot containing current "outer" tuple + * ResultTupleSlot pointer to slot in tuple table for projected tuple + * ExprContext node's current expression context + * ProjInfo info this node uses to form tuple projections + * NumScanAttributes size of ScanAttributes array + * ScanAttributes attribute numbers of interest in this tuple + * ---------------- + */ +typedef struct TidScanState +{ + CommonState cstate; /* its first field is NodeTag */ + int tss_NumTids; + int tss_TidPtr; + int tss_MarkTidPtr; + ItemPointer *tss_TidList; + HeapTupleData tss_htup; +} TidScanState; /* ---------------------------------------------------------------- * Join State Information diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 09f60466f4..4e830d7527 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -6,7 +6,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: nodes.h,v 1.55 1999/10/15 01:49:47 momjian Exp $ + * $Id: nodes.h,v 1.56 1999/11/23 20:07:02 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -47,6 +47,7 @@ typedef enum NodeTag T_Choose, T_Group, T_SubPlan, + T_TidScan, /*--------------------- * TAGS FOR PRIMITIVE NODES (primnodes.h) @@ -80,6 +81,7 @@ typedef enum NodeTag T_RestrictInfo, T_JoinInfo, T_Stream, + T_TidPath, /*--------------------- * TAGS FOR EXECUTOR NODES (execnodes.h) @@ -110,6 +112,7 @@ typedef enum NodeTag T_SortState, T_UniqueState, T_HashState, + T_TidScanState, /*--------------------- * TAGS FOR MEMORY NODES (memnodes.h) diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index 00e7025917..9cd06e2d93 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -6,7 +6,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: plannodes.h,v 1.33 1999/11/15 03:28:06 tgl Exp $ + * $Id: plannodes.h,v 1.34 1999/11/23 20:07:02 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -179,7 +179,19 @@ typedef struct IndexScan IndexScanState *indxstate; } IndexScan; -/* +/* ---------------- + * tid scan node + * ---------------- + */ +typedef struct TidScan +{ + Scan scan; + bool needRescan; + List *tideval; + TidScanState *tidstate; +} TidScan; + +/* * ========== * Join nodes * ========== diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h index 91fa85dd25..0f143017b2 100644 --- a/src/include/nodes/relation.h +++ b/src/include/nodes/relation.h @@ -6,7 +6,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: relation.h,v 1.38 1999/08/16 02:17:40 tgl Exp $ + * $Id: relation.h,v 1.39 1999/11/23 20:07:02 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -193,6 +193,13 @@ typedef struct IndexPath Relids joinrelids; /* other rels mentioned in indexqual */ } IndexPath; +typedef struct TidPath +{ + Path path; + List *tideval; + Relids unjoined_relids; /* some rels not yet part of my Path */ +} TidPath; + /* * All join-type paths share these fields. */ diff --git a/src/include/optimizer/cost.h b/src/include/optimizer/cost.h index 6791435a4d..abde39b237 100644 --- a/src/include/optimizer/cost.h +++ b/src/include/optimizer/cost.h @@ -6,7 +6,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: cost.h,v 1.23 1999/08/06 04:00:13 tgl Exp $ + * $Id: cost.h,v 1.24 1999/11/23 20:07:05 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -31,11 +31,13 @@ extern bool _enable_sort_; extern bool _enable_nestloop_; extern bool _enable_mergejoin_; extern bool _enable_hashjoin_; +extern bool _enable_tidscan_; extern Cost cost_seqscan(int relid, int relpages, int reltuples); extern Cost cost_index(Oid indexid, int expected_indexpages, Cost selec, int relpages, int reltuples, int indexpages, int indextuples, bool is_injoin); +extern Cost cost_tidscan(List *evallist); extern Cost cost_sort(List *pathkeys, int tuples, int width); extern Cost cost_nestloop(Cost outercost, Cost innercost, int outertuples, int innertuples, int outerpages, bool is_indexjoin); diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h index 65ece63c57..2aca95e605 100644 --- a/src/include/optimizer/pathnode.h +++ b/src/include/optimizer/pathnode.h @@ -6,7 +6,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: pathnode.h,v 1.21 1999/08/16 02:17:45 tgl Exp $ + * $Id: pathnode.h,v 1.22 1999/11/23 20:07:06 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -27,6 +27,7 @@ extern Path *create_seqscan_path(RelOptInfo *rel); extern IndexPath *create_index_path(Query *root, RelOptInfo *rel, RelOptInfo *index, List *restriction_clauses); +extern TidPath *create_tidscan_path(RelOptInfo *rel, List *tideval); extern NestPath *create_nestloop_path(RelOptInfo *joinrel, RelOptInfo *outer_rel, Path *outer_path, Path *inner_path, diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h index 58da94368c..92ce9f9719 100644 --- a/src/include/optimizer/paths.h +++ b/src/include/optimizer/paths.h @@ -7,7 +7,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: paths.h,v 1.35 1999/08/21 03:49:15 tgl Exp $ + * $Id: paths.h,v 1.36 1999/11/23 20:07:06 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -30,6 +30,12 @@ extern List *create_index_paths(Query *root, RelOptInfo *rel, List *indices, List *joininfo_list); extern List *expand_indexqual_conditions(List *indexquals); +/* + * tidpath.h + * routines to generate tid paths + */ +extern List *create_tidscan_paths(Query *root, RelOptInfo *rel); + /* * joinpath.c * routines to create join paths