Hello Team,
Good Day,
I have been working on adding a CustomScanState
object in the executor state in my project. As part of CustomScanState
, I execute queries and store their results in the Tuplestorestate
object. After storing all tuples in the Tuplestorestate
, I retrieve each tuple and place it in the TupleTableSlot
using the tuplestore_gettupleslot()
function.
However, I encounter an error: "trying to store a minimal tuple into the wrong type of slot." Upon debugging, I discovered that the TupleTableSlot
only holds virtual tuples (tupleTableSlot->tts_ops
is set to TTSOpsVirtual
). In contrast, tuplestore_gettupleslot()
calls ExecStoreMinimalTuple()
, which expects TupleTableSlotOps
of type TTSOpsMinimalTuple
.
Further investigation revealed that in the ExecInitCustomScan()
function within the nodeCustom.c
source file, where ScanTupleSlot
and ResultTupleSlots
are initialized, users can choose custom slots by setting slotOps
in CustomScanState
. We initialize the ScanTupleSlot
based on user-specified slotOps
, but for ResultTupleSlot
, we proceed with TTSOpsVirtual
instead of the custom slotOps
, which is causing the issue.
Is this behavior expected? Is there a way to store tuples in slots according to the TupleTableSlot
type?
I found a function
ExecForceStoreMinimalTuple()
which can be used in my case. We need to pass the MinimalTuple to this function, but I was unable to find a way to fetch the tuple from tuple storestate. We do have
tuplestore_gettuple() function to get the minimal tuple but it is a static function, is there any other function like that?
Below is the code snippet of ExecInitCustomScan() , for simplicity I removed some code in the function. I took it from the nodeCustom.c file in the PG source.
CustomScanState *
ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags)
{
CustomScanState *css;
const TupleTableSlotOps *slotOps;
css = castNode(CustomScanState,
cscan->methods->CreateCustomScanState(cscan));
// ------------------------------- CODE STARTED ----------------
/*
* Use a custom slot if specified in CustomScanState or use virtual slot
* otherwise.
*/
slotOps = css->slotOps;
if (!slotOps)
slotOps = &TTSOpsVirtual;
if (cscan->custom_scan_tlist != NIL || scan_rel == NULL)
{
ExecInitScanTupleSlot(estate, &css->ss, scan_tupdesc, slotOps); // Here we are using slotOps provided by user
}
else
{
ExecInitScanTupleSlot(estate, &css->ss, RelationGetDescr(scan_rel),
slotOps); // Here we are using slotOps provided by user
}
ExecInitResultTupleSlotTL(&css->ss.ps, &TTSOpsVirtual); // Here we have hard coded TTSOpsVirtual
// -------------------------- CODE ENDED -----------------------
}