326 lines
10 KiB
C++
326 lines
10 KiB
C++
/* -------------------------------------------------------------------------
|
|
*
|
|
* nodeForeignscan.cpp
|
|
* Routines to support scans of foreign tables
|
|
*
|
|
* Portions Copyright (c) 2020 Huawei Technologies Co.,Ltd.
|
|
* Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
*
|
|
* IDENTIFICATION
|
|
* src/gausskernel/runtime/executor/nodeForeignscan.cpp
|
|
*
|
|
* -------------------------------------------------------------------------
|
|
*
|
|
* INTERFACE ROUTINES
|
|
*
|
|
* ExecForeignScan scans a foreign table.
|
|
* ExecInitForeignScan creates and initializes state info.
|
|
* ExecReScanForeignScan rescans the foreign relation.
|
|
* ExecEndForeignScan releases any resources allocated.
|
|
*/
|
|
#include "postgres.h"
|
|
#include "knl/knl_variable.h"
|
|
|
|
#include "executor/executor.h"
|
|
#include "executor/node/nodeForeignscan.h"
|
|
#include "foreign/fdwapi.h"
|
|
#include "foreign/foreign.h"
|
|
#include "utils/rel.h"
|
|
#include "utils/rel_gs.h"
|
|
#include "utils/memutils.h"
|
|
#ifdef ENABLE_MOT
|
|
#include "storage/mot/jit_exec.h"
|
|
#endif
|
|
|
|
#ifdef PGXC
|
|
#include "utils/lsyscache.h"
|
|
#include "pgxc/pgxc.h"
|
|
#endif
|
|
|
|
#include "utils/knl_relcache.h"
|
|
|
|
static TupleTableSlot* ExecForeignScan(PlanState* state);
|
|
static TupleTableSlot* ForeignNext(ForeignScanState* node);
|
|
static bool ForeignRecheck(ForeignScanState* node, TupleTableSlot* slot);
|
|
|
|
/* ----------------------------------------------------------------
|
|
* ForeignNext
|
|
*
|
|
* This is a workhorse for ExecForeignScan
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
static TupleTableSlot* ForeignNext(ForeignScanState* node)
|
|
{
|
|
TupleTableSlot* slot = NULL;
|
|
ForeignScan* plan = (ForeignScan*)node->ss.ps.plan;
|
|
ExprContext* econtext = node->ss.ps.ps_ExprContext;
|
|
MemoryContext oldcontext;
|
|
|
|
/* Call the Iterate function in short-lived context */
|
|
oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
|
|
slot = node->fdwroutine->IterateForeignScan(node);
|
|
MemoryContextSwitchTo(oldcontext);
|
|
|
|
/*
|
|
* If any system columns are requested, we have to force the tuple into
|
|
* physical-tuple form to avoid "cannot extract system attribute from
|
|
* virtual tuple" errors later. We also insert a valid value for
|
|
* tableoid, which is the only actually-useful system column.
|
|
*/
|
|
if (plan->fsSystemCol && !TupIsNull(slot)) {
|
|
HeapTuple tup = ExecMaterializeSlot(slot);
|
|
|
|
if (!OidIsValid(tup->t_tableOid))
|
|
tup->t_tableOid = RelationGetRelid(node->ss.ss_currentRelation);
|
|
tup->t_bucketId = RelationGetBktid(node->ss.ss_currentRelation);
|
|
#ifdef PGXC
|
|
tup->t_xc_node_id = u_sess->pgxc_cxt.PGXCNodeIdentifier;
|
|
#endif
|
|
}
|
|
|
|
return slot;
|
|
}
|
|
|
|
/*
|
|
* ForeignRecheck -- access method routine to recheck a tuple in EvalPlanQual
|
|
*/
|
|
static bool ForeignRecheck(ForeignScanState* node, TupleTableSlot* slot)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* ExecForeignScan(node)
|
|
*
|
|
* Fetches the next tuple from the FDW, checks local quals, and
|
|
* returns it.
|
|
* We call the ExecScan() routine and pass it the appropriate
|
|
* access method functions.
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
static TupleTableSlot* ExecForeignScan(PlanState* state)
|
|
{
|
|
ForeignScanState* node = castNode(ForeignScanState, state);
|
|
return ExecScan((ScanState*)node, (ExecScanAccessMtd)ForeignNext, (ExecScanRecheckMtd)ForeignRecheck);
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* ExecInitForeignScan
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
ForeignScanState* ExecInitForeignScan(ForeignScan* node, EState* estate, int eflags)
|
|
{
|
|
ForeignScanState* scanstate = NULL;
|
|
Relation currentRelation = NULL;
|
|
FdwRoutine* fdwroutine = NULL;
|
|
Index scanrelid = node->scan.scanrelid;
|
|
int tlistvarno;
|
|
errno_t rc;
|
|
|
|
/* check for unsupported flags */
|
|
Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
|
|
|
|
/*
|
|
* create state structure
|
|
*/
|
|
scanstate = makeNode(ForeignScanState);
|
|
scanstate->ss.ps.plan = (Plan*)node;
|
|
scanstate->ss.ps.state = estate;
|
|
scanstate->ss.ps.ExecProcNode = ExecForeignScan;
|
|
|
|
/*
|
|
* Miscellaneous initialization
|
|
*
|
|
* create expression context for node
|
|
*/
|
|
#ifdef ENABLE_MOT
|
|
if ((estate->mot_jit_context == NULL) || IS_PGXC_COORDINATOR || !JitExec::IsMotCodegenEnabled()) {
|
|
#endif
|
|
ExecAssignExprContext(estate, &scanstate->ss.ps);
|
|
#ifdef ENABLE_MOT
|
|
}
|
|
#endif
|
|
|
|
scanstate->ss.ps.ps_TupFromTlist = false;
|
|
|
|
/*
|
|
* This function ExecInitForeignScan will be called by ExecInitVecForeignScan.
|
|
* If the node is VecForeignScan, do not need initialize here.
|
|
*/
|
|
if (!IsA(node, VecForeignScan)) {
|
|
/*
|
|
* 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);
|
|
}
|
|
/*
|
|
* tuple table initialization
|
|
*/
|
|
ExecInitResultTupleSlot(estate, &scanstate->ss.ps);
|
|
ExecInitScanTupleSlot(estate, &scanstate->ss);
|
|
|
|
/*
|
|
* open the base relation and acquire appropriate lock on it.
|
|
* or it is a foreign join, we should recreate resultdesc and project info
|
|
*/
|
|
if (scanrelid > 0) {
|
|
if (node->rel == NULL) {
|
|
currentRelation = ExecOpenScanRelation(estate, node->scan.scanrelid);
|
|
} else {
|
|
if (node->in_compute_pool == false) {
|
|
currentRelation = ExecOpenScanRelation(estate, node->scan.scanrelid);
|
|
} else {
|
|
currentRelation = get_rel_from_meta(node->rel);
|
|
scanstate->options = node->options;
|
|
}
|
|
}
|
|
scanstate->ss.ss_currentRelation = currentRelation;
|
|
|
|
/*
|
|
* Acquire function pointers from the FDW's handler, and init fdw_state.
|
|
*/
|
|
if (!node->rel) {
|
|
fdwroutine = GetFdwRoutineForRelation(currentRelation, true);
|
|
} else {
|
|
ForeignDataWrapper* fdw = NULL;
|
|
if (node->options->stype == T_OBS_SERVER || node->options->stype == T_HDFS_SERVER) {
|
|
fdw = GetForeignDataWrapperByName(HDFS_FDW, false);
|
|
} else {
|
|
fdw = GetForeignDataWrapperByName(DIST_FDW, false);
|
|
}
|
|
fdwroutine = GetFdwRoutine(fdw->fdwhandler);
|
|
|
|
/* Save the data for later reuse in LocalMyDBCacheMemCxt */
|
|
FdwRoutine* cfdwroutine = (FdwRoutine*)MemoryContextAlloc(LocalMyDBCacheMemCxt(), sizeof(FdwRoutine));
|
|
rc = memcpy_s(cfdwroutine, sizeof(FdwRoutine), fdwroutine, sizeof(FdwRoutine));
|
|
securec_check(rc, "\0", "\0");
|
|
currentRelation->rd_fdwroutine = cfdwroutine;
|
|
}
|
|
} else {
|
|
/* We can't use the relcache, so get fdwroutine the hard way */
|
|
fdwroutine = GetFdwRoutineByServerId(node->fs_server);
|
|
}
|
|
|
|
/*
|
|
* Determine the scan tuple type. If the FDW provided a targetlist
|
|
* describing the scan tuples, use that; else use base relation's rowtype.
|
|
*/
|
|
if (node->fdw_scan_tlist != NIL || currentRelation == NULL) {
|
|
TupleDesc scan_tupdesc;
|
|
|
|
scan_tupdesc = ExecTypeFromTL(node->fdw_scan_tlist, false);
|
|
ExecAssignScanType(&scanstate->ss, scan_tupdesc);
|
|
tlistvarno = INDEX_VAR;
|
|
} else {
|
|
TupleDesc scan_tupdesc;
|
|
|
|
/* don't trust FDWs to return tuples fulfilling NOT NULL constraints */
|
|
scan_tupdesc = CreateTupleDescCopy(RelationGetDescr(currentRelation));
|
|
ExecAssignScanType(&scanstate->ss, scan_tupdesc);
|
|
/* Node's targetlist will contain Vars with varno = scanrelid */
|
|
tlistvarno = scanrelid;
|
|
}
|
|
|
|
/*
|
|
* Initialize result tuple type and projection info.
|
|
*/
|
|
ExecAssignResultTypeFromTL(
|
|
&scanstate->ss.ps,
|
|
scanstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor->td_tam_ops);
|
|
ExecAssignScanProjectionInfoWithVarno(&scanstate->ss, tlistvarno);
|
|
|
|
scanstate->fdwroutine = fdwroutine;
|
|
scanstate->fdw_state = NULL;
|
|
|
|
/* Initialize any outer plan. */
|
|
if (outerPlan(node)) {
|
|
outerPlanState(scanstate) = ExecInitNode(outerPlan(node), estate, eflags);
|
|
}
|
|
|
|
#ifdef ENABLE_MOT
|
|
if ((estate->mot_jit_context == NULL) || IS_PGXC_COORDINATOR || !JitExec::IsMotCodegenEnabled()) {
|
|
#endif
|
|
scanstate->scanMcxt = AllocSetContextCreate(CurrentMemoryContext,
|
|
"Foreign Scan",
|
|
ALLOCSET_DEFAULT_MINSIZE,
|
|
ALLOCSET_DEFAULT_INITSIZE,
|
|
ALLOCSET_DEFAULT_MAXSIZE);
|
|
|
|
/*
|
|
* Tell the FDW to initiate the scan.
|
|
*/
|
|
fdwroutine->BeginForeignScan(scanstate, eflags);
|
|
#ifdef ENABLE_MOT
|
|
}
|
|
#endif
|
|
|
|
return scanstate;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* ExecEndForeignScan
|
|
*
|
|
* frees any storage allocated through C routines.
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
void ExecEndForeignScan(ForeignScanState* node)
|
|
{
|
|
/* Let the FDW shut down */
|
|
#ifdef ENABLE_MOT
|
|
if (node->fdw_state != NULL) {
|
|
#endif
|
|
node->fdwroutine->EndForeignScan(node);
|
|
#ifdef ENABLE_MOT
|
|
}
|
|
#endif
|
|
|
|
/* Shut down any outer plan. */
|
|
if (outerPlanState(node))
|
|
ExecEndNode(outerPlanState(node));
|
|
|
|
/* Free the exprcontext */
|
|
ExecFreeExprContext(&node->ss.ps);
|
|
|
|
/* clean out the tuple table */
|
|
(void)ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
|
|
(void)ExecClearTuple(node->ss.ss_ScanTupleSlot);
|
|
|
|
/* close the relation. */
|
|
ForeignScan* scan = (ForeignScan*)node->ss.ps.plan;
|
|
if (node->ss.ss_currentRelation != NULL) {
|
|
if (NULL == scan->rel) {
|
|
ExecCloseScanRelation(node->ss.ss_currentRelation);
|
|
} else {
|
|
if (false == scan->in_compute_pool) {
|
|
ExecCloseScanRelation(node->ss.ss_currentRelation);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* clear obs sk key */
|
|
if (node->options != NULL) {
|
|
char* obskey = getFTOptionValue(node->options->fOptions, OPTION_NAME_OBSKEY);
|
|
if (obskey != NULL) {
|
|
errno_t rc = EOK;
|
|
rc = memset_s(obskey, strlen(obskey), 0, strlen(obskey));
|
|
securec_check(rc, "\0", "\0");
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* ExecReScanForeignScan
|
|
*
|
|
* Rescans the relation.
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
void ExecReScanForeignScan(ForeignScanState* node)
|
|
{
|
|
node->fdwroutine->ReScanForeignScan(node);
|
|
|
|
ExecScanReScan(&node->ss);
|
|
}
|