blob: 26583b959e180229d8a099af396e68856f3b2ce3 [file] [log] [blame]
/*
* execHeapScan.c
* Support routines for scanning Heap tables.
*
* 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.
*
*/
#include "postgres.h"
#include "executor/executor.h"
#include "nodes/execnodes.h"
#include "executor/nodeTableScan.h"
static void
InitHeapScanOpaque(ScanState *scanState)
{
SeqScanState *state = (SeqScanState *)scanState;
Assert(state->opaque == NULL);
state->opaque = palloc0(sizeof(SeqScanOpaqueData));
}
static void
FreeHeapScanOpaque(ScanState *scanState)
{
SeqScanState *state = (SeqScanState *)scanState;
Assert(state->opaque != NULL);
pfree(state->opaque);
state->opaque = NULL;
}
TupleTableSlot *
HeapScanNext(ScanState *scanState)
{
Assert(IsA(scanState, TableScanState) ||
IsA(scanState, DynamicTableScanState));
SeqScanState *node = (SeqScanState *)scanState;
Assert(node->opaque != NULL);
HeapTuple tuple;
HeapScanDesc scandesc;
Index scanrelid;
EState *estate;
ScanDirection direction;
TupleTableSlot *slot;
Assert((node->ss.scan_state & SCAN_SCAN) != 0);
/*
* get information from the estate and scan state
*/
estate = node->ss.ps.state;
scandesc = node->opaque->ss_currentScanDesc;
scanrelid = ((SeqScan *) node->ss.ps.plan)->scanrelid;
direction = estate->es_direction;
slot = node->ss.ss_ScanTupleSlot;
/*
* Check if we are evaluating PlanQual for tuple of this relation.
* Additional checking is not good, but no other way for now. We could
* introduce new nodes for this case and handle SeqScan --> NewNode
* switching in Init/ReScan plan...
*/
if (estate->es_evTuple != NULL &&
estate->es_evTuple[scanrelid - 1] != NULL)
{
if (estate->es_evTupleNull[scanrelid - 1])
{
return ExecClearTuple(slot);
}
ExecStoreGenericTuple(estate->es_evTuple[scanrelid - 1], slot, false);
/*
* Note that unlike IndexScan, SeqScan never uses keys in
* heap_beginscan (and this is very bad) - so, here we do not check
* the keys.
*/
/* Flag for the next call that no more tuples */
estate->es_evTupleNull[scanrelid - 1] = true;
return slot;
}
/*
* get the next tuple from the access methods
*/
if (node->opaque->ss_heapTupleData.bot == node->opaque->ss_heapTupleData.top &&
!node->opaque->ss_heapTupleData.seen_EOS)
{
node->opaque->ss_heapTupleData.last = NULL;
node->opaque->ss_heapTupleData.bot = 0;
node->opaque->ss_heapTupleData.top = lengthof(node->opaque->ss_heapTupleData.item);
heap_getnextx(scandesc, direction, node->opaque->ss_heapTupleData.item,
&node->opaque->ss_heapTupleData.top,
&node->opaque->ss_heapTupleData.seen_EOS);
if (scandesc->rs_pageatatime &&
IsA(scanState, TableScanState))
{
Gpmon_M_Incr(GpmonPktFromTableScanState((TableScanState *)scanState), GPMON_TABLESCAN_PAGE);
CheckSendPlanStateGpmonPkt(&node->ss.ps);
}
}
node->opaque->ss_heapTupleData.last = NULL;
if (node->opaque->ss_heapTupleData.bot < node->opaque->ss_heapTupleData.top)
{
node->opaque->ss_heapTupleData.last =
&node->opaque->ss_heapTupleData.item[node->opaque->ss_heapTupleData.bot++];
}
tuple = node->opaque->ss_heapTupleData.last;
/*
* save the tuple and the buffer returned to us by the access methods in
* our scan tuple slot and return the slot. Note: we pass 'false' because
* tuples returned by heap_getnext() are pointers onto disk pages and were
* not created with palloc() and so should not be pfree()'d. Note also
* that ExecStoreTuple will increment the refcount of the buffer; the
* refcount will not be dropped until the tuple table slot is cleared.
*/
if (tuple)
{
ExecStoreHeapTuple(tuple,
slot,
scandesc->rs_cbuf,
false);
}
else
{
ExecClearTuple(slot);
}
return slot;
}
void
BeginScanHeapRelation(ScanState *scanState)
{
Assert(IsA(scanState, TableScanState) ||
IsA(scanState, DynamicTableScanState));
SeqScanState *node = (SeqScanState *)scanState;
Assert(node->opaque == NULL);
Assert(node->ss.scan_state == SCAN_INIT || node->ss.scan_state == SCAN_DONE);
InitHeapScanOpaque(scanState);
Assert(node->opaque != NULL);
node->opaque->ss_currentScanDesc = heap_beginscan(
node->ss.ss_currentRelation,
node->ss.ps.state->es_snapshot,
0,
NULL);
node->opaque->ss_heapTupleData.bot = 0;
node->opaque->ss_heapTupleData.top = 0;
node->opaque->ss_heapTupleData.seen_EOS = 0;
node->opaque->ss_heapTupleData.last = NULL;
node->ss.scan_state = SCAN_SCAN;
}
void
EndScanHeapRelation(ScanState *scanState)
{
Assert(IsA(scanState, TableScanState) ||
IsA(scanState, DynamicTableScanState));
SeqScanState *node = (SeqScanState *)scanState;
Assert((node->ss.scan_state & SCAN_SCAN) != 0);
Assert(node->opaque != NULL &&
node->opaque->ss_currentScanDesc != NULL);
heap_endscan(node->opaque->ss_currentScanDesc);
FreeHeapScanOpaque(scanState);
node->ss.scan_state = SCAN_INIT;
}
void
ReScanHeapRelation(ScanState *scanState)
{
Assert(IsA(scanState, TableScanState) ||
IsA(scanState, DynamicTableScanState));
SeqScanState *node = (SeqScanState *)scanState;
Assert(node->opaque != NULL &&
node->opaque->ss_currentScanDesc != NULL);
heap_rescan(node->opaque->ss_currentScanDesc, NULL /* new scan keys */);
node->opaque->ss_heapTupleData.bot = 0;
node->opaque->ss_heapTupleData.top = 0;
node->opaque->ss_heapTupleData.seen_EOS = 0;
node->opaque->ss_heapTupleData.last = NULL;
}
void
MarkPosHeapRelation(ScanState *scanState)
{
Assert(IsA(scanState, TableScanState) ||
IsA(scanState, DynamicTableScanState));
SeqScanState *node = (SeqScanState *)scanState;
Assert((node->ss.scan_state & SCAN_SCAN) != 0);
Assert(node->opaque != NULL &&
node->opaque->ss_currentScanDesc != NULL);
heap_markposx(node->opaque->ss_currentScanDesc,
node->opaque->ss_heapTupleData.last);
node->ss.scan_state |= SCAN_MARKPOS;
}
void
RestrPosHeapRelation(ScanState *scanState)
{
Assert(IsA(scanState, TableScanState) ||
IsA(scanState, DynamicTableScanState));
SeqScanState *node = (SeqScanState *)scanState;
Assert((node->ss.scan_state & SCAN_SCAN) != 0);
Assert((node->ss.scan_state & SCAN_MARKPOS) != 0);
/*
* Clear any reference to the previously returned tuple. This is needed
* because the slot is simply pointing at scan->rs_cbuf, which
* heap_restrpos will change; we'd have an internally inconsistent slot if
* we didn't do this.
*/
ExecClearTuple(node->ss.ss_ScanTupleSlot);
Assert(node->opaque != NULL &&
node->opaque->ss_currentScanDesc != NULL);
heap_restrpos(node->opaque->ss_currentScanDesc);
node->ss.scan_state &= (~ ((int) SCAN_MARKPOS));
}