* This file and its contents are licensed under the Apache License 2.0.
* Please see the included NOTICE for copyright information and
* LICENSE-APACHE for a copy of the license.
*/
#include <postgres.h>
#include <parser/parsetree.h>
#include <nodes/execnodes.h>
#include <nodes/makefuncs.h>
#include <nodes/nodeFuncs.h>
#include <nodes/plannodes.h>
#include <executor/node/nodeModifyTable.h>
#include <utils/rel.h>
#include <utils/lsyscache.h>
#include <foreign/foreign.h>
#include <catalog/pg_type.h>
#include "hypertable_insert.h"
#include "chunk_dispatch_state.h"
#include "chunk_dispatch_plan.h"
#include "hypertable_cache.h"
static PlanState *
get_chunk_dispatch_state(ModifyTableState *mtstate, PlanState *substate)
{
switch (nodeTag(substate))
{
case T_ExtensiblePlanState:
{
ExtensiblePlanState *csstate = castNode(ExtensiblePlanState, substate);
if (strcmp(csstate->methods->ExtensibleName, CHUNK_DISPATCH_STATE_NAME) == 0)
return substate;
break;
}
case T_ResultState:
return get_chunk_dispatch_state(mtstate, castNode(ResultState, substate)->ps.lefttree);
default:
break;
}
return NULL;
}
* HypertableInsert (with corresponding executor node) is a plan node that
* implements INSERTs for hypertables. It is mostly a wrapper around the
* ModifyTable plan node that simply calls the wrapped ModifyTable plan without
* doing much else, apart from some initial state setup.
*
* The wrapping is needed to setup state in the execution phase, and give access
* to the ModifyTableState node to sub-plan states in the PlanState tree. For
* instance, the ChunkDispatchState node needs to set the arbiter index list in
* the ModifyTableState node whenever it inserts into a new chunk.
*/
static void
hypertable_insert_begin(ExtensiblePlanState *node, EState *estate, int eflags)
{
HypertableInsertState *state = (HypertableInsertState *) node;
ModifyTableState *mtstate;
PlanState *ps;
bool PG_USED_FOR_ASSERTS_ONLY found_cdstate = false;
int i;
ps = ExecInitNode(&state->mt->plan, estate, eflags);
node->extensible_ps = list_make1(ps);
mtstate = castNode(ModifyTableState, ps);
* Find all ChunkDispatchState subnodes and set their parent
* ModifyTableState node
*/
for (i = 0; i < mtstate->mt_nplans; i++)
{
ChunkDispatchState *cdstate =
(ChunkDispatchState *) get_chunk_dispatch_state(mtstate, mtstate->mt_plans[i]);
if (cdstate)
{
found_cdstate = true;
ts_chunk_dispatch_state_set_parent(cdstate, mtstate);
}
}
Assert(found_cdstate);
}
static TupleTableSlot *
hypertable_insert_exec(ExtensiblePlanState *node)
{
return ExecProcNode((PlanState *)linitial(node->extensible_ps));
}
static void
hypertable_insert_end(ExtensiblePlanState *node)
{
ExecEndNode((PlanState *)linitial(node->extensible_ps));
}
static void
hypertable_insert_rescan(ExtensiblePlanState *node)
{
ExecReScan((PlanState *)linitial(node->extensible_ps));
}
static ExtensibleExecMethods hypertable_insert_state_methods = {
.ExtensibleName = "HypertableInsertState",
.BeginExtensiblePlan = hypertable_insert_begin,
.ExecExtensiblePlan = hypertable_insert_exec,
.EndExtensiblePlan = hypertable_insert_end,
.ReScanExtensiblePlan = hypertable_insert_rescan,
};
static Node *
hypertable_insert_state_create(ExtensiblePlan *cscan)
{
HypertableInsertState *state;
ModifyTable *mt = castNode(ModifyTable, linitial(cscan->extensible_plans));
state = (HypertableInsertState *) newNode(sizeof(HypertableInsertState), T_ExtensiblePlanState);
state->cscan_state.methods = &hypertable_insert_state_methods;
state->mt = mt;
return (Node *) state;
}
static ExtensiblePlanMethods hypertable_insert_plan_methods = {
.ExtensibleName = "HypertableInsert",
.CreateExtensiblePlanState = hypertable_insert_state_create,
};
* Make a targetlist to meet CustomScan expectations.
*
* When a CustomScan isn't scanning a real relation (scanrelid=0), it will build
* a virtual TupleDesc for the scan "input" based on custom_scan_tlist. The
* "output" targetlist is then expected to reference the attributes of the
* input's TupleDesc. Without projection, the targetlist will be only Vars with
* varno set to INDEX_VAR (to indicate reference to the TupleDesc instead of a
* real relation) and matching the order of the attributes in the TupleDesc.
*
* Any other order, or non-Vars, will lead to the CustomScan performing
* projection.
*
* Since the CustomScan for hypertable insert just wraps ModifyTable, no
* projection is needed, so we'll build a targetlist to avoid this.
*/
static List *
make_var_targetlist(const List *tlist)
{
List *new_tlist = NIL;
ListCell *lc;
int resno = 1;
foreach (lc, tlist)
{
TargetEntry *tle = lfirst_node(TargetEntry, lc);
Var *var = makeVarFromTargetEntry(INDEX_VAR, tle);
var->varattno = resno;
new_tlist = lappend(new_tlist, makeTargetEntry(&var->xpr, resno, tle->resname, false));
resno++;
}
return new_tlist;
}
* Construct the HypertableInsert's target list based on the ModifyTable's
* target list, which now exists after having been created by
* set_plan_references().
*/
void
ts_hypertable_insert_fixup_tlist(Plan *plan)
{
if (IsA(plan, ExtensiblePlan))
{
ExtensiblePlan *cscan = (ExtensiblePlan *) plan;
if (cscan->methods == &hypertable_insert_plan_methods)
{
ModifyTable *mt = linitial_node(ModifyTable, cscan->extensible_plans);
if (mt->plan.targetlist == NIL)
{
cscan->extensible_plan_tlist = NIL;
cscan->scan.plan.targetlist = NIL;
}
else
{
cscan->extensible_plan_tlist = mt->plan.targetlist;
cscan->scan.plan.targetlist = make_var_targetlist(mt->plan.targetlist);
}
}
}
}
static Plan *
hypertable_insert_plan_create(PlannerInfo *root, RelOptInfo *rel, ExtensiblePath *best_path,
List *tlist, List *clauses, List *custom_plans)
{
ExtensiblePlan *cscan = makeNode(ExtensiblePlan);
#ifdef OG30
ExtensiblePlan *mt =(ExtensiblePlan *) linitial(custom_plans);
Assert(IsA(mt, ExtensiblePlan));
cscan->methods = &hypertable_insert_plan_methods;
cscan->extensible_plans = list_make1(mt);
cscan->scan.scanrelid = 0;
cscan->scan.plan.startup_cost = mt->scan.plan.startup_cost;
cscan->scan.plan.total_cost = mt->scan.plan.total_cost;
cscan->scan.plan.plan_rows = mt->scan.plan.plan_rows;
cscan->scan.plan.plan_width = mt->scan.plan.plan_width;
#else
ModifyTable *mt =(ModifyTable *) linitial(custom_plans);
Assert(IsA(mt, ModifyTable));
cscan->methods = &hypertable_insert_plan_methods;
cscan->extensible_plans = list_make1(mt);
cscan->scan.scanrelid = 0;
cscan->scan.plan.startup_cost = mt->plan.startup_cost;
cscan->scan.plan.total_cost = mt->plan.total_cost;
cscan->scan.plan.plan_rows = mt->plan.plan_rows;
cscan->scan.plan.plan_width = mt->plan.plan_width;
#endif
* targetlist set until set_plan_references (setrefs.c) is run */
#ifndef OG30
Assert(tlist == NIL);
#endif
* to adopt the target list of the ModifyTable subplan we wrap without
* further projection. For a CustomScan this means setting the "input"
* custom_scan_tlist to the ModifyTable's target list and having an "output"
* targetlist that references the TupleDesc that is created from the
* custom_scan_tlist at execution time. Now, while this seems
* straight-forward, there are several things with how ModifyTable nodes are
* handled in the planner that complicates this:
*
* - First, ModifyTable doesn't have a targetlist set at this point, and
* it is only set later in set_plan_references (setrefs.c) if there's a
* RETURNING clause.
*
* - Second, top-level plan nodes, except for ModifyTable nodes, need to
* have a targetlist matching root->processed_tlist. This is asserted in
* apply_tlist_labeling, which is called in create_plan (createplan.c)
* immediately after this function returns. ModifyTable is exempted
* because it doesn't always have a targetlist that matches
* processed_tlist. So, even if we had access to ModifyTable's
* targetlist here we wouldn't be able to use it since we're a
* CustomScan and thus not exempted.
*
* - Third, a CustomScan's targetlist should reference the attributes of the
* TupleDesc that gets created from the custom_scan_tlist at the start of
* execution. This means we need to make the targetlist into all Vars with
* attribute numbers that correspond to the TupleDesc instead of result
* relation in the ModifyTable.
*
* To get around these issues, we set the targetlist here to
* root->processed_tlist, and at the end of planning when the ModifyTable's
* targetlist is set, we go back and fix up the CustomScan's targetlist.
*/
cscan->scan.plan.targetlist =(List*) copyObject(root->processed_tlist);
cscan->extensible_plan_tlist = cscan->scan.plan.targetlist;
* we save the original list of arbiter indexes here
* because we modify that list during execution and
* we still need the original list in case that plan
* gets reused
*/
return &cscan->scan.plan;
}
static ExtensiblePathMethods hypertable_insert_path_methods = {
.ExtensibleName = "HypertableInsertPath",
.PlanExtensiblePath = hypertable_insert_plan_create,
};
Path *
ts_hypertable_insert_path_create(PlannerInfo *root, ModifyTablePath *mtpath)
{
Path *path = &mtpath->path;
Cache *hcache = ts_hypertable_cache_pin();
ListCell *lc_path, *lc_rel;
List *subpaths = NIL;
Hypertable *ht = NULL;
HypertableInsertPath *hipath;
Assert(list_length(mtpath->subpaths) == list_length(mtpath->resultRelations));
forboth (lc_path, mtpath->subpaths, lc_rel, mtpath->resultRelations)
{
Path *subpath =(Path *) lfirst(lc_path);
Index rti = lfirst_int(lc_rel);
RangeTblEntry *rte = planner_rt_fetch(rti, root);
ht = ts_hypertable_cache_get_entry(hcache, rte->relid, CACHE_FLAG_MISSING_OK);
if (ht != NULL)
{
* We replace the plan with our custom chunk dispatch plan.
*/
subpath = ts_chunk_dispatch_path_create(mtpath, subpath, rti, rte->relid);
}
subpaths = lappend(subpaths, subpath);
}
Assert(NULL != ht);
ts_cache_release(hcache);
hipath =(HypertableInsertPath *) palloc0(sizeof(HypertableInsertPath));
memcpy(&hipath->cpath.path, path, sizeof(Path));
hipath->cpath.path.type = T_ExtensiblePath;
hipath->cpath.path.pathtype = T_ExtensiblePlan;
hipath->cpath.extensible_paths = list_make1(mtpath);
hipath->cpath.methods = &hypertable_insert_path_methods;
path = &hipath->cpath.path;
mtpath->subpaths = subpaths;
return path;
}