blob: 7903febd019a7185904a77fb19d2181fcb8f3ffc [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*-------------------------------------------------------------------------
*
* nodeFunctionscan.c
* Support routines for scanning RangeFunctions (functions in rangetable).
*
* Portions Copyright (c) 2006-2008, Greenplum inc
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/nodeFunctionscan.c,v 1.40.2.1 2006/12/26 19:26:56 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/*
* INTERFACE ROUTINES
* ExecFunctionScan scans a function.
* ExecFunctionNext retrieve next tuple in sequential order.
* ExecInitFunctionScan creates and initializes a functionscan node.
* ExecEndFunctionScan releases any storage allocated.
* ExecFunctionReScan rescans the function
*/
#include "postgres.h"
#include "cdb/cdbvars.h"
#include "executor/nodeFunctionscan.h"
#include "funcapi.h"
#include "optimizer/var.h" /* CDB: contain_var_reference() */
#include "parser/parsetree.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "cdb/memquota.h"
#include "executor/spi.h"
static TupleTableSlot *FunctionNext(FunctionScanState *node);
static void ExecFunctionScanExplainEnd(PlanState *planstate, struct StringInfoData *buf);
/* ----------------------------------------------------------------
* Scan Support
* ----------------------------------------------------------------
*/
/* ----------------------------------------------------------------
* FunctionNext
*
* This is a workhorse for ExecFunctionScan
* ----------------------------------------------------------------
*/
static TupleTableSlot *
FunctionNext(FunctionScanState *node)
{
TupleTableSlot *slot;
EState *estate;
ScanDirection direction;
Tuplestorestate *tuplestorestate;
/*
* get information from the estate and scan state
*/
estate = node->ss.ps.state;
direction = estate->es_direction;
tuplestorestate = node->tuplestorestate;
/*
* If first time through, read all tuples from function and put them in a
* tuplestore. Subsequent calls just fetch tuples from tuplestore.
*/
if (tuplestorestate == NULL)
{
tuplestorestate = ExecMakeTableFunctionResult(
node->funcexpr,
node->ss.ps.ps_ExprContext,
node->tupdesc,
PlanStateOperatorMemKB( (PlanState *) node));
node->tuplestorestate = tuplestorestate;
/* CDB: Offer extra info for EXPLAIN ANALYZE. */
if (node->ss.ps.instrument)
{
/* Let the tuplestore share our Instrumentation object. */
tuplestore_set_instrument(tuplestorestate, node->ss.ps.instrument);
/* Request a callback at end of query. */
node->ss.ps.cdbexplainfun = ExecFunctionScanExplainEnd;
}
}
/*
* Get the next tuple from tuplestore. Return NULL if no more tuples.
*/
slot = node->ss.ss_ScanTupleSlot;
if (tuplestore_gettupleslot(tuplestorestate,
ScanDirectionIsForward(direction),
slot))
{
/* CDB: Label each row with a synthetic ctid for subquery dedup. */
if (node->cdb_want_ctid)
{
HeapTuple tuple = ExecFetchSlotHeapTuple(slot);
/* Increment 48-bit row count */
node->cdb_fake_ctid.ip_posid++;
if (node->cdb_fake_ctid.ip_posid == 0)
ItemPointerSetBlockNumber(&node->cdb_fake_ctid,
1 + ItemPointerGetBlockNumber(&node->cdb_fake_ctid));
tuple->t_self = node->cdb_fake_ctid;
}
}
if (!TupIsNull(slot))
{
Gpmon_M_Incr_Rows_Out(GpmonPktFromFuncScanState(node));
CheckSendPlanStateGpmonPkt(&node->ss.ps);
}
else if (!node->ss.ps.delayEagerFree)
{
ExecEagerFreeFunctionScan((FunctionScanState *)(&node->ss.ps));
}
return slot;
}
/* ----------------------------------------------------------------
* ExecFunctionScan(node)
*
* Scans the function sequentially and returns the next qualifying
* tuple.
* It calls the ExecScan() routine and passes it the access method
* which retrieves tuples sequentially.
*
*/
TupleTableSlot *
ExecFunctionScan(FunctionScanState *node)
{
/*
* use FunctionNext as access method
*/
return ExecScan(&node->ss, (ExecScanAccessMtd) FunctionNext);
}
/* ----------------------------------------------------------------
* ExecInitFunctionScan
* ----------------------------------------------------------------
*/
FunctionScanState *
ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags)
{
FunctionScanState *scanstate;
RangeTblEntry *rte;
Oid funcrettype;
TypeFuncClass functypclass;
TupleDesc tupdesc = NULL;
/*
* FunctionScan should not have any children.
*/
Assert(outerPlan(node) == NULL);
Assert(innerPlan(node) == NULL);
/*
* create new ScanState for node
*/
scanstate = makeNode(FunctionScanState);
scanstate->ss.ps.plan = (Plan *) node;
scanstate->ss.ps.state = estate;
/*
* Miscellaneous initialization
*
* create expression context for node
*/
ExecAssignExprContext(estate, &scanstate->ss.ps);
#define FUNCTIONSCAN_NSLOTS 2
/*
* tuple table initialization
*/
ExecInitResultTupleSlot(estate, &scanstate->ss.ps);
ExecInitScanTupleSlot(estate, &scanstate->ss);
/*
* initialize child expressions
*/
scanstate->ss.ps.targetlist = (List *)
ExecInitExpr((Expr *) node->scan.plan.targetlist,
(PlanState *) scanstate);
scanstate->ss.ps.qual = (List *)
ExecInitExpr((Expr *) node->scan.plan.qual,
(PlanState *) scanstate);
/* Check if targetlist or qual contains a var node referencing the ctid column */
scanstate->cdb_want_ctid = contain_ctid_var_reference(&node->scan);
ItemPointerSet(&scanstate->cdb_fake_ctid, 0, 0);
ItemPointerSet(&scanstate->cdb_mark_ctid, 0, 0);
/*
* get info about function
*/
rte = rt_fetch(node->scan.scanrelid, estate->es_range_table);
Assert(rte->rtekind == RTE_FUNCTION);
/*
* Now determine if the function returns a simple or composite type, and
* build an appropriate tupdesc.
*/
functypclass = get_expr_result_type(rte->funcexpr,
&funcrettype,
&tupdesc);
if (functypclass == TYPEFUNC_COMPOSITE)
{
/* Composite data type, e.g. a table's row type */
Assert(tupdesc);
/* Must copy it out of typcache for safety */
tupdesc = CreateTupleDescCopy(tupdesc);
}
else if (functypclass == TYPEFUNC_SCALAR)
{
/* Base data type, i.e. scalar */
char *attname = strVal(linitial(rte->eref->colnames));
tupdesc = CreateTemplateTupleDesc(1, false);
TupleDescInitEntry(tupdesc,
(AttrNumber) 1,
attname,
funcrettype,
-1,
0);
}
else if (functypclass == TYPEFUNC_RECORD)
{
tupdesc = BuildDescFromLists(rte->eref->colnames,
rte->funccoltypes,
rte->funccoltypmods);
}
else
{
/* crummy error message, but parser should have caught this */
elog(ERROR, "function in FROM has unsupported return type");
}
/*
* For RECORD results, make sure a typmod has been assigned. (The
* function should do this for itself, but let's cover things in case it
* doesn't.)
*/
BlessTupleDesc(tupdesc);
scanstate->tupdesc = tupdesc;
ExecAssignScanType(&scanstate->ss, tupdesc);
/*
* Other node-specific setup
*/
scanstate->tuplestorestate = NULL;
scanstate->funcexpr = ExecInitExpr((Expr *) rte->funcexpr,
(PlanState *) scanstate);
/*
* Initialize result tuple type and projection info.
*/
ExecAssignResultTypeFromTL(&scanstate->ss.ps);
ExecAssignScanProjectionInfo(&scanstate->ss);
initGpmonPktForFunctionScan((Plan *)node, &scanstate->ss.ps.gpmon_pkt, estate);
if (gp_resqueue_memory_policy != RESQUEUE_MEMORY_POLICY_NONE)
{
SPI_ReserveMemory(((Plan *)node)->operatorMemKB * 1024L);
}
return scanstate;
}
int
ExecCountSlotsFunctionScan(FunctionScan *node)
{
return ExecCountSlotsNode(outerPlan(node)) +
ExecCountSlotsNode(innerPlan(node)) +
FUNCTIONSCAN_NSLOTS;
}
/*
* ExecFunctionScanExplainEnd
* Called before ExecutorEnd to finish EXPLAIN ANALYZE reporting.
*
* The cleanup that ordinarily would occur during ExecutorEnd() needs to be
* done earlier in order to report statistics to EXPLAIN ANALYZE. Note that
* ExecEndFunctionScan() will be called for a second time during ExecutorEnd().
*/
void
ExecFunctionScanExplainEnd(PlanState *planstate, struct StringInfoData *buf __attribute__((unused)))
{
ExecEagerFreeFunctionScan((FunctionScanState *) planstate);
} /* ExecFunctionScanExplainEnd */
/* ----------------------------------------------------------------
* ExecEndFunctionScan
*
* frees any storage allocated through C routines.
* ----------------------------------------------------------------
*/
void
ExecEndFunctionScan(FunctionScanState *node)
{
/*
* Free the exprcontext
*/
ExecFreeExprContext(&node->ss.ps);
/*
* clean out the tuple table
*/
ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
ExecClearTuple(node->ss.ss_ScanTupleSlot);
ExecEagerFreeFunctionScan(node);
EndPlanStateGpmonPkt(&node->ss.ps);
}
/* ----------------------------------------------------------------
* ExecFunctionMarkPos
*
* Calls tuplestore to save the current position in the stored file.
* ----------------------------------------------------------------
*/
void
ExecFunctionMarkPos(FunctionScanState *node)
{
/*
* if we haven't materialized yet, just return.
*/
if (!node->tuplestorestate)
return;
node->cdb_mark_ctid = node->cdb_fake_ctid;
tuplestore_markpos(node->tuplestorestate);
}
/* ----------------------------------------------------------------
* ExecFunctionRestrPos
*
* Calls tuplestore to restore the last saved file position.
* ----------------------------------------------------------------
*/
void
ExecFunctionRestrPos(FunctionScanState *node)
{
/*
* if we haven't materialized yet, just return.
*/
if (!node->tuplestorestate)
return;
node->cdb_fake_ctid = node->cdb_mark_ctid;
tuplestore_restorepos(node->tuplestorestate);
}
/* ----------------------------------------------------------------
* ExecFunctionReScan
*
* Rescans the relation.
* ----------------------------------------------------------------
*/
void
ExecFunctionReScan(FunctionScanState *node, ExprContext *exprCtxt)
{
ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
/*node->ss.ps.ps_TupFromTlist = false;*/
/*
* If we haven't materialized yet, just return.
*/
if (!node->tuplestorestate)
return;
ItemPointerSet(&node->cdb_fake_ctid, 0, 0);
/*
* Here we have a choice whether to drop the tuplestore (and recompute the
* function outputs) or just rescan it. We must recompute if the
* expression contains parameters, else we rescan. XXX maybe we should
* recompute if the function is volatile?
*/
if (node->ss.ps.chgParam != NULL)
{
ExecEagerFreeFunctionScan(node);
}
else
{
tuplestore_rescan(node->tuplestorestate);
}
}
void
initGpmonPktForFunctionScan(Plan *planNode, gpmon_packet_t *gpmon_pkt, EState *estate)
{
Assert(planNode != NULL && gpmon_pkt != NULL && IsA(planNode, FunctionScan));
{
RangeTblEntry *rte = rt_fetch(((Scan *)planNode)->scanrelid, estate->es_range_table);
char *funcname = (rte->funcexpr && IsA(rte->funcexpr, FuncExpr)) ?
get_func_name(((FuncExpr *)rte->funcexpr)->funcid)
: rte->eref->aliasname;
Assert(GPMON_FUNCSCAN_TOTAL <= (int)GPMON_QEXEC_M_COUNT);
InitPlanNodeGpmonPkt(planNode, gpmon_pkt, estate, PMNT_FunctionScan,
(int64)planNode->plan_rows,
funcname);
if (funcname && funcname != rte->eref->aliasname)
pfree(funcname);
}
}
void
ExecEagerFreeFunctionScan(FunctionScanState *node)
{
if (node->tuplestorestate != NULL)
{
tuplestore_end(node->tuplestorestate);
}
node->tuplestorestate = NULL;
}