/*------------------------------------------------------------------------- * * nodeSort.c * Routines to handle sorting of relations. * * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $Header: /cvsroot/pgsql/src/backend/executor/nodeSort.c,v 1.43 2003/05/05 17:57:47 tgl Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" #include "executor/execdebug.h" #include "executor/nodeSort.h" #include "utils/tuplesort.h" /* ---------------------------------------------------------------- * ExtractSortKeys * * Extract the sorting key information from the plan node. * * Returns two palloc'd arrays, one of sort operator OIDs and * one of attribute numbers. * ---------------------------------------------------------------- */ static void ExtractSortKeys(Sort *sortnode, Oid **sortOperators, AttrNumber **attNums) { List *targetList; int keycount; Oid *sortOps; AttrNumber *attNos; List *tl; /* * get information from the node */ targetList = sortnode->plan.targetlist; keycount = sortnode->keycount; /* * first allocate space for results */ if (keycount <= 0) elog(ERROR, "ExtractSortKeys: keycount <= 0"); sortOps = (Oid *) palloc0(keycount * sizeof(Oid)); *sortOperators = sortOps; attNos = (AttrNumber *) palloc0(keycount * sizeof(AttrNumber)); *attNums = attNos; /* * extract info from the resdom nodes in the target list */ foreach(tl, targetList) { TargetEntry *target = (TargetEntry *) lfirst(tl); Resdom *resdom = target->resdom; Index reskey = resdom->reskey; if (reskey > 0) /* ignore TLEs that are not sort keys */ { Assert(reskey <= keycount); sortOps[reskey - 1] = resdom->reskeyop; attNos[reskey - 1] = resdom->resno; } } } /* ---------------------------------------------------------------- * ExecSort * * Sorts tuples from the outer subtree of the node using tuplesort, * which saves the results in a temporary file or memory. After the * initial call, returns a tuple from the file with each call. * * Conditions: * -- none. * * Initial States: * -- the outer child is prepared to return the first tuple. * ---------------------------------------------------------------- */ TupleTableSlot * ExecSort(SortState *node) { EState *estate; ScanDirection dir; Tuplesortstate *tuplesortstate; HeapTuple heapTuple; TupleTableSlot *slot; bool should_free; /* * get state info from node */ SO1_printf("ExecSort: %s\n", "entering routine"); estate = node->ss.ps.state; dir = estate->es_direction; tuplesortstate = (Tuplesortstate *) node->tuplesortstate; /* * If first time through, read all tuples from outer plan and pass * them to tuplesort.c. Subsequent calls just fetch tuples from * tuplesort. */ if (!node->sort_Done) { Sort *plannode = (Sort *) node->ss.ps.plan; PlanState *outerNode; TupleDesc tupDesc; Oid *sortOperators; AttrNumber *attNums; SO1_printf("ExecSort: %s\n", "sorting subplan"); /* * Want to scan subplan in the forward direction while creating * the sorted data. */ estate->es_direction = ForwardScanDirection; /* * Initialize tuplesort module. */ SO1_printf("ExecSort: %s\n", "calling tuplesort_begin"); outerNode = outerPlanState(node); tupDesc = ExecGetResultType(outerNode); ExtractSortKeys(plannode, &sortOperators, &attNums); tuplesortstate = tuplesort_begin_heap(tupDesc, plannode->keycount, sortOperators, attNums, true /* randomAccess */ ); node->tuplesortstate = (void *) tuplesortstate; pfree(sortOperators); pfree(attNums); /* * Scan the subplan and feed all the tuples to tuplesort. */ for (;;) { slot = ExecProcNode(outerNode); if (TupIsNull(slot)) break; tuplesort_puttuple(tuplesortstate, (void *) slot->val); } /* * Complete the sort. */ tuplesort_performsort(tuplesortstate); /* * restore to user specified direction */ estate->es_direction = dir; /* * finally set the sorted flag to true */ node->sort_Done = true; SO1_printf("ExecSort: %s\n", "sorting done"); } SO1_printf("ExecSort: %s\n", "retrieving tuple from tuplesort"); /* * Get the first or next tuple from tuplesort. Returns NULL if no more * tuples. */ heapTuple = tuplesort_getheaptuple(tuplesortstate, ScanDirectionIsForward(dir), &should_free); slot = node->ss.ps.ps_ResultTupleSlot; return ExecStoreTuple(heapTuple, slot, InvalidBuffer, should_free); } /* ---------------------------------------------------------------- * ExecInitSort * * Creates the run-time state information for the sort node * produced by the planner and initailizes its outer subtree. * ---------------------------------------------------------------- */ SortState * ExecInitSort(Sort *node, EState *estate) { SortState *sortstate; SO1_printf("ExecInitSort: %s\n", "initializing sort node"); /* * create state structure */ sortstate = makeNode(SortState); sortstate->ss.ps.plan = (Plan *) node; sortstate->ss.ps.state = estate; sortstate->sort_Done = false; sortstate->tuplesortstate = NULL; /* * Miscellaneous initialization * * Sort nodes don't initialize their ExprContexts because they never call * ExecQual or ExecProject. */ #define SORT_NSLOTS 2 /* * tuple table initialization * * sort nodes only return scan tuples from their sorted relation. */ ExecInitResultTupleSlot(estate, &sortstate->ss.ps); ExecInitScanTupleSlot(estate, &sortstate->ss); /* * initializes child nodes */ outerPlanState(sortstate) = ExecInitNode(outerPlan(node), estate); /* * initialize tuple type. no need to initialize projection info * because this node doesn't do projections. */ ExecAssignResultTypeFromOuterPlan(&sortstate->ss.ps); ExecAssignScanTypeFromOuterPlan(&sortstate->ss); sortstate->ss.ps.ps_ProjInfo = NULL; SO1_printf("ExecInitSort: %s\n", "sort node initialized"); return sortstate; } int ExecCountSlotsSort(Sort *node) { return ExecCountSlotsNode(outerPlan((Plan *) node)) + ExecCountSlotsNode(innerPlan((Plan *) node)) + SORT_NSLOTS; } /* ---------------------------------------------------------------- * ExecEndSort(node) * ---------------------------------------------------------------- */ void ExecEndSort(SortState *node) { SO1_printf("ExecEndSort: %s\n", "shutting down sort node"); /* * clean out the tuple table */ ExecClearTuple(node->ss.ss_ScanTupleSlot); /* * Release tuplesort resources */ if (node->tuplesortstate != NULL) tuplesort_end((Tuplesortstate *) node->tuplesortstate); node->tuplesortstate = NULL; /* * shut down the subplan */ ExecEndNode(outerPlanState(node)); SO1_printf("ExecEndSort: %s\n", "sort node shutdown"); } /* ---------------------------------------------------------------- * ExecSortMarkPos * * Calls tuplesort to save the current position in the sorted file. * ---------------------------------------------------------------- */ void ExecSortMarkPos(SortState *node) { /* * if we haven't sorted yet, just return */ if (!node->sort_Done) return; tuplesort_markpos((Tuplesortstate *) node->tuplesortstate); } /* ---------------------------------------------------------------- * ExecSortRestrPos * * Calls tuplesort to restore the last saved sort file position. * ---------------------------------------------------------------- */ void ExecSortRestrPos(SortState *node) { /* * if we haven't sorted yet, just return. */ if (!node->sort_Done) return; /* * restore the scan to the previously marked position */ tuplesort_restorepos((Tuplesortstate *) node->tuplesortstate); } void ExecReScanSort(SortState *node, ExprContext *exprCtxt) { /* * If we haven't sorted yet, just return. If outerplan' chgParam is * not NULL then it will be re-scanned by ExecProcNode, else - no * reason to re-scan it at all. */ if (!node->sort_Done) return; ExecClearTuple(node->ss.ps.ps_ResultTupleSlot); /* * If subnode is to be rescanned then we forget previous sort results; * we have to re-read the subplan and re-sort. * * Otherwise we can just rewind and rescan the sorted output. */ if (((PlanState *) node)->lefttree->chgParam != NULL) { node->sort_Done = false; tuplesort_end((Tuplesortstate *) node->tuplesortstate); node->tuplesortstate = NULL; } else tuplesort_rescan((Tuplesortstate *) node->tuplesortstate); }